Add support for dynamically adjusted buffers to allow the full use of
the bandwidth of long fat pipes (i.e. 100Mbps+ trans-oceanic or trans-continental links). Bandwidth-delay products up to 64MB are supported. Also add support (not compiled by default) for the None cypher. The None cypher can only be enabled on non-interactive sessions (those without a pty where -T was not used) and must be enabled in both the client and server configuration files and on the client command line. Additionally, the None cypher will only be activated after authentication is complete. To enable the None cypher you must add -DNONE_CIPHER_ENABLED to CFLAGS via the make command line or in /etc/make.conf. This code is a style(9) compliant version of these features extracted from the patches published at: http://www.psc.edu/networking/projects/hpn-ssh/ Merging this patch has been a collaboration between me and Bjoern. Reviewed by: bz Approved by: re (kib), des (maintainer)
This commit is contained in:
parent
06521fbb49
commit
8998619212
120
crypto/openssh/README.hpn
Normal file
120
crypto/openssh/README.hpn
Normal file
@ -0,0 +1,120 @@
|
||||
Notes:
|
||||
|
||||
NONE CIPHER:
|
||||
To use the NONE option you must have the NoneEnabled switch set on the server
|
||||
and you MUST have *both* NoneEnabled and NoneSwitch set to yes on the client.
|
||||
The NONE feature works with ALL ssh subsystems (as far as we can tell)
|
||||
as long as there is no tty allocated.
|
||||
If a user uses the -T switch to prevent a tty being created the NONE cipher
|
||||
will be disabled.
|
||||
|
||||
|
||||
PERFORMANCE:
|
||||
The performance increase will only be as good as the network and TCP stack
|
||||
tuning on the reciever side of the connection allows. As a rule of thumb a
|
||||
user will need at least 10Mb/s connection with a 100ms RTT to see a doubling
|
||||
of performance.
|
||||
The HPN-SSH home page http://www.psc.edu/networking/projects/hpn-ssh
|
||||
describes this in greater detail.
|
||||
|
||||
|
||||
BUFFER SIZES:
|
||||
- if HPN is disabled the receive buffer size will be set to the OpenSSH default
|
||||
of 64K.
|
||||
|
||||
- if a HPN system connects to a non-HPN system the receive buffer will
|
||||
be set to the HPNBufferSize value. The default is 2MB but user adjustable.
|
||||
|
||||
- If a HPN to HPN connection is established a number of different things might
|
||||
happen based on the user options and conditions.
|
||||
|
||||
Conditions: HPNBufferSize NOT Set, TCPRcvBufPoll enabled, TCPRcvBuf NOT Set
|
||||
Result: HPN Buffer Size = up to 64MB
|
||||
This is the default state. The HPN buffer size will grow to a maximum of
|
||||
64MB as the TCP receive buffer grows. The maximum HPN Buffer size of 64MB
|
||||
is geared towards 10GigE transcontinental connections.
|
||||
|
||||
Conditions: HPNBufferSize NOT Set, TCPRcvBufPoll disabled, TCPRcvBuf NOT Set
|
||||
Result: HPN Buffer Size = TCP receive buffer value.
|
||||
Users on non-autotuning systesm should disable TCPRcvBufPoll in the
|
||||
ssh_cofig and sshd_config
|
||||
|
||||
Conditions: HPNBufferSize SET, TCPRcvBufPoll disabled, TCPRcvBuf NOT Set
|
||||
Result: HPN Buffer Size = minmum of TCP receive buffer and HPNBufferSize.
|
||||
This would be the system defined TCP receive buffer (RWIN).
|
||||
|
||||
Conditions: HPNBufferSize SET, TCPRcvBufPoll disabled, TCPRcvBuf SET
|
||||
Result: HPN Buffer Size = minmum of TCPRcvBuf and HPNBufferSize.
|
||||
Generally there is no need to set both.
|
||||
|
||||
Conditions: HPNBufferSize SET, TCPRcvBufPoll enabled, TCPRcvBuf NOT Set
|
||||
Result: HPN Buffer Size = grows to HPNBufferSize
|
||||
The buffer will grow up to the maximum size specified here.
|
||||
|
||||
Conditions: HPNBufferSize SET, TCPRcvBufPoll enabled, TCPRcvBuf SET
|
||||
Result: HPN Buffer Size = minmum of TCPRcvBuf and HPNBufferSize.
|
||||
Generally there is no need to set both of these, especially on autotuning
|
||||
systems. However, if the users wishes to override the autotuning this would
|
||||
be one way to do it.
|
||||
|
||||
Conditions: HPNBufferSize NOT Set, TCPRcvBufPoll enabled, TCPRcvBuf SET
|
||||
Result: HPN Buffer Size = TCPRcvBuf.
|
||||
This will override autotuning and set the TCP recieve buffer to the user
|
||||
defined value.
|
||||
|
||||
|
||||
HPN SPECIFIC CONFIGURATION OPTIONS:
|
||||
|
||||
- HPNDisabled=[yes/no] client/server
|
||||
In some situations, such as transfers on a local area network, the impact
|
||||
of the HPN code produces a net decrease in performance. In these cases it is
|
||||
helpful to disable the HPN functionality. By default HPNDisabled is set to no.
|
||||
|
||||
- HPNBufferSize=[int]KB client/server
|
||||
This is the default buffer size the HPN functionality uses when interacting
|
||||
with non-HPN SSH installations. Conceptually this is similar to the TcpRcvBuf
|
||||
option as applied to the internal SSH flow control. This value can range from
|
||||
1KB to 64MB (1-65536). Use of oversized or undersized buffers can cause
|
||||
performance problems depending on the roud trip time of the network path.
|
||||
The default size of this buffer is 2MB.
|
||||
|
||||
- TcpRcvBufPoll=[yes/no] client/server
|
||||
Enable or disable the polling of the TCP receive buffer through the life
|
||||
of the connection. You would want to make sure that this option is enabled
|
||||
for systems making use of autotuning kernels (linux 2.4.24+, 2.6, MS Vista,
|
||||
FreeBSD 7.x and later). Default is yes.
|
||||
|
||||
- TcpRcvBuf=[int]KB client
|
||||
Set the TCP socket receive buffer to n Kilobytes. It can be set up to the
|
||||
maximum socket size allowed by the system. This is useful in situations where
|
||||
the TCP receive window is set low but the maximum buffer size is set higher
|
||||
(as is typical). This works on a per TCP connection basis. You can also use
|
||||
this to artifically limit the transfer rate of the connection. In these cases
|
||||
the throughput will be no more than n/RTT. The minimum buffer size is 1KB.
|
||||
Default is the current system wide TCP receive buffer size.
|
||||
|
||||
- NoneEnabled=[yes/no] client/server
|
||||
Enable or disable the use of the None cipher. Care must always be used when
|
||||
enabling this as it will allow users to send data in the clear. However, it
|
||||
is important to note that authentication information remains encrypted even
|
||||
if this option is enabled. Set to no by default.
|
||||
|
||||
- NoneSwitch=[yes/no] client
|
||||
Switch the encryption cipher being used to the None cipher after
|
||||
authentication takes place. NoneEnabled must be enabled on both the client
|
||||
and server side of the connection. When the connection switches to the NONE
|
||||
cipher a warning is sent to STDERR. The connection attempt will fail with an
|
||||
error if a client requests a NoneSwitch from the server that does not
|
||||
explicitly have NoneEnabled set to yes.
|
||||
Note: The NONE cipher cannot be used in interactive (shell) sessions and it
|
||||
will fail silently. Set to no by default.
|
||||
|
||||
|
||||
CREDITS:
|
||||
|
||||
This patch was conceived, designed, and led by Chris Rapier (rapier@psc.edu)
|
||||
The majority of the actual coding for versions up to HPN12v1 was performed
|
||||
by Michael Stevens (mstevens@andrew.cmu.edu).
|
||||
The MT-AES-CTR cipher was implemented by Ben Bennet (ben@psc.edu).
|
||||
This work was financed, in part, by Cisco System, Inc., the National Library
|
||||
of Medicine, and the National Science Foundation.
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: buffer.c,v 1.32 2010/02/09 03:56:28 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -25,7 +26,7 @@
|
||||
#include "log.h"
|
||||
|
||||
#define BUFFER_MAX_CHUNK 0x100000
|
||||
#define BUFFER_MAX_LEN 0xa00000
|
||||
#define BUFFER_MAX_LEN 0x4000000 /* 64MB */
|
||||
#define BUFFER_ALLOCSZ 0x008000
|
||||
|
||||
/* Initializes the buffer structure. */
|
||||
@ -165,6 +166,13 @@ buffer_len(const Buffer *buffer)
|
||||
return buffer->end - buffer->offset;
|
||||
}
|
||||
|
||||
/* Returns the maximum number of bytes of data that may be in the buffer. */
|
||||
u_int
|
||||
buffer_get_max_len(void)
|
||||
{
|
||||
return (BUFFER_MAX_LEN);
|
||||
}
|
||||
|
||||
/* Gets data from the beginning of the buffer. */
|
||||
|
||||
int
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: buffer.h,v 1.21 2010/08/31 11:54:45 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
@ -46,6 +47,8 @@ int buffer_get_ret(Buffer *, void *, u_int);
|
||||
int buffer_consume_ret(Buffer *, u_int);
|
||||
int buffer_consume_end_ret(Buffer *, u_int);
|
||||
|
||||
u_int buffer_get_max_len(void);
|
||||
|
||||
#include <openssl/bn.h>
|
||||
|
||||
void buffer_put_bignum(Buffer *, const BIGNUM *);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: channels.c,v 1.310 2010/11/24 01:24:14 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -170,6 +171,11 @@ static void port_open_helper(Channel *c, char *rtype);
|
||||
static int connect_next(struct channel_connect *);
|
||||
static void channel_connect_ctx_free(struct channel_connect *);
|
||||
|
||||
/* -- HPN */
|
||||
|
||||
static int hpn_disabled = 0;
|
||||
static u_int buffer_size = CHAN_HPN_MIN_WINDOW_DEFAULT;
|
||||
|
||||
/* -- channel core */
|
||||
|
||||
Channel *
|
||||
@ -309,6 +315,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd,
|
||||
c->self = found;
|
||||
c->type = type;
|
||||
c->ctype = ctype;
|
||||
c->dynamic_window = 0;
|
||||
c->local_window = window;
|
||||
c->local_window_max = window;
|
||||
c->local_consumed = 0;
|
||||
@ -808,10 +815,45 @@ channel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset)
|
||||
FD_SET(c->sock, writeset);
|
||||
}
|
||||
|
||||
static u_int
|
||||
channel_tcpwinsz(void)
|
||||
{
|
||||
u_int32_t tcpwinsz;
|
||||
socklen_t optsz;
|
||||
int ret, sd;
|
||||
u_int maxlen;
|
||||
|
||||
/* If we are not on a socket return 128KB. */
|
||||
if (!packet_connection_is_on_socket())
|
||||
return (128 * 1024);
|
||||
|
||||
tcpwinsz = 0;
|
||||
optsz = sizeof(tcpwinsz);
|
||||
sd = packet_get_connection_in();
|
||||
ret = getsockopt(sd, SOL_SOCKET, SO_RCVBUF, &tcpwinsz, &optsz);
|
||||
|
||||
/* Return no more than the maximum buffer size. */
|
||||
maxlen = buffer_get_max_len();
|
||||
if ((ret == 0) && tcpwinsz > maxlen)
|
||||
tcpwinsz = maxlen;
|
||||
/* In case getsockopt() failed return a minimum. */
|
||||
if (tcpwinsz == 0)
|
||||
tcpwinsz = CHAN_TCP_WINDOW_DEFAULT;
|
||||
debug2("tcpwinsz: %d for connection: %d", tcpwinsz, sd);
|
||||
return (tcpwinsz);
|
||||
}
|
||||
|
||||
static void
|
||||
channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset)
|
||||
{
|
||||
u_int limit = compat20 ? c->remote_window : packet_get_maxsize();
|
||||
u_int limit;
|
||||
|
||||
/* Check buffer limits. */
|
||||
if (!c->tcpwinsz || c->dynamic_window > 0)
|
||||
c->tcpwinsz = channel_tcpwinsz();
|
||||
|
||||
limit = MIN(compat20 ? c->remote_window : packet_get_maxsize(),
|
||||
2 * c->tcpwinsz);
|
||||
|
||||
if (c->istate == CHAN_INPUT_OPEN &&
|
||||
limit > 0 &&
|
||||
@ -1789,14 +1831,25 @@ channel_check_window(Channel *c)
|
||||
c->local_maxpacket*3) ||
|
||||
c->local_window < c->local_window_max/2) &&
|
||||
c->local_consumed > 0) {
|
||||
u_int addition = 0;
|
||||
|
||||
/* Adjust max window size if we are in a dynamic environment. */
|
||||
if (c->dynamic_window && c->tcpwinsz > c->local_window_max) {
|
||||
/*
|
||||
* Grow the window somewhat aggressively to maintain
|
||||
* pressure.
|
||||
*/
|
||||
addition = 1.5 * (c->tcpwinsz - c->local_window_max);
|
||||
c->local_window_max += addition;
|
||||
}
|
||||
packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
|
||||
packet_put_int(c->remote_id);
|
||||
packet_put_int(c->local_consumed);
|
||||
packet_put_int(c->local_consumed + addition);
|
||||
packet_send();
|
||||
debug2("channel %d: window %d sent adjust %d",
|
||||
c->self, c->local_window,
|
||||
c->local_consumed);
|
||||
c->local_window += c->local_consumed;
|
||||
c->local_window += c->local_consumed + addition;
|
||||
c->local_consumed = 0;
|
||||
}
|
||||
return 1;
|
||||
@ -2634,6 +2687,15 @@ channel_set_af(int af)
|
||||
IPv4or6 = af;
|
||||
}
|
||||
|
||||
void
|
||||
channel_set_hpn(int disabled, u_int buf_size)
|
||||
{
|
||||
hpn_disabled = disabled;
|
||||
buffer_size = buf_size;
|
||||
debug("HPN Disabled: %d, HPN Buffer Size: %d",
|
||||
hpn_disabled, buffer_size);
|
||||
}
|
||||
|
||||
static int
|
||||
channel_setup_fwd_listener(int type, const char *listen_addr,
|
||||
u_short listen_port, int *allocated_listen_port,
|
||||
@ -2786,10 +2848,18 @@ channel_setup_fwd_listener(int type, const char *listen_addr,
|
||||
*allocated_listen_port);
|
||||
}
|
||||
|
||||
/* Allocate a channel number for the socket. */
|
||||
/*
|
||||
* Allocate a channel number for the socket. Explicitly test
|
||||
* for hpn disabled option. If true use smaller window size.
|
||||
*/
|
||||
if (hpn_disabled)
|
||||
c = channel_new("port listener", type, sock, sock, -1,
|
||||
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
|
||||
0, "port listener", 1);
|
||||
else
|
||||
c = channel_new("port listener", type, sock, sock, -1,
|
||||
buffer_size, CHAN_TCP_PACKET_DEFAULT,
|
||||
0, "port listener", 1);
|
||||
c->path = xstrdup(host);
|
||||
c->host_port = port_to_connect;
|
||||
c->listening_port = listen_port;
|
||||
@ -3334,10 +3404,16 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
|
||||
*chanids = xcalloc(num_socks + 1, sizeof(**chanids));
|
||||
for (n = 0; n < num_socks; n++) {
|
||||
sock = socks[n];
|
||||
if (hpn_disabled)
|
||||
nc = channel_new("x11 listener",
|
||||
SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
|
||||
CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
|
||||
0, "X11 inet listener", 1);
|
||||
else
|
||||
nc = channel_new("x11 listener",
|
||||
SSH_CHANNEL_X11_LISTENER, sock, sock, -1,
|
||||
buffer_size, CHAN_X11_PACKET_DEFAULT,
|
||||
0, "X11 inet listener", 1);
|
||||
nc->single_connection = single_connection;
|
||||
(*chanids)[n] = nc->self;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: channels.h,v 1.104 2010/05/14 23:29:23 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
@ -125,6 +126,8 @@ struct Channel {
|
||||
u_int local_window_max;
|
||||
u_int local_consumed;
|
||||
u_int local_maxpacket;
|
||||
u_int tcpwinsz;
|
||||
int dynamic_window;
|
||||
int extended_usage;
|
||||
int single_connection;
|
||||
|
||||
@ -162,11 +165,15 @@ struct Channel {
|
||||
/* default window/packet sizes for tcp/x11-fwd-channel */
|
||||
#define CHAN_SES_PACKET_DEFAULT (32*1024)
|
||||
#define CHAN_SES_WINDOW_DEFAULT (64*CHAN_SES_PACKET_DEFAULT)
|
||||
|
||||
#define CHAN_TCP_PACKET_DEFAULT (32*1024)
|
||||
#define CHAN_TCP_WINDOW_DEFAULT (64*CHAN_TCP_PACKET_DEFAULT)
|
||||
|
||||
#define CHAN_X11_PACKET_DEFAULT (16*1024)
|
||||
#define CHAN_X11_WINDOW_DEFAULT (4*CHAN_X11_PACKET_DEFAULT)
|
||||
|
||||
#define CHAN_HPN_MIN_WINDOW_DEFAULT (2*1024*1024)
|
||||
|
||||
/* possible input states */
|
||||
#define CHAN_INPUT_OPEN 0
|
||||
#define CHAN_INPUT_WAIT_DRAIN 1
|
||||
@ -294,4 +301,7 @@ void chan_rcvd_ieof(Channel *);
|
||||
void chan_write_failed(Channel *);
|
||||
void chan_obuf_empty(Channel *);
|
||||
|
||||
/* hpn handler */
|
||||
void channel_set_hpn(int, u_int);
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: cipher.c,v 1.82 2009/01/26 09:58:15 markus Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -163,7 +164,12 @@ ciphers_valid(const char *names)
|
||||
for ((p = strsep(&cp, CIPHER_SEP)); p && *p != '\0';
|
||||
(p = strsep(&cp, CIPHER_SEP))) {
|
||||
c = cipher_by_name(p);
|
||||
if (c == NULL || c->number != SSH_CIPHER_SSH2) {
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
if (c == NULL || (c->number != SSH_CIPHER_SSH2 &&
|
||||
c->number != SSH_CIPHER_NONE)) {
|
||||
#else
|
||||
if (c == NULL || (c->number != SSH_CIPHER_SSH2)) {
|
||||
#endif
|
||||
debug("bad cipher %s [%s]", p, names);
|
||||
xfree(cipher_list);
|
||||
return 0;
|
||||
@ -337,6 +343,9 @@ cipher_get_keyiv(CipherContext *cc, u_char *iv, u_int len)
|
||||
int evplen;
|
||||
|
||||
switch (c->number) {
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
case SSH_CIPHER_NONE:
|
||||
#endif
|
||||
case SSH_CIPHER_SSH2:
|
||||
case SSH_CIPHER_DES:
|
||||
case SSH_CIPHER_BLOWFISH:
|
||||
@ -371,6 +380,9 @@ cipher_set_keyiv(CipherContext *cc, u_char *iv)
|
||||
int evplen = 0;
|
||||
|
||||
switch (c->number) {
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
case SSH_CIPHER_NONE:
|
||||
#endif
|
||||
case SSH_CIPHER_SSH2:
|
||||
case SSH_CIPHER_DES:
|
||||
case SSH_CIPHER_BLOWFISH:
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: clientloop.c,v 1.231 2011/01/16 12:05:59 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -1768,9 +1769,14 @@ client_request_x11(const char *request_type, int rchan)
|
||||
sock = x11_connect_display();
|
||||
if (sock < 0)
|
||||
return NULL;
|
||||
c = channel_new("x11",
|
||||
SSH_CHANNEL_X11_OPEN, sock, sock, -1,
|
||||
CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1);
|
||||
if (options.hpn_disabled)
|
||||
c = channel_new("x11", SSH_CHANNEL_X11_OPEN, sock, sock, -1,
|
||||
CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
|
||||
0, "x11", 1);
|
||||
else
|
||||
c = channel_new("x11", SSH_CHANNEL_X11_OPEN, sock, sock, -1,
|
||||
options.hpn_buffer_size, CHAN_X11_PACKET_DEFAULT,
|
||||
0, "x11", 1);
|
||||
c->force_drain = 1;
|
||||
return c;
|
||||
}
|
||||
@ -1790,9 +1796,15 @@ client_request_agent(const char *request_type, int rchan)
|
||||
sock = ssh_get_authentication_socket();
|
||||
if (sock < 0)
|
||||
return NULL;
|
||||
if (options.hpn_disabled)
|
||||
c = channel_new("authentication agent connection",
|
||||
SSH_CHANNEL_OPEN, sock, sock, -1,
|
||||
CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0,
|
||||
CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0,
|
||||
"authentication agent connection", 1);
|
||||
else
|
||||
c = channel_new("authentication agent connection",
|
||||
SSH_CHANNEL_OPEN, sock, sock, -1,
|
||||
options.hpn_buffer_size, options.hpn_buffer_size, 0,
|
||||
"authentication agent connection", 1);
|
||||
c->force_drain = 1;
|
||||
return c;
|
||||
@ -1820,8 +1832,14 @@ client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (options.hpn_disabled)
|
||||
c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1,
|
||||
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
|
||||
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
|
||||
0, "tun", 1);
|
||||
else
|
||||
c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1,
|
||||
options.hpn_buffer_size, CHAN_TCP_PACKET_DEFAULT,
|
||||
0, "tun", 1);
|
||||
c->datagram = 1;
|
||||
|
||||
#if defined(SSH_TUN_FILTER)
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: compat.c,v 1.78 2008/09/11 14:22:37 markus Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved.
|
||||
*
|
||||
@ -170,6 +171,16 @@ compat_datafellows(const char *version)
|
||||
strlen(check[i].pat), 0) == 1) {
|
||||
debug("match: %s pat %s", version, check[i].pat);
|
||||
datafellows = check[i].bugs;
|
||||
/*
|
||||
* Check to see if the remote side is OpenSSH and not
|
||||
* HPN. It is utterly strange to check it from the
|
||||
* version string and expose the option that way.
|
||||
*/
|
||||
if (strstr(version,"OpenSSH") != NULL &&
|
||||
strstr(version,"hpn") == NULL) {
|
||||
datafellows |= SSH_BUG_LARGEWINDOW;
|
||||
debug("Remote is not HPN-aware");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: compat.h,v 1.42 2008/09/11 14:22:37 markus Exp $ */
|
||||
/* $FReeBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved.
|
||||
@ -58,6 +59,7 @@
|
||||
#define SSH_OLD_FORWARD_ADDR 0x01000000
|
||||
#define SSH_BUG_RFWD_ADDR 0x02000000
|
||||
#define SSH_NEW_OPENSSH 0x04000000
|
||||
#define SSH_BUG_LARGEWINDOW 0x08000000
|
||||
|
||||
void enable_compat13(void);
|
||||
void enable_compat20(void);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: kex.c,v 1.86 2010/09/22 05:01:29 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
|
||||
*
|
||||
@ -90,8 +91,13 @@ kex_names_valid(const char *names)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* put algorithm proposal into buffer */
|
||||
/* Put algorithm proposal into buffer. */
|
||||
#ifndef NONE_CIPHER_ENABLED
|
||||
static void
|
||||
#else
|
||||
/* Also used in sshconnect2.c. */
|
||||
void
|
||||
#endif
|
||||
kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
|
||||
{
|
||||
u_int i;
|
||||
@ -407,6 +413,9 @@ kex_choose_conf(Kex *kex)
|
||||
int nenc, nmac, ncomp;
|
||||
u_int mode, ctos, need;
|
||||
int first_kex_follows, type;
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
int auth_flag;
|
||||
#endif
|
||||
|
||||
my = kex_buf2prop(&kex->my, NULL);
|
||||
peer = kex_buf2prop(&kex->peer, &first_kex_follows);
|
||||
@ -430,6 +439,10 @@ kex_choose_conf(Kex *kex)
|
||||
}
|
||||
|
||||
/* Algorithm Negotiation */
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
auth_flag = packet_get_authentication_state();
|
||||
debug ("AUTH STATE is %d", auth_flag);
|
||||
#endif
|
||||
for (mode = 0; mode < MODE_MAX; mode++) {
|
||||
newkeys = xcalloc(1, sizeof(*newkeys));
|
||||
kex->newkeys[mode] = newkeys;
|
||||
@ -441,6 +454,17 @@ kex_choose_conf(Kex *kex)
|
||||
choose_enc (&newkeys->enc, cprop[nenc], sprop[nenc]);
|
||||
choose_mac (&newkeys->mac, cprop[nmac], sprop[nmac]);
|
||||
choose_comp(&newkeys->comp, cprop[ncomp], sprop[ncomp]);
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
debug("REQUESTED ENC.NAME is '%s'", newkeys->enc.name);
|
||||
if (strcmp(newkeys->enc.name, "none") == 0) {
|
||||
debug("Requesting NONE. Authflag is %d", auth_flag);
|
||||
if (auth_flag == 1)
|
||||
debug("None requested post authentication.");
|
||||
else
|
||||
fatal("Pre-authentication none cipher requests "
|
||||
"are not allowed.");
|
||||
}
|
||||
#endif
|
||||
debug("kex: %s %s %s %s",
|
||||
ctos ? "client->server" : "server->client",
|
||||
newkeys->enc.name,
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: kex.h,v 1.52 2010/09/22 05:01:29 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
|
||||
@ -140,6 +141,10 @@ struct Kex {
|
||||
|
||||
int kex_names_valid(const char *);
|
||||
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
void kex_prop2buf(Buffer *, char *[PROPOSAL_MAX]);
|
||||
#endif
|
||||
|
||||
Kex *kex_setup(char *[PROPOSAL_MAX]);
|
||||
void kex_finish(Kex *);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: misc.c,v 1.84 2010/11/21 01:01:13 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2005,2006 Damien Miller. All rights reserved.
|
||||
@ -996,3 +997,34 @@ sock_set_v6only(int s)
|
||||
error("setsockopt IPV6_V6ONLY: %s", strerror(errno));
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
sock_get_rcvbuf(int *size, int rcvbuf)
|
||||
{
|
||||
int sock, socksize;
|
||||
socklen_t socksizelen = sizeof(socksize);
|
||||
|
||||
/*
|
||||
* Create a socket but do not connect it. We use it
|
||||
* only to get the rcv socket size.
|
||||
*/
|
||||
sock = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (sock < 0)
|
||||
sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the tcp_rcv_buf option is set and passed in, attempt to set the
|
||||
* buffer size to its value.
|
||||
*/
|
||||
if (rcvbuf)
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf,
|
||||
sizeof(rcvbuf));
|
||||
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_RCVBUF,
|
||||
&socksize, &socksizelen) == 0)
|
||||
if (size != NULL)
|
||||
*size = socksize;
|
||||
close(sock);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: misc.h,v 1.47 2010/11/21 01:01:13 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
@ -36,6 +37,7 @@ void sanitise_stdfd(void);
|
||||
void ms_subtract_diff(struct timeval *, int *);
|
||||
void ms_to_timeval(struct timeval *, int);
|
||||
void sock_set_v6only(int);
|
||||
void sock_get_rcvbuf(int *, int);
|
||||
|
||||
struct passwd *pwcopy(struct passwd *);
|
||||
const char *ssh_gai_strerror(int);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: myproposal.h,v 1.27 2010/09/01 22:42:13 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
@ -75,6 +76,10 @@
|
||||
"arcfour256,arcfour128," \
|
||||
"aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc," \
|
||||
"aes192-cbc,aes256-cbc,arcfour,rijndael-cbc@lysator.liu.se"
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
#define KEX_ENCRYPT_INCLUDE_NONE KEX_DEFAULT_ENCRYPT \
|
||||
",none"
|
||||
#endif
|
||||
#define KEX_DEFAULT_MAC \
|
||||
"hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160," \
|
||||
"hmac-ripemd160@openssh.com," \
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: packet.c,v 1.172 2010/11/13 23:27:50 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -195,6 +196,9 @@ struct session_state {
|
||||
};
|
||||
|
||||
static struct session_state *active_state, *backup_state;
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
static int rekey_requested = 0;
|
||||
#endif
|
||||
|
||||
static struct session_state *
|
||||
alloc_session_state(void)
|
||||
@ -1861,12 +1865,26 @@ packet_send_ignore(int nbytes)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
void
|
||||
packet_request_rekeying(void)
|
||||
{
|
||||
rekey_requested = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define MAX_PACKETS (1U<<31)
|
||||
int
|
||||
packet_need_rekeying(void)
|
||||
{
|
||||
if (datafellows & SSH_BUG_NOREKEY)
|
||||
return 0;
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
if (rekey_requested == 1) {
|
||||
rekey_requested = 0;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return
|
||||
(active_state->p_send.packets > MAX_PACKETS) ||
|
||||
(active_state->p_read.packets > MAX_PACKETS) ||
|
||||
@ -1958,3 +1976,11 @@ packet_restore_state(void)
|
||||
add_recv_bytes(len);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
int
|
||||
packet_get_authentication_state(void)
|
||||
{
|
||||
return (active_state->after_authentication);
|
||||
}
|
||||
#endif
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: packet.h,v 1.55 2010/11/13 23:27:50 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
@ -38,6 +39,9 @@ void packet_set_interactive(int, int, int);
|
||||
int packet_is_interactive(void);
|
||||
void packet_set_server(void);
|
||||
void packet_set_authenticated(void);
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
int packet_get_authentication_state(void);
|
||||
#endif
|
||||
|
||||
void packet_start(u_char);
|
||||
void packet_put_char(int ch);
|
||||
@ -117,6 +121,9 @@ do { \
|
||||
} while (0)
|
||||
|
||||
int packet_need_rekeying(void);
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
void packet_request_rekeying(void);
|
||||
#endif
|
||||
void packet_set_rekey_limit(u_int32_t);
|
||||
|
||||
void packet_backup_state(void);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: readconf.c,v 1.190 2010/11/13 23:27:50 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -138,6 +139,10 @@ typedef enum {
|
||||
oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
|
||||
oVisualHostKey, oUseRoaming, oZeroKnowledgePasswordAuthentication,
|
||||
oKexAlgorithms, oIPQoS,
|
||||
oHPNDisabled, oHPNBufferSize, oTcpRcvBufPoll, oTcpRcvBuf,
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
oNoneEnabled, oNoneSwitch,
|
||||
#endif
|
||||
oVersionAddendum,
|
||||
oDeprecated, oUnsupported
|
||||
} OpCodes;
|
||||
@ -249,6 +254,14 @@ static struct {
|
||||
#endif
|
||||
{ "kexalgorithms", oKexAlgorithms },
|
||||
{ "ipqos", oIPQoS },
|
||||
{ "hpndisabled", oHPNDisabled },
|
||||
{ "hpnbuffersize", oHPNBufferSize },
|
||||
{ "tcprcvbufpoll", oTcpRcvBufPoll },
|
||||
{ "tcprcvbuf", oTcpRcvBuf },
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
{ "noneenabled", oNoneEnabled },
|
||||
{ "noneswitch", oNoneSwitch },
|
||||
#endif
|
||||
|
||||
{ "versionaddendum", oVersionAddendum },
|
||||
{ NULL, oBadOption }
|
||||
@ -1021,6 +1034,47 @@ process_config_line(Options *options, const char *host,
|
||||
} while (arg != NULL && *arg != '\0');
|
||||
break;
|
||||
|
||||
case oHPNDisabled:
|
||||
intptr = &options->hpn_disabled;
|
||||
goto parse_flag;
|
||||
|
||||
case oHPNBufferSize:
|
||||
intptr = &options->hpn_buffer_size;
|
||||
goto parse_int;
|
||||
|
||||
case oTcpRcvBufPoll:
|
||||
intptr = &options->tcp_rcv_buf_poll;
|
||||
goto parse_flag;
|
||||
|
||||
case oTcpRcvBuf:
|
||||
intptr = &options->tcp_rcv_buf;
|
||||
goto parse_int;
|
||||
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
case oNoneEnabled:
|
||||
intptr = &options->none_enabled;
|
||||
goto parse_flag;
|
||||
|
||||
/*
|
||||
* We check to see if the command comes from the command line or not.
|
||||
* If it does then enable it otherwise fail. NONE must never be a
|
||||
* default configuration.
|
||||
*/
|
||||
case oNoneSwitch:
|
||||
if (strcmp(filename,"command-line") == 0) {
|
||||
intptr = &options->none_switch;
|
||||
goto parse_flag;
|
||||
} else {
|
||||
debug("NoneSwitch directive found in %.200s.",
|
||||
filename);
|
||||
error("NoneSwitch is found in %.200s.\n"
|
||||
"You may only use this configuration option "
|
||||
"from the command line", filename);
|
||||
error("Continuing...");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
case oDeprecated:
|
||||
debug("%s line %d: Deprecated option \"%s\"",
|
||||
filename, linenum, keyword);
|
||||
@ -1181,6 +1235,14 @@ initialize_options(Options * options)
|
||||
options->zero_knowledge_password_authentication = -1;
|
||||
options->ip_qos_interactive = -1;
|
||||
options->ip_qos_bulk = -1;
|
||||
options->hpn_disabled = -1;
|
||||
options->hpn_buffer_size = -1;
|
||||
options->tcp_rcv_buf_poll = -1;
|
||||
options->tcp_rcv_buf = -1;
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
options->none_enabled = -1;
|
||||
options->none_switch = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1345,6 +1407,36 @@ fill_default_options(Options * options)
|
||||
/* options->hostname will be set in the main program if appropriate */
|
||||
/* options->host_key_alias should not be set by default */
|
||||
/* options->preferred_authentications will be set in ssh */
|
||||
if (options->hpn_disabled == -1)
|
||||
options->hpn_disabled = 0;
|
||||
if (options->hpn_buffer_size > -1)
|
||||
{
|
||||
u_int maxlen;
|
||||
|
||||
/* If a user tries to set the size to 0 set it to 1KB. */
|
||||
if (options->hpn_buffer_size == 0)
|
||||
options->hpn_buffer_size = 1024;
|
||||
/* Limit the buffer to BUFFER_MAX_LEN. */
|
||||
maxlen = buffer_get_max_len();
|
||||
if (options->hpn_buffer_size > (maxlen / 1024)) {
|
||||
debug("User requested buffer larger than %ub: %ub. "
|
||||
"Request reverted to %ub", maxlen,
|
||||
options->hpn_buffer_size * 1024, maxlen);
|
||||
options->hpn_buffer_size = maxlen;
|
||||
}
|
||||
debug("hpn_buffer_size set to %d", options->hpn_buffer_size);
|
||||
}
|
||||
if (options->tcp_rcv_buf == 0)
|
||||
options->tcp_rcv_buf = 1;
|
||||
if (options->tcp_rcv_buf > -1)
|
||||
options->tcp_rcv_buf *= 1024;
|
||||
if (options->tcp_rcv_buf_poll == -1)
|
||||
options->tcp_rcv_buf_poll = 1;
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
/* options->none_enabled must not be set by default */
|
||||
if (options->none_switch == -1)
|
||||
options->none_switch = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: readconf.h,v 1.88 2010/11/13 23:27:50 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
@ -132,6 +133,17 @@ typedef struct {
|
||||
|
||||
int use_roaming;
|
||||
|
||||
int hpn_disabled; /* Switch to disable HPN buffer management. */
|
||||
int hpn_buffer_size; /* User definable size for HPN buffer
|
||||
* window. */
|
||||
int tcp_rcv_buf_poll; /* Option to poll recv buf every window
|
||||
* transfer. */
|
||||
int tcp_rcv_buf; /* User switch to set tcp recv buffer. */
|
||||
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
int none_enabled; /* Allow none to be used */
|
||||
int none_switch; /* Use none cipher */
|
||||
#endif
|
||||
} Options;
|
||||
|
||||
#define SSHCTL_MASTER_NO 0
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: servconf.c,v 1.213 2010/11/13 23:27:50 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
* All rights reserved
|
||||
@ -141,6 +142,12 @@ initialize_server_options(ServerOptions *options)
|
||||
options->authorized_principals_file = NULL;
|
||||
options->ip_qos_interactive = -1;
|
||||
options->ip_qos_bulk = -1;
|
||||
options->hpn_disabled = -1;
|
||||
options->hpn_buffer_size = -1;
|
||||
options->tcp_rcv_buf_poll = -1;
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
options->none_enabled = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -283,6 +290,37 @@ fill_default_server_options(ServerOptions *options)
|
||||
options->ip_qos_interactive = IPTOS_LOWDELAY;
|
||||
if (options->ip_qos_bulk == -1)
|
||||
options->ip_qos_bulk = IPTOS_THROUGHPUT;
|
||||
if (options->hpn_disabled == -1)
|
||||
options->hpn_disabled = 0;
|
||||
if (options->hpn_buffer_size == -1) {
|
||||
/*
|
||||
* HPN buffer size option not explicitly set. Try to figure
|
||||
* out what value to use or resort to default.
|
||||
*/
|
||||
options->hpn_buffer_size = CHAN_SES_WINDOW_DEFAULT;
|
||||
if (!options->hpn_disabled) {
|
||||
sock_get_rcvbuf(&options->hpn_buffer_size, 0);
|
||||
debug ("HPN Buffer Size: %d", options->hpn_buffer_size);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* In the case that the user sets both values in a
|
||||
* contradictory manner hpn_disabled overrrides hpn_buffer_size.
|
||||
*/
|
||||
if (options->hpn_disabled <= 0) {
|
||||
u_int maxlen;
|
||||
|
||||
maxlen = buffer_get_max_len();
|
||||
if (options->hpn_buffer_size == 0)
|
||||
options->hpn_buffer_size = 1;
|
||||
/* Limit the maximum buffer to BUFFER_MAX_LEN. */
|
||||
if (options->hpn_buffer_size > maxlen / 1024)
|
||||
options->hpn_buffer_size = maxlen;
|
||||
else
|
||||
options->hpn_buffer_size *= 1024;
|
||||
} else
|
||||
options->hpn_buffer_size = CHAN_TCP_WINDOW_DEFAULT;
|
||||
}
|
||||
|
||||
/* Turn privilege separation on by default */
|
||||
if (use_privsep == -1)
|
||||
@ -330,6 +368,10 @@ typedef enum {
|
||||
sZeroKnowledgePasswordAuthentication, sHostCertificate,
|
||||
sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
|
||||
sKexAlgorithms, sIPQoS,
|
||||
sHPNDisabled, sHPNBufferSize, sTcpRcvBufPoll,
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
sNoneEnabled,
|
||||
#endif
|
||||
sVersionAddendum,
|
||||
sDeprecated, sUnsupported
|
||||
} ServerOpCodes;
|
||||
@ -455,6 +497,12 @@ static struct {
|
||||
{ "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL },
|
||||
{ "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
|
||||
{ "ipqos", sIPQoS, SSHCFG_ALL },
|
||||
{ "hpndisabled", sHPNDisabled, SSHCFG_ALL },
|
||||
{ "hpnbuffersize", sHPNBufferSize, SSHCFG_ALL },
|
||||
{ "tcprcvbufpoll", sTcpRcvBufPoll, SSHCFG_ALL },
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
{ "noneenabled", sNoneEnabled, SSHCFG_ALL },
|
||||
#endif
|
||||
{ "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
|
||||
{ NULL, sBadOption, 0 }
|
||||
};
|
||||
@ -1409,6 +1457,24 @@ process_server_config_line(ServerOptions *options, char *line,
|
||||
} while (arg != NULL && *arg != '\0');
|
||||
break;
|
||||
|
||||
case sHPNDisabled:
|
||||
intptr = &options->hpn_disabled;
|
||||
goto parse_flag;
|
||||
|
||||
case sHPNBufferSize:
|
||||
intptr = &options->hpn_buffer_size;
|
||||
goto parse_int;
|
||||
|
||||
case sTcpRcvBufPoll:
|
||||
intptr = &options->tcp_rcv_buf_poll;
|
||||
goto parse_flag;
|
||||
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
case sNoneEnabled:
|
||||
intptr = &options->none_enabled;
|
||||
goto parse_flag;
|
||||
#endif
|
||||
|
||||
case sDeprecated:
|
||||
logit("%s line %d: Deprecated option %s",
|
||||
filename, linenum, arg);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: servconf.h,v 1.95 2010/11/13 23:27:50 djm Exp $ */
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
@ -160,6 +161,15 @@ typedef struct {
|
||||
char *revoked_keys_file;
|
||||
char *trusted_user_ca_keys;
|
||||
char *authorized_principals_file;
|
||||
|
||||
int hpn_disabled; /* Disable HPN functionality. */
|
||||
int hpn_buffer_size; /* Set HPN buffer size - default 2MB.*/
|
||||
int tcp_rcv_buf_poll; /* Poll TCP rcv window in autotuning
|
||||
* kernels. */
|
||||
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
int none_enabled; /* Enable NONE cipher switch. */
|
||||
#endif
|
||||
} ServerOptions;
|
||||
|
||||
void initialize_server_options(ServerOptions *);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: serverloop.c,v 1.159 2009/05/28 16:50:16 andreas Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -726,7 +727,7 @@ server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg)
|
||||
/* Wait until all output has been sent to the client. */
|
||||
drain_output();
|
||||
|
||||
debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.",
|
||||
debug("End of interactive session; stdin %ld, stdout (read %ld, " "sent %ld), stderr %ld bytes.",
|
||||
stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes);
|
||||
|
||||
/* Free and clear the buffers. */
|
||||
@ -998,8 +999,14 @@ server_request_tun(void)
|
||||
sock = tun_open(tun, mode);
|
||||
if (sock < 0)
|
||||
goto done;
|
||||
if (options.hpn_disabled)
|
||||
c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1,
|
||||
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1);
|
||||
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0,
|
||||
"tun", 1);
|
||||
else
|
||||
c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1,
|
||||
options.hpn_buffer_size, CHAN_TCP_PACKET_DEFAULT, 0,
|
||||
"tun", 1);
|
||||
c->datagram = 1;
|
||||
#if defined(SSH_TUN_FILTER)
|
||||
if (mode == SSH_TUNMODE_POINTOPOINT)
|
||||
@ -1035,6 +1042,8 @@ server_request_session(void)
|
||||
c = channel_new("session", SSH_CHANNEL_LARVAL,
|
||||
-1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT,
|
||||
0, "server-session", 1);
|
||||
if (!options.hpn_disabled && options.tcp_rcv_buf_poll)
|
||||
c->dynamic_window = 1;
|
||||
if (session_open(the_authctxt, c->self) != 1) {
|
||||
debug("session open failed, free channel %d", c->self);
|
||||
channel_free(c);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: session.c,v 1.258 2010/11/25 04:10:09 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
* All rights reserved
|
||||
@ -232,7 +233,10 @@ auth_input_request_forwarding(struct passwd * pw)
|
||||
goto authsock_err;
|
||||
}
|
||||
|
||||
/* Allocate a channel for the authentication agent socket. */
|
||||
/*
|
||||
* Allocate a channel for the authentication agent socket.
|
||||
* Ignore HPN on that one given no improvement expected.
|
||||
*/
|
||||
nc = channel_new("auth socket",
|
||||
SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1,
|
||||
CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
|
||||
@ -2283,10 +2287,14 @@ session_set_fds(Session *s, int fdin, int fdout, int fderr, int ignore_fderr,
|
||||
*/
|
||||
if (s->chanid == -1)
|
||||
fatal("no channel for session %d", s->self);
|
||||
channel_set_fds(s->chanid,
|
||||
fdout, fdin, fderr,
|
||||
if (options.hpn_disabled)
|
||||
channel_set_fds(s->chanid, fdout, fdin, fderr,
|
||||
ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
|
||||
1, is_tty, CHAN_SES_WINDOW_DEFAULT);
|
||||
else
|
||||
channel_set_fds(s->chanid, fdout, fdin, fderr,
|
||||
ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ,
|
||||
1, is_tty, options.hpn_buffer_size);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -242,7 +242,8 @@ diagnostic messages from
|
||||
Specify how many requests may be outstanding at any one time.
|
||||
Increasing this may slightly improve file transfer speed
|
||||
but will increase memory usage.
|
||||
The default is 64 outstanding requests.
|
||||
The default is 256 outstanding requests providing for 8MB
|
||||
of outstanding data with a 32KB buffer.
|
||||
.It Fl r
|
||||
Recursively copy entire directories when uploading and downloading.
|
||||
Note that
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: sftp.c,v 1.132 2010/12/04 00:18:01 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
||||
*
|
||||
@ -69,7 +70,7 @@ typedef void EditLine;
|
||||
#include "sftp-client.h"
|
||||
|
||||
#define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */
|
||||
#define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */
|
||||
#define DEFAULT_NUM_REQUESTS 256 /* # concurrent outstanding requests */
|
||||
|
||||
/* File to read commands from */
|
||||
FILE* infile;
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: ssh.c,v 1.356 2011/01/06 22:23:53 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -546,6 +547,15 @@ main(int ac, char **av)
|
||||
break;
|
||||
case 'T':
|
||||
no_tty_flag = 1;
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
/*
|
||||
* Ensure that the user does not try to backdoor a
|
||||
* NONE cipher switch on an interactive session by
|
||||
* explicitly disabling it if the user asks for a
|
||||
* session without a tty.
|
||||
*/
|
||||
options.none_switch = 0;
|
||||
#endif
|
||||
break;
|
||||
case 'o':
|
||||
dummy = 1;
|
||||
@ -1368,9 +1378,46 @@ ssh_session2_open(void)
|
||||
if (!isatty(err))
|
||||
set_nonblock(err);
|
||||
|
||||
window = CHAN_SES_WINDOW_DEFAULT;
|
||||
/*
|
||||
* We need to check to see what to do about buffer sizes here.
|
||||
* - In an HPN to non-HPN connection we want to limit the window size to
|
||||
* something reasonable in case the far side has the large window bug.
|
||||
* - In an HPN to HPN connection we want to use the max window size but
|
||||
* allow the user to override it.
|
||||
* - Lastly if HPN is disabled then use the ssh standard window size.
|
||||
*
|
||||
* We cannot just do a getsockopt() here and set the ssh window to that
|
||||
* as in case of autotuning of socket buffers the window would get stuck
|
||||
* at the initial buffer size, generally less than 96k. Therefore we
|
||||
* need to set the maximum ssh window size to the maximum HPN buffer
|
||||
* size unless the user has set TcpRcvBufPoll to no. In that case we
|
||||
* can just set the window to the minimum of HPN buffer size and TCP
|
||||
* receive buffer size.
|
||||
*/
|
||||
if (tty_flag)
|
||||
options.hpn_buffer_size = CHAN_SES_WINDOW_DEFAULT;
|
||||
else
|
||||
options.hpn_buffer_size = CHAN_HPN_MIN_WINDOW_DEFAULT;
|
||||
|
||||
if (datafellows & SSH_BUG_LARGEWINDOW) {
|
||||
debug("HPN to Non-HPN Connection");
|
||||
} else if (options.tcp_rcv_buf_poll <= 0) {
|
||||
sock_get_rcvbuf(&options.hpn_buffer_size, 0);
|
||||
debug("HPNBufferSize set to TCP RWIN: %d",
|
||||
options.hpn_buffer_size);
|
||||
} else if (options.tcp_rcv_buf > 0) {
|
||||
sock_get_rcvbuf(&options.hpn_buffer_size,
|
||||
options.tcp_rcv_buf);
|
||||
debug("HPNBufferSize set to user TCPRcvBuf: %d",
|
||||
options.hpn_buffer_size);
|
||||
}
|
||||
debug("Final hpn_buffer_size = %d", options.hpn_buffer_size);
|
||||
channel_set_hpn(options.hpn_disabled, options.hpn_buffer_size);
|
||||
window = options.hpn_buffer_size;
|
||||
|
||||
packetmax = CHAN_SES_PACKET_DEFAULT;
|
||||
if (tty_flag) {
|
||||
window = CHAN_SES_WINDOW_DEFAULT;
|
||||
window >>= 1;
|
||||
packetmax >>= 1;
|
||||
}
|
||||
@ -1378,7 +1425,10 @@ ssh_session2_open(void)
|
||||
"session", SSH_CHANNEL_OPENING, in, out, err,
|
||||
window, packetmax, CHAN_EXTENDED_WRITE,
|
||||
"client-session", /*nonblock*/0);
|
||||
|
||||
if (!options.hpn_disabled && options.tcp_rcv_buf_poll > 0) {
|
||||
c->dynamic_window = 1;
|
||||
debug("Enabled Dynamic Window Scaling\n");
|
||||
}
|
||||
debug3("ssh_session2_open: channel_new: %d", c->self);
|
||||
|
||||
channel_send_open(c->self);
|
||||
|
@ -182,6 +182,29 @@ ssh_kill_proxy_command(void)
|
||||
kill(proxy_command_pid, SIGHUP);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set TCP receive buffer if requested.
|
||||
* Note: tuning needs to happen after the socket is created but before the
|
||||
* connection happens so winscale is negotiated properly.
|
||||
*/
|
||||
static void
|
||||
ssh_set_socket_recvbuf(int sock)
|
||||
{
|
||||
void *buf = (void *)&options.tcp_rcv_buf;
|
||||
int socksize, sz = sizeof(options.tcp_rcv_buf);
|
||||
socklen_t len = sizeof(int);
|
||||
|
||||
debug("setsockopt attempting to set SO_RCVBUF to %d",
|
||||
options.tcp_rcv_buf);
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, buf, sz) >= 0) {
|
||||
getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &socksize, &len);
|
||||
debug("setsockopt SO_RCVBUF: %.100s %d", strerror(errno),
|
||||
socksize);
|
||||
} else
|
||||
error("Couldn't set socket receive buffer to %d: %.100s",
|
||||
options.tcp_rcv_buf, strerror(errno));
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a (possibly privileged) socket for use as the ssh connection.
|
||||
*/
|
||||
@ -205,6 +228,8 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
|
||||
strerror(errno));
|
||||
else
|
||||
debug("Allocated local port %d.", p);
|
||||
if (options.tcp_rcv_buf > 0)
|
||||
ssh_set_socket_recvbuf(sock);
|
||||
return sock;
|
||||
}
|
||||
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||
@ -214,6 +239,9 @@ ssh_create_socket(int privileged, struct addrinfo *ai)
|
||||
}
|
||||
fcntl(sock, F_SETFD, FD_CLOEXEC);
|
||||
|
||||
if (options.tcp_rcv_buf > 0)
|
||||
ssh_set_socket_recvbuf(sock);
|
||||
|
||||
/* Bind the socket to an alternative local IP address */
|
||||
if (options.bind_address == NULL)
|
||||
return sock;
|
||||
@ -557,7 +585,7 @@ ssh_exchange_identification(int timeout_ms)
|
||||
snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s",
|
||||
compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1,
|
||||
compat20 ? PROTOCOL_MINOR_2 : minor1,
|
||||
SSH_VERSION, compat20 ? "\r\n" : "\n");
|
||||
SSH_RELEASE, compat20 ? "\r\n" : "\n");
|
||||
if (roaming_atomicio(vwrite, connection_out, buf, strlen(buf))
|
||||
!= strlen(buf))
|
||||
fatal("write: %.100s", strerror(errno));
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: sshconnect2.c,v 1.186 2010/11/29 23:45:51 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||
* Copyright (c) 2008 Damien Miller. All rights reserved.
|
||||
@ -81,6 +82,16 @@
|
||||
extern char *client_version_string;
|
||||
extern char *server_version_string;
|
||||
extern Options options;
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
extern Kex *xxx_kex;
|
||||
|
||||
/*
|
||||
* tty_flag is set in ssh.c so we can use it here. If set then prevent
|
||||
* the switch to the null cipher.
|
||||
*/
|
||||
|
||||
extern int tty_flag;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* SSH2 key exchange
|
||||
@ -419,6 +430,29 @@ ssh_userauth2(const char *local_user, const char *server_user, char *host,
|
||||
pubkey_cleanup(&authctxt);
|
||||
dispatch_range(SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL);
|
||||
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
/*
|
||||
* If the user explicitly requests to use the none cipher enable it
|
||||
* post authentication and only if the right conditions are met: both
|
||||
* of the NONE switches must be true and there must be no tty allocated.
|
||||
*/
|
||||
if (options.none_switch == 1 && options.none_enabled == 1) {
|
||||
if (!tty_flag) {
|
||||
debug("Requesting none cipher re-keying...");
|
||||
myproposal[PROPOSAL_ENC_ALGS_STOC] = "none";
|
||||
myproposal[PROPOSAL_ENC_ALGS_CTOS] = "none";
|
||||
kex_prop2buf(&xxx_kex->my, myproposal);
|
||||
packet_request_rekeying();
|
||||
fprintf(stderr, "WARNING: enabled NONE cipher\n");
|
||||
} else {
|
||||
/* Requested NONE cipher on an interactive session. */
|
||||
debug("Cannot switch to NONE cipher with tty "
|
||||
"allocated");
|
||||
fprintf(stderr, "NONE cipher switch disabled given "
|
||||
"a TTY is allocated\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
debug("Authentication succeeded (%s).", authctxt.method->name);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* $OpenBSD: sshd.c,v 1.381 2011/01/11 06:13:10 djm Exp $ */
|
||||
/* $FreeBSD$ */
|
||||
/*
|
||||
* Author: Tatu Ylonen <ylo@cs.hut.fi>
|
||||
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
|
||||
@ -429,7 +430,7 @@ sshd_exchange_identification(int sock_in, int sock_out)
|
||||
minor = PROTOCOL_MINOR_1;
|
||||
}
|
||||
snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", major, minor,
|
||||
SSH_VERSION, newline);
|
||||
SSH_RELEASE, newline);
|
||||
server_version_string = xstrdup(buf);
|
||||
|
||||
/* Send our protocol version identification. */
|
||||
@ -1011,6 +1012,8 @@ server_listen(void)
|
||||
int ret, listen_sock, on = 1;
|
||||
struct addrinfo *ai;
|
||||
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
|
||||
int socksize;
|
||||
socklen_t len;
|
||||
|
||||
for (ai = options.listen_addrs; ai; ai = ai->ai_next) {
|
||||
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
|
||||
@ -1051,6 +1054,11 @@ server_listen(void)
|
||||
|
||||
debug("Bind to port %s on %s.", strport, ntop);
|
||||
|
||||
len = sizeof(socksize);
|
||||
getsockopt(listen_sock, SOL_SOCKET, SO_RCVBUF, &socksize, &len);
|
||||
debug("Server TCP RWIN socket size: %d", socksize);
|
||||
debug("HPN Buffer Size: %d", options.hpn_buffer_size);
|
||||
|
||||
/* Bind the socket to the desired port. */
|
||||
if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) {
|
||||
error("Bind to port %s on %s failed: %.200s.",
|
||||
@ -1960,6 +1968,9 @@ main(int ac, char **av)
|
||||
/* Log the connection. */
|
||||
verbose("Connection from %.500s port %d", remote_ip, remote_port);
|
||||
|
||||
/* Set HPN options for the child. */
|
||||
channel_set_hpn(options.hpn_disabled, options.hpn_buffer_size);
|
||||
|
||||
/*
|
||||
* We don't want to listen forever unless the other side
|
||||
* successfully authenticates itself. So we set up an alarm which is
|
||||
@ -2319,6 +2330,12 @@ do_ssh2_kex(void)
|
||||
if (options.ciphers != NULL) {
|
||||
myproposal[PROPOSAL_ENC_ALGS_CTOS] =
|
||||
myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers;
|
||||
#ifdef NONE_CIPHER_ENABLED
|
||||
} else if (options.none_enabled == 1) {
|
||||
debug ("WARNING: None cipher enabled");
|
||||
myproposal[PROPOSAL_ENC_ALGS_CTOS] =
|
||||
myproposal[PROPOSAL_ENC_ALGS_STOC] = KEX_ENCRYPT_INCLUDE_NONE;
|
||||
#endif
|
||||
}
|
||||
myproposal[PROPOSAL_ENC_ALGS_CTOS] =
|
||||
compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]);
|
||||
|
@ -117,6 +117,18 @@
|
||||
# override default of no subsystems
|
||||
Subsystem sftp /usr/libexec/sftp-server
|
||||
|
||||
# Disable HPN tuning improvements.
|
||||
#HPNDisabled no
|
||||
|
||||
# Buffer size for HPN to non-HPN connections.
|
||||
#HPNBufferSize 2048
|
||||
|
||||
# TCP receive socket buffer polling for HPN. Disable on non autotuning kernels.
|
||||
#TcpRcvBufPoll yes
|
||||
|
||||
# Allow the use of the NONE cipher.
|
||||
#NoneEnabled no
|
||||
|
||||
# Example of overriding settings on a per-user basis
|
||||
#Match User anoncvs
|
||||
# X11Forwarding no
|
||||
|
@ -40,7 +40,7 @@ const char *
|
||||
ssh_version_get(void) {
|
||||
|
||||
if (version == NULL)
|
||||
version = xstrdup(SSH_VERSION_BASE " " SSH_VERSION_ADDENDUM);
|
||||
version = xstrdup(SSH_VERSION);
|
||||
return (version);
|
||||
}
|
||||
|
||||
@ -50,11 +50,13 @@ ssh_version_set_addendum(const char *add) {
|
||||
size_t size;
|
||||
|
||||
if (add != NULL) {
|
||||
size = strlen(SSH_VERSION_BASE) + 1 + strlen(add) + 1;
|
||||
size = strlen(SSH_VERSION_BASE) + strlen(SSH_VERSION_HPN) + 1 +
|
||||
strlen(add) + 1;
|
||||
newvers = xmalloc(size);
|
||||
snprintf(newvers, size, "%s %s", SSH_VERSION_BASE, add);
|
||||
snprintf(newvers, size, "%s %s", SSH_VERSION_BASE,
|
||||
SSH_VERSION_HPN, add);
|
||||
} else {
|
||||
newvers = xstrdup(SSH_VERSION_BASE);
|
||||
newvers = xstrdup(SSH_VERSION_BASE SSH_VERSION_HPN);
|
||||
}
|
||||
if (version != NULL)
|
||||
xfree(version);
|
||||
|
@ -3,10 +3,11 @@
|
||||
|
||||
#ifndef SSH_VERSION
|
||||
|
||||
#define SSH_VERSION (ssh_version_get())
|
||||
#define SSH_RELEASE (ssh_version_get())
|
||||
#define SSH_VERSION_BASE "OpenSSH_5.8p2"
|
||||
#define SSH_VERSION_ADDENDUM "FreeBSD-20110503"
|
||||
#define SSH_VERSION_HPN "_hpn13v11"
|
||||
#define SSH_VERSION SSH_VERSION_BASE SSH_VERSION_HPN " " SSH_VERSION_ADDENDUM
|
||||
#define SSH_RELEASE (ssh_version_get())
|
||||
|
||||
const char *ssh_version_get(void);
|
||||
void ssh_version_set_addendum(const char *);
|
||||
|
Loading…
Reference in New Issue
Block a user