X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/aa3378df3cc7da00b8f93569d0c88f0c626ddb18..3d398e04930e1df6c820fa0e6520e396f8437e52:/serverloop.c diff --git a/serverloop.c b/serverloop.c index 683598ef..6a81806b 100644 --- a/serverloop.c +++ b/serverloop.c @@ -2,17 +2,56 @@ * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved - * Created: Sun Sep 10 00:30:37 1995 ylo * Server main loop for handling the interactive session. + * + * 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". + * + * SSH2 support by Markus Friedl. + * 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 "includes.h" +RCSID("$OpenBSD: serverloop.c,v 1.34 2000/10/27 07:32:18 markus Exp $"); + #include "xmalloc.h" #include "ssh.h" #include "packet.h" #include "buffer.h" #include "servconf.h" #include "pty.h" +#include "channels.h" + +#include "compat.h" +#include "ssh2.h" +#include "session.h" +#include "dispatch.h" +#include "auth-options.h" + +extern ServerOptions options; static Buffer stdin_buffer; /* Buffer for stdin data. */ static Buffer stdout_buffer; /* Buffer for stdout data. */ @@ -36,17 +75,25 @@ static int max_fd; /* Max file descriptor number for select(). */ /* * This SIGCHLD kludge is used to detect when the child exits. The server * will exit after that, as soon as forwarded connections have terminated. + * + * After SIGCHLD child_has_selected is set to 1 after the first pass + * through the wait_until_can_do_something() select(). This ensures + * that the child's output gets a chance to drain before it is yanked. */ -static int child_pid; /* Pid of the child. */ +static pid_t child_pid; /* Pid of the child. */ static volatile int child_terminated; /* The child has terminated. */ +static volatile int child_has_selected; /* Child has had chance to drain. */ static volatile int child_wait_status; /* Status from wait(). */ -void +void server_init_dispatch(void); + +void sigchld_handler(int sig) { int save_errno = errno; - int wait_pid; + pid_t wait_pid; + debug("Received SIGCHLD."); wait_pid = wait((int *) &child_wait_status); if (wait_pid != -1) { @@ -54,123 +101,36 @@ sigchld_handler(int sig) error("Strange, got SIGCHLD and wait returned pid %d but child is %d", wait_pid, child_pid); if (WIFEXITED(child_wait_status) || - WIFSIGNALED(child_wait_status)) + WIFSIGNALED(child_wait_status)) { child_terminated = 1; + child_has_selected = 0; + } } signal(SIGCHLD, sigchld_handler); errno = save_errno; } - -/* - * Process any buffered packets that have been received from the client. - */ -void -process_buffered_input_packets() +void +sigchld_handler2(int sig) { - int type; - char *data; - unsigned int data_len; - int row, col, xpixel, ypixel; - int payload_len; - - /* Process buffered packets from the client. */ - while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) { - switch (type) { - case SSH_CMSG_STDIN_DATA: - /* Stdin data from the client. Append it to the buffer. */ - /* Ignore any data if the client has closed stdin. */ - if (fdin == -1) - break; - data = packet_get_string(&data_len); - packet_integrity_check(payload_len, (4 + data_len), type); - buffer_append(&stdin_buffer, data, data_len); - memset(data, 0, data_len); - xfree(data); - break; - - case SSH_CMSG_EOF: - /* - * Eof from the client. The stdin descriptor to the - * program will be closed when all buffered data has - * drained. - */ - debug("EOF received for stdin."); - packet_integrity_check(payload_len, 0, type); - stdin_eof = 1; - break; - - case SSH_CMSG_WINDOW_SIZE: - debug("Window change received."); - packet_integrity_check(payload_len, 4 * 4, type); - row = packet_get_int(); - col = packet_get_int(); - xpixel = packet_get_int(); - ypixel = packet_get_int(); - if (fdin != -1) - pty_change_window_size(fdin, row, col, xpixel, ypixel); - break; - - case SSH_MSG_PORT_OPEN: - debug("Received port open request."); - channel_input_port_open(payload_len); - break; - - case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: - debug("Received channel open confirmation."); - packet_integrity_check(payload_len, 4 + 4, type); - channel_input_open_confirmation(); - break; - - case SSH_MSG_CHANNEL_OPEN_FAILURE: - debug("Received channel open failure."); - packet_integrity_check(payload_len, 4, type); - channel_input_open_failure(); - break; - - case SSH_MSG_CHANNEL_DATA: - channel_input_data(payload_len); - break; - - case SSH_MSG_CHANNEL_CLOSE: - debug("Received channel close."); - packet_integrity_check(payload_len, 4, type); - channel_input_close(); - break; - - case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: - debug("Received channel close confirmation."); - packet_integrity_check(payload_len, 4, type); - channel_input_close_confirmation(); - break; - - default: - /* - * In this phase, any unexpected messages cause a - * protocol error. This is to ease debugging; also, - * since no confirmations are sent messages, - * unprocessed unknown messages could cause strange - * problems. Any compatible protocol extensions must - * be negotiated before entering the interactive - * session. - */ - packet_disconnect("Protocol error during session: type %d", - type); - } - } + int save_errno = errno; + debug("Received SIGCHLD."); + child_terminated = 1; + child_has_selected = 0; + errno = save_errno; } /* * Make packets from buffered stderr data, and buffer it for sending * to the client. */ -void +void make_packets_from_stderr_data() { int len; /* Send buffered stderr data to the client. */ while (buffer_len(&stderr_buffer) > 0 && - packet_not_very_much_data_to_write()) { + packet_not_very_much_data_to_write()) { len = buffer_len(&stderr_buffer); if (packet_is_interactive()) { if (len > 512) @@ -192,14 +152,14 @@ make_packets_from_stderr_data() * Make packets from buffered stdout data, and buffer it for sending to the * client. */ -void +void make_packets_from_stdout_data() { int len; /* Send buffered stdout data to the client. */ while (buffer_len(&stdout_buffer) > 0 && - packet_not_very_much_data_to_write()) { + packet_not_very_much_data_to_write()) { len = buffer_len(&stdout_buffer); if (packet_is_interactive()) { if (len > 512) @@ -223,7 +183,7 @@ make_packets_from_stdout_data() * have data or can accept data. Optionally, a maximum time can be specified * for the duration of the wait (0 = infinite). */ -void +void wait_until_can_do_something(fd_set * readset, fd_set * writeset, unsigned int max_time_milliseconds) { @@ -235,27 +195,37 @@ retry_select: /* Initialize select() masks. */ FD_ZERO(readset); - - /* - * Read packets from the client unless we have too much buffered - * stdin or channel data. - */ - if (buffer_len(&stdin_buffer) < 4096 && - channel_not_very_much_buffered_data()) - FD_SET(connection_in, readset); - - /* - * If there is not too much data already buffered going to the - * client, try to get some more data from the program. - */ - if (packet_not_very_much_data_to_write()) { - if (!fdout_eof) - FD_SET(fdout, readset); - if (!fderr_eof) - FD_SET(fderr, readset); - } FD_ZERO(writeset); + if (compat20) { + /* wrong: bad condition XXX */ + if (channel_not_very_much_buffered_data()) + FD_SET(connection_in, readset); + } else { + /* + * Read packets from the client unless we have too much + * buffered stdin or channel data. + */ + if (buffer_len(&stdin_buffer) < buffer_high && + channel_not_very_much_buffered_data()) + FD_SET(connection_in, readset); + /* + * If there is not too much data already buffered going to + * the client, try to get some more data from the program. + */ + if (packet_not_very_much_data_to_write()) { + if (!fdout_eof) + FD_SET(fdout, readset); + if (!fderr_eof) + FD_SET(fderr, readset); + } + /* + * If we have buffered data, try to write some of that data + * to the program. + */ + if (fdin != -1 && buffer_len(&stdin_buffer) > 0) + FD_SET(fdin, writeset); + } /* Set masks for channel descriptors. */ channel_prepare_select(readset, writeset); @@ -266,11 +236,6 @@ retry_select: if (packet_have_data_to_write()) FD_SET(connection_out, writeset); - /* If we have buffered data, try to write some of that data to the - program. */ - if (fdin != -1 && buffer_len(&stdin_buffer) > 0) - FD_SET(fdin, writeset); - /* Update the maximum descriptor number if appropriate. */ if (channel_max_fd() > max_fd) max_fd = channel_max_fd(); @@ -290,6 +255,8 @@ retry_select: tv.tv_usec = 1000 * (max_time_milliseconds % 1000); tvp = &tv; } + if (tvp!=NULL) + debug("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds); /* Wait for something to happen, or the timeout to expire. */ ret = select(max_fd + 1, readset, writeset, NULL, tvp); @@ -300,13 +267,16 @@ retry_select: else goto retry_select; } + + if (child_terminated) + child_has_selected = 1; } /* * Processes input from the client and the program. Input data is stored * in buffers and processed later. */ -void +void process_input(fd_set * readset) { int len; @@ -318,27 +288,27 @@ process_input(fd_set * readset) if (len == 0) { verbose("Connection closed by remote host."); fatal_cleanup(); + } else if (len < 0) { + if (errno != EINTR && errno != EAGAIN) { + verbose("Read error from remote host: %.100s", strerror(errno)); + fatal_cleanup(); + } + } else { + /* Buffer any received data. */ + packet_process_incoming(buf, len); } - /* - * There is a kernel bug on Solaris that causes select to - * sometimes wake up even though there is no data available. - */ - if (len < 0 && errno == EAGAIN) - len = 0; - - if (len < 0) { - verbose("Read error from remote host: %.100s", strerror(errno)); - fatal_cleanup(); - } - /* Buffer any received data. */ - packet_process_incoming(buf, len); } + if (compat20) + return; + /* Read and buffer any available stdout data from the program. */ if (!fdout_eof && FD_ISSET(fdout, readset)) { len = read(fdout, buf, sizeof(buf)); - if (len <= 0) + if (len < 0 && (errno == EINTR || errno == EAGAIN)) { + /* do nothing */ + } else if (len <= 0) { fdout_eof = 1; - else { + } else { buffer_append(&stdout_buffer, buf, len); fdout_bytes += len; } @@ -346,30 +316,35 @@ process_input(fd_set * readset) /* Read and buffer any available stderr data from the program. */ if (!fderr_eof && FD_ISSET(fderr, readset)) { len = read(fderr, buf, sizeof(buf)); - if (len <= 0) + if (len < 0 && (errno == EINTR || errno == EAGAIN)) { + /* do nothing */ + } else if (len <= 0) { fderr_eof = 1; - else + } else { buffer_append(&stderr_buffer, buf, len); + } } } /* * Sends data from internal buffers to client program stdin. */ -void +void process_output(fd_set * writeset) { int len; /* Write buffered data to program stdin. */ - if (fdin != -1 && FD_ISSET(fdin, writeset)) { + if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) { len = write(fdin, buffer_ptr(&stdin_buffer), - buffer_len(&stdin_buffer)); - if (len <= 0) { + buffer_len(&stdin_buffer)); + if (len < 0 && (errno == EINTR || errno == EAGAIN)) { + /* do nothing */ + } else if (len <= 0) { #ifdef USE_PIPES close(fdin); #else - if (fdout == -1) + if (fdin != fdout) close(fdin); else shutdown(fdin, SHUT_WR); /* We will no longer send. */ @@ -391,7 +366,7 @@ process_output(fd_set * writeset) * Wait until all buffered output has been sent to the client. * This is used when the program terminates. */ -void +void drain_output() { /* Send any buffered stdout data to the client. */ @@ -416,6 +391,12 @@ drain_output() packet_write_wait(); } +void +process_buffered_input_packets() +{ + dispatch_run(DISPATCH_NONBLOCK, NULL, NULL); +} + /* * Performs the interactive session. This handles data transmission between * the client and the program. Note that the notion of stdin, stdout, and @@ -423,10 +404,12 @@ drain_output() * stdin (of the child program), and reads from stdout and stderr (of the * child program). */ -void -server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) +void +server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) { - int wait_status, wait_pid; /* Status and pid returned by wait(). */ + fd_set readset, writeset; + int wait_status; /* Status returned by wait(). */ + pid_t wait_pid; /* pid returned by wait(). */ int waiting_termination = 0; /* Have displayed waiting close message. */ unsigned int max_time_milliseconds; unsigned int previous_stdout_buffer_bytes; @@ -438,12 +421,22 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) /* Initialize the SIGCHLD kludge. */ child_pid = pid; child_terminated = 0; + child_has_selected = 0; signal(SIGCHLD, sigchld_handler); + signal(SIGPIPE, SIG_IGN); /* Initialize our global variables. */ fdin = fdin_arg; fdout = fdout_arg; fderr = fderr_arg; + + /* nonblocking IO */ + set_nonblock(fdin); + set_nonblock(fdout); + /* we don't have stderr for interactive terminal sessions, see below */ + if (fderr != -1) + set_nonblock(fderr); + connection_in = packet_get_connection_in(); connection_out = packet_get_connection_out(); @@ -480,9 +473,10 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) if (fderr == -1) fderr_eof = 1; + server_init_dispatch(); + /* Main loop of the server for the interactive session mode. */ for (;;) { - fd_set readset, writeset; /* Process buffered packets from the client. */ process_buffered_input_packets(); @@ -495,7 +489,7 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) #ifdef USE_PIPES close(fdin); #else - if (fdout == -1) + if (fdin != fdout) close(fdin); else shutdown(fdin, SHUT_WR); /* We will no longer send. */ @@ -533,10 +527,13 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) * descriptors, and we have no more data to send to the * client, and there is no pending buffered data. */ - if (fdout_eof && fderr_eof && !packet_have_data_to_write() && - buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) { + if (((fdout_eof && fderr_eof) || + (child_terminated && child_has_selected)) && + !packet_have_data_to_write() && + (buffer_len(&stdout_buffer) == 0) && + (buffer_len(&stderr_buffer) == 0)) { if (!channel_still_open()) - goto quit; + break; if (!waiting_termination) { const char *s = "Waiting for forwarded connections to terminate...\r\n"; char *cp; @@ -563,7 +560,6 @@ server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) process_output(&writeset); } -quit: /* Cleanup and termination code. */ /* Wait until all output has been sent to the client. */ @@ -609,7 +605,7 @@ quit: /* Check if it matches the process we forked. */ if (wait_pid != pid) error("Strange, wait returned pid %d, expected %d", - wait_pid, pid); + wait_pid, pid); } /* We no longer want our SIGCHLD handler to be called. */ @@ -649,3 +645,247 @@ quit: packet_disconnect("wait returned status %04x.", wait_status); /* NOTREACHED */ } + +void +server_loop2(void) +{ + fd_set readset, writeset; + int had_channel = 0; + int status; + pid_t pid; + + debug("Entering interactive session for SSH2."); + + signal(SIGCHLD, sigchld_handler2); + signal(SIGPIPE, SIG_IGN); + child_terminated = 0; + connection_in = packet_get_connection_in(); + connection_out = packet_get_connection_out(); + max_fd = connection_in; + if (connection_out > max_fd) + max_fd = connection_out; + server_init_dispatch(); + + for (;;) { + process_buffered_input_packets(); + if (!had_channel && channel_still_open()) + had_channel = 1; + if (had_channel && !channel_still_open()) { + debug("!channel_still_open."); + break; + } + if (packet_not_very_much_data_to_write()) + channel_output_poll(); + wait_until_can_do_something(&readset, &writeset, 0); + if (child_terminated && child_has_selected) { + /* XXX: race - assumes only one child has terminated */ + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + session_close_by_pid(pid, status); + child_terminated = 0; + child_has_selected = 0; + signal(SIGCHLD, sigchld_handler2); + } + channel_after_select(&readset, &writeset); + process_input(&readset); + process_output(&writeset); + } + signal(SIGCHLD, SIG_DFL); + while ((pid = waitpid(-1, &status, WNOHANG)) > 0) + session_close_by_pid(pid, status); + channel_stop_listening(); +} + +void +server_input_stdin_data(int type, int plen, void *ctxt) +{ + char *data; + unsigned int data_len; + + /* Stdin data from the client. Append it to the buffer. */ + /* Ignore any data if the client has closed stdin. */ + if (fdin == -1) + return; + data = packet_get_string(&data_len); + packet_integrity_check(plen, (4 + data_len), type); + buffer_append(&stdin_buffer, data, data_len); + memset(data, 0, data_len); + xfree(data); +} + +void +server_input_eof(int type, int plen, void *ctxt) +{ + /* + * Eof from the client. The stdin descriptor to the + * program will be closed when all buffered data has + * drained. + */ + debug("EOF received for stdin."); + packet_integrity_check(plen, 0, type); + stdin_eof = 1; +} + +void +server_input_window_size(int type, int plen, void *ctxt) +{ + int row = packet_get_int(); + int col = packet_get_int(); + int xpixel = packet_get_int(); + int ypixel = packet_get_int(); + + debug("Window change received."); + packet_integrity_check(plen, 4 * 4, type); + if (fdin != -1) + pty_change_window_size(fdin, row, col, xpixel, ypixel); +} + +int +input_direct_tcpip(void) +{ + int sock; + char *target, *originator; + int target_port, originator_port; + + target = packet_get_string(NULL); + target_port = packet_get_int(); + originator = packet_get_string(NULL); + originator_port = packet_get_int(); + packet_done(); + + debug("open direct-tcpip: from %s port %d to %s port %d", + originator, originator_port, target, target_port); + + /* XXX check permission */ + if (no_port_forwarding_flag || !options.allow_tcp_forwarding) { + xfree(target); + xfree(originator); + return -1; + } + sock = channel_connect_to(target, target_port); + xfree(target); + xfree(originator); + if (sock < 0) + return -1; + return channel_new("direct-tcpip", SSH_CHANNEL_OPEN, + sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, + CHAN_TCP_PACKET_DEFAULT, 0, xstrdup("direct-tcpip"), 1); +} + +void +server_input_channel_open(int type, int plen, void *ctxt) +{ + Channel *c = NULL; + char *ctype; + int id; + unsigned int len; + int rchan; + int rmaxpack; + int rwindow; + + ctype = packet_get_string(&len); + rchan = packet_get_int(); + rwindow = packet_get_int(); + rmaxpack = packet_get_int(); + + debug("server_input_channel_open: ctype %s rchan %d win %d max %d", + ctype, rchan, rwindow, rmaxpack); + + if (strcmp(ctype, "session") == 0) { + debug("open session"); + packet_done(); + /* + * A server session has no fd to read or write + * until a CHANNEL_REQUEST for a shell is made, + * so we set the type to SSH_CHANNEL_LARVAL. + * Additionally, a callback for handling all + * CHANNEL_REQUEST messages is registered. + */ + id = channel_new(ctype, SSH_CHANNEL_LARVAL, + -1, -1, -1, 0, CHAN_SES_PACKET_DEFAULT, + 0, xstrdup("server-session"), 1); + if (session_open(id) == 1) { + channel_register_callback(id, SSH2_MSG_CHANNEL_REQUEST, + session_input_channel_req, (void *)0); + channel_register_cleanup(id, session_close_by_channel); + c = channel_lookup(id); + } else { + debug("session open failed, free channel %d", id); + channel_free(id); + } + } else if (strcmp(ctype, "direct-tcpip") == 0) { + id = input_direct_tcpip(); + if (id >= 0) + c = channel_lookup(id); + } + if (c != NULL) { + debug("confirm %s", ctype); + c->remote_id = rchan; + c->remote_window = rwindow; + c->remote_maxpacket = rmaxpack; + + packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); + packet_put_int(c->remote_id); + packet_put_int(c->self); + packet_put_int(c->local_window); + packet_put_int(c->local_maxpacket); + packet_send(); + } else { + debug("failure %s", ctype); + packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); + packet_put_int(rchan); + packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); + packet_put_cstring("bla bla"); + packet_put_cstring(""); + packet_send(); + } + xfree(ctype); +} + +void +server_init_dispatch_20() +{ + debug("server_init_dispatch_20"); + dispatch_init(&dispatch_protocol_error); + dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); + dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); + dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); + dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &channel_input_channel_request); + dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); +} +void +server_init_dispatch_13() +{ + debug("server_init_dispatch_13"); + dispatch_init(NULL); + dispatch_set(SSH_CMSG_EOF, &server_input_eof); + dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data); + dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); + dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); + dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); +} +void +server_init_dispatch_15() +{ + server_init_dispatch_13(); + debug("server_init_dispatch_15"); + dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); + dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose); +} +void +server_init_dispatch() +{ + if (compat20) + server_init_dispatch_20(); + else if (compat13) + server_init_dispatch_13(); + else + server_init_dispatch_15(); +}