aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh')
-rw-r--r--lib/ssh/doc/src/notes.xml35
-rw-r--r--lib/ssh/doc/src/ssh.xml3
-rw-r--r--lib/ssh/doc/src/ssh_client_channel.xml14
-rw-r--r--lib/ssh/doc/src/ssh_connection.xml446
-rw-r--r--lib/ssh/doc/src/ssh_server_channel.xml8
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml468
-rw-r--r--lib/ssh/doc/src/ssh_sftpd.xml33
-rw-r--r--lib/ssh/doc/src/using_ssh.xml14
-rw-r--r--lib/ssh/src/Makefile2
-rw-r--r--lib/ssh/src/ssh.app.src10
-rw-r--r--lib/ssh/src/ssh.erl2
-rw-r--r--lib/ssh/src/ssh.hrl39
-rw-r--r--lib/ssh/src/ssh_client_channel.erl2
-rw-r--r--lib/ssh/src/ssh_connect.hrl3
-rw-r--r--lib/ssh/src/ssh_connection.erl222
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl116
-rw-r--r--lib/ssh/src/ssh_message.erl18
-rw-r--r--lib/ssh/src/ssh_server_channel.erl2
-rw-r--r--lib/ssh/src/ssh_sftp.erl376
-rw-r--r--lib/ssh/src/ssh_sftpd.erl14
-rw-r--r--lib/ssh/src/ssh_transport.erl561
-rw-r--r--lib/ssh/src/ssh_userauth.hrl78
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl2
-rw-r--r--lib/ssh/test/ssh_bench_SUITE.erl12
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_SUITE.erl4
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_client.erl2
-rw-r--r--lib/ssh/test/ssh_chan_behaviours_server.erl2
-rw-r--r--lib/ssh/test/ssh_compat_SUITE.erl17
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl4
-rw-r--r--lib/ssh/test/ssh_options_SUITE.erl2
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE.erl6
-rw-r--r--lib/ssh/test/ssh_test_lib.erl4
-rw-r--r--lib/ssh/test/ssh_trpt_test_lib.erl69
-rw-r--r--lib/ssh/vsn.mk2
34 files changed, 1332 insertions, 1260 deletions
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 9503060140..60f20c7c3f 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,21 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.7.7</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ SSH uses the new crypto API.</p>
+ <p>
+ Own Id: OTP-15673</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.7.6</title>
<section><title>Improvements and New Features</title>
@@ -372,6 +387,24 @@
</section>
</section>
+<section><title>Ssh 4.6.9.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If a client was connected to an server on an already open
+ socket, the callback <c>fun(PeerName,FingerPrint)</c> in
+ the <c>accept_callback</c> option passed the local name
+ in the argument PeerName instead of the remote name.</p>
+ <p>
+ Own Id: OTP-15763</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.6.9.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -2977,7 +3010,7 @@
</item>
<item>
<p>
- Fixed internal error on when client and server can not
+ Fixed internal error on when client and server cannot
agree o which authmethod to use.</p>
<p>
Own Id: OTP-10731 Aux Id: seq12237 </p>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 1a53a2ea98..8b7cb4dcd4 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -46,7 +46,7 @@
In that encrypted connection one or more channels could be opened with
<seealso marker="ssh_connection#session_channel/2">ssh_connection:session_channel/2,4</seealso>.
</p>
- <p>Each channel is an isolated "pipe" between a client-side process and a server-side process. Thoose process
+ <p>Each channel is an isolated "pipe" between a client-side process and a server-side process. Those process
pairs could handle for example file transfers (sftp) or remote command execution (shell, exec and/or cli).
If a custom shell is implemented, the user of the client could execute the special commands remotely. Note that
the user is not necessarily a human but probably a system interfacing the SSH app.
@@ -347,6 +347,7 @@
<datatype>
<name name="subsystem_daemon_option"/>
+ <name name="subsystem_specs"/>
<name name="subsystem_spec"/>
<desc>
<p>Defines a subsystem in the daemon.</p>
diff --git a/lib/ssh/doc/src/ssh_client_channel.xml b/lib/ssh/doc/src/ssh_client_channel.xml
index cd28b95fd3..e6683dbd0b 100644
--- a/lib/ssh/doc/src/ssh_client_channel.xml
+++ b/lib/ssh/doc/src/ssh_client_channel.xml
@@ -150,12 +150,12 @@
<tag><c>{init_args(), list()}</c></tag>
<item><p>The list of arguments to the <c>init</c> function of the callback module.</p></item>
- <tag><c>{cm, ssh:connection_ref()}</c></tag>
+ <tag><c>{cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>}</c></tag>
<item><p>Reference to the <c>ssh</c> connection as returned by
<seealso marker="ssh#connect-3">ssh:connect/3</seealso>.
</p></item>
- <tag><c>{channel_id, ssh:channel_id()}</c></tag>
+ <tag><c>{channel_id, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>}</c></tag>
<item><p>Id of the <c>ssh</c> channel as returned by
<seealso marker="ssh_connection#session_channel/2">ssh_connection:session_channel/2,4</seealso>.
</p></item>
@@ -198,7 +198,7 @@
{ok, ChannelRef} | {error, Reason}</name>
<fsummary>Starts a process that handles an SSH channel.</fsummary>
<type>
- <v>SshConnection = ssh:connection_ref()</v>
+ <v>SshConnection = <seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso></v>
<d>As returned by <seealso marker="ssh#connect-3">ssh:connect/3</seealso></d>
<v>ChannelId = <seealso marker="ssh#type-channel_id">ssh:channel_id()</seealso></v>
@@ -374,7 +374,7 @@
function and all channels are to handle the following message.</p>
<taglist>
- <tag><c>{ssh_channel_up, ssh:channel_id(), ssh:connection_ref()}</c></tag>
+ <tag><c>{ssh_channel_up, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>}</c></tag>
<item><p>This is the first message that the channel receives.
It is sent just before the <seealso
marker="#init-1">init/1</seealso> function
@@ -393,21 +393,21 @@
ChannelId, State}</name>
<fsummary>Handles <c>ssh</c> connection protocol messages.</fsummary>
<type>
- <v>Msg = ssh_connection:event()</v>
+ <v>Msg = <seealso marker="ssh_connection#type-event">ssh_connection:event()</seealso></v>
<v>ChannelId = <seealso marker="ssh#type-channel_id">ssh:channel_id()</seealso></v>
<v>State = term()</v>
</type>
<desc>
<p>Handles SSH Connection Protocol messages that may need
service-specific attention. For details,
- see <seealso marker="ssh_connection"> ssh_connection:event()</seealso>.
+ see <seealso marker="ssh_connection#type-event">ssh_connection:event()</seealso>.
</p>
<p>The following message is taken care of by the
<c>ssh_client_channel</c> behavior.</p>
<taglist>
- <tag><c>{closed, ssh:channel_id()}</c></tag>
+ <tag><c>{closed, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>}</c></tag>
<item><p>The channel behavior sends a close message to the
other side, if such a message has not already been sent.
Then it terminates the channel with reason <c>normal</c>.</p></item>
diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml
index 2a701929f6..9fa1da659c 100644
--- a/lib/ssh/doc/src/ssh_connection.xml
+++ b/lib/ssh/doc/src/ssh_connection.xml
@@ -40,128 +40,119 @@
<p>The <url href="http://www.ietf.org/rfc/rfc4254.txt">SSH Connection Protocol</url>
is used by clients and servers, that is, SSH channels, to communicate over the
SSH connection. The API functions in this module send SSH Connection Protocol events,
- which are received as messages by the remote channel.
- If the receiving channel is an Erlang process, the
- messages have the format
- <c><![CDATA[{ssh_cm, connection_ref(), ssh_event_msg()}]]></c>.
+ which are received as messages by the remote channel handling the remote channel.
+ The Erlang format of thoose messages is
+ (see also <seealso marker="#type-event">below</seealso>):
+ </p>
+ <p><c>{ssh_cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>, </c><seealso marker="#type-channel_msg"><c>channel_msg()</c></seealso><c>}</c>
+ </p>
+ <p>
If the <seealso marker="ssh_client_channel">ssh_client_channel</seealso> behavior is used to
implement the channel process, these messages are handled by
<seealso marker="ssh_client_channel#Module:handle_ssh_msg-2">handle_ssh_msg/2</seealso>.</p>
</description>
- <section>
- <title>DATA TYPES</title>
-
- <p>Type definitions that are used more than once in this module,
- or abstractions to indicate the intended use of the data
- type, or both:</p>
-
- <taglist>
- <tag><c>boolean() =</c></tag>
- <item><p><c>true | false </c></p></item>
- <tag><c>string() =</c></tag>
- <item><p>list of ASCII characters</p></item>
- <tag><c>timeout() =</c></tag>
- <item><p><c>infinity | integer()</c> in milliseconds</p></item>
- <tag><c>connection_ref() =</c></tag>
- <item><p>opaque() -as returned by
- <c>ssh:connect/3</c> or sent to an SSH channel processes</p></item>
- <tag><c>channel_id() =</c></tag>
- <item><p><c>integer()</c></p></item>
- <tag><c>ssh_data_type_code() =</c></tag>
- <item><p><c>1</c> ("stderr") | <c>0</c> ("normal") are
- valid values, see
- <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> Section 5.2.</p></item>
- <tag><c>ssh_request_status() =</c></tag>
- <item><p> <c>success | failure</c></p></item>
- <tag><c>event() =</c></tag>
- <item><p><c>{ssh_cm, connection_ref(), ssh_event_msg()}</c></p></item>
- <tag><c>ssh_event_msg() =</c></tag>
- <item><p><c>data_events() | status_events() | terminal_events()</c></p></item>
- <tag><c>reason() =</c></tag>
- <item><p><c>timeout | closed</c></p></item>
- </taglist>
-
- <taglist>
- <tag><em>data_events()</em></tag>
- <item>
- <taglist>
- <tag><c><![CDATA[{data, channel_id(), ssh_data_type_code(), Data :: binary()}]]></c></tag>
- <item><p>Data has arrived on the channel. This event is sent as a
- result of calling <seealso marker="ssh_connection#send-3">
- ssh_connection:send/[3,4,5]</seealso>.</p></item>
-
- <tag><c><![CDATA[{eof, channel_id()}]]></c></tag>
- <item><p>Indicates that the other side sends no more data.
- This event is sent as a result of calling <seealso
- marker="ssh_connection#send_eof-2"> ssh_connection:send_eof/2</seealso>.
- </p></item>
- </taglist>
- </item>
+ <datatypes>
+ <datatype>
+ <name name="ssh_data_type_code"/>
+ <desc>
+ <p>The valid values are <c>0</c> ("normal") and <c>1</c> ("stderr"), see
+ <url href="https://tools.ietf.org/html/rfc4254#page-8">RFC 4254, Section 5.2</url>.</p>
+ </desc>
+ </datatype>
- <tag><em>status_events()</em></tag>
- <item>
+ <datatype>
+ <name name="result"/>
+ <name name="reason"/>
+ <desc>
+ <p>The result of a call.</p>
+ <p>If the request reached the peer, was handled and the response
+ reached the requesting node the <seealso marker="#type-req_status">req_status()</seealso>
+ is the status reported from the peer.</p>
+ <p>If not, the <seealso marker="#type-reason">reason()</seealso> indicates what went wrong:</p>
+ <taglist>
+ <tag><c>closed</c></tag>
+ <item>indicates that the channel or connection was closed when trying to send the request
+ </item>
+ <tag><c>timeout</c></tag>
+ <item>indicates that the operation exceeded a time limit
+ </item>
+ </taglist>
+ </desc>
+ </datatype>
- <taglist>
- <tag><c><![CDATA[{signal, channel_id(), ssh_signal()}]]></c></tag>
- <item><p>A signal can be delivered to the remote process/service
- using the following message. Some systems do not support
- signals, in which case they are to ignore this message. There is
- currently no function to generate this event as the signals
- referred to are on OS-level and not something generated by an
- Erlang program.</p></item>
+ <datatype>
+ <name name="req_status"/>
+ <desc>
+ <p>The status of a request.
+ Coresponds to the <c>SSH_MSG_CHANNEL_SUCCESS</c> and <c>SSH_MSG_CHANNEL_FAILURE</c> values in
+ <url href="https://tools.ietf.org/html/rfc4254#section-5.4">RFC 4254, Section 5.4</url>.
+ </p>
+ </desc>
+ </datatype>
- <tag><c><![CDATA[{exit_signal, channel_id(), ExitSignal :: string(), ErrorMsg ::string(),
- LanguageString :: string()}]]></c></tag>
+ <datatype_title>SSH Connection Protocol: General</datatype_title>
+ <datatype>
+ <name name="event"/>
+ <name name="channel_msg"/>
+ <desc>
+ <p>As mentioned in the introduction, the
+ <url href="https://tools.ietf.org/html/rfc4254">SSH Connection Protocol</url>
+ events are handled as messages. When writing a channel handling process without using
+ the support by the <seealso marker="ssh_client_channel">ssh_client_channel</seealso>
+ behavior the process must handle thoose messages.
+ </p>
+ </desc>
+ </datatype>
- <item><p>A remote execution can terminate violently because of a signal.
- Then this message can be received. For details on valid string
- values, see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url>
- Section 6.10, which shows a special case of these signals.</p></item>
+ <datatype>
+ <name name="want_reply"/>
+ <desc>
+ <p>Messages that include a <c>WantReply</c> expect the channel handling
+ process to call <seealso marker="ssh_connection#reply_request-4">
+ ssh_connection:reply_request/4</seealso>
+ with the boolean value of <c>WantReply</c> as the second argument.</p>
+ </desc>
+ </datatype>
- <tag><c><![CDATA[{exit_status, channel_id(), ExitStatus :: integer()}]]></c></tag>
- <item><p>When the command running at the other end terminates, the
- following message can be sent to return the exit status of the
- command. A zero <c>exit_status</c> usually means that the command
- terminated successfully. This event is sent as a result of calling
- <seealso marker="ssh_connection#exit_status-3">
- ssh_connection:exit_status/3</seealso>.</p></item>
- <tag><c><![CDATA[{closed, channel_id()}]]></c></tag>
- <item><p>This event is sent as a result of calling
- <seealso marker="ssh_connection#close-2">ssh_connection:close/2</seealso>.
- Both the handling of this event and sending it are taken care of by the
- <seealso marker="ssh_client_channel">ssh_client_channel</seealso> behavior.</p></item>
-
- </taglist>
- </item>
+ <datatype_title>Data Transfer (RFC 4254, section 5.2)</datatype_title>
+ <datatype>
+ <name name="data_ch_msg"/>
+ <desc>
+ <p>Data has arrived on the channel. This event is sent as a result of calling
+ <seealso marker="ssh_connection#send-3"> ssh_connection:send/[3,4,5]</seealso>.
+ </p>
+ </desc>
+ </datatype>
- <tag><em>terminal_events()</em></tag>
- <item>
- <p>Channels implementing a shell and command execution on the
- server side are to handle the following messages that can be sent by client-
- channel processes.</p>
+ <datatype_title>Closing a Channel (RFC 4254, section 5.3)</datatype_title>
+ <datatype>
+ <name name="eof_ch_msg"/>
+ <desc>
+ <p>Indicates that the other side sends no more data. This event is sent as a result of calling
+ <seealso marker="ssh_connection#send_eof-2"> ssh_connection:send_eof/2</seealso>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="closed_ch_msg"/>
+ <desc>
+ <p>This event is sent as a result of calling
+ <seealso marker="ssh_connection#close-2">ssh_connection:close/2</seealso>.
+ Both the handling of this event and sending it are taken care of by the
+ <seealso marker="ssh_client_channel">ssh_client_channel</seealso> behavior.</p>
+ </desc>
+ </datatype>
- <p>Events that include a <c>WantReply</c> expect the event handling
- process to call <seealso marker="ssh_connection#reply_request-4">
- ssh_connection:reply_request/4</seealso>
- with the boolean value of <c>WantReply</c> as the second argument.</p>
- <taglist>
- <tag><c><![CDATA[{env, channel_id(), WantReply :: boolean(),
- Var ::string(), Value :: string()}]]></c></tag>
- <item><p>Environment variables can be passed to the shell/command
- to be started later. This event is sent as a result of calling <seealso
- marker="ssh_connection#setenv-5"> ssh_connection:setenv/5</seealso>.
- </p></item>
-
- <tag><c><![CDATA[{pty, channel_id(),
- WantReply :: boolean(), {Terminal :: string(), CharWidth :: integer(),
- RowHeight :: integer(), PixelWidth :: integer(), PixelHeight :: integer(),
- TerminalModes :: [{Opcode :: atom() | integer(),
- Value :: integer()}]}}]]></c></tag>
- <item><p>A pseudo-terminal has been requested for the
+ <datatype_title>Requesting a Pseudo-Terminal (RFC 4254, section 6.2)</datatype_title>
+ <datatype>
+ <name name="pty_ch_msg"/>
+ <name name="term_mode"/>
+ <desc>
+ <p>A pseudo-terminal has been requested for the
session. <c>Terminal</c> is the value of the TERM environment
variable value, that is, <c>vt100</c>. Zero dimension parameters must
be ignored. The character/row dimensions override the pixel
@@ -169,46 +160,103 @@
drawable area of the window. <c>Opcode</c> in the
<c>TerminalModes</c> list is the mnemonic name, represented
as a lowercase Erlang atom, defined in
- <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url>, Section 8.
+ <url href="https://tools.ietf.org/html/rfc4254#section-8">RFC 4254</url>, Section 8.
It can also be an <c>Opcode</c> if the mnemonic name is not listed in the
RFC. Example: <c>OP code: 53, mnemonic name ECHO erlang atom:
echo</c>. This event is sent as a result of calling <seealso
- marker="ssh_connection#ptty_alloc/4">ssh_connection:ptty_alloc/4</seealso>.</p></item>
+ marker="ssh_connection#ptty_alloc/4">ssh_connection:ptty_alloc/4</seealso>.</p>
+ </desc>
+ </datatype>
+
- <tag><c><![CDATA[{shell, WantReply :: boolean()}]]></c></tag>
- <item><p>This message requests that the user default shell
+ <datatype_title>Environment Variable Passing (RFC 4254, section 6.4)</datatype_title>
+ <datatype>
+ <name name="env_ch_msg"/>
+ <desc>
+ <p>Environment variables can be passed to the shell/command
+ to be started later. This event is sent as a result of calling <seealso
+ marker="ssh_connection#setenv-5"> ssh_connection:setenv/5</seealso>.
+ </p>
+ </desc>
+ </datatype>
+
+
+ <datatype_title>Starting a Shell or Command (RFC 4254, section 6.5)</datatype_title>
+ <datatype>
+ <name name="shell_ch_msg"/>
+ <desc>
+ <p>This message requests that the user default shell
is started at the other end. This event is sent as a result of calling
<seealso marker="ssh_connection#shell-2"> ssh_connection:shell/2</seealso>.
- </p></item>
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="exec_ch_msg"/>
+ <desc>
+ <p>This message requests that the server starts
+ execution of the given command. This event is sent as a result of calling <seealso
+ marker="ssh_connection#exec-4">ssh_connection:exec/4 </seealso>.
+ </p>
+ </desc>
+ </datatype>
+
- <tag><c><![CDATA[{window_change, channel_id(), CharWidth() :: integer(),
- RowHeight :: integer(), PixWidth :: integer(), PixHeight :: integer()}]]></c></tag>
- <item><p>When the window (terminal) size changes on the client
+ <datatype_title>Window Dimension Change Message (RFC 4254, section 6.7)</datatype_title>
+ <datatype>
+ <name name="window_change_ch_msg"/>
+ <desc>
+ <p>When the window (terminal) size changes on the client
side, it <em>can</em> send a message to the server side to inform it of
- the new dimensions. No API function generates this event.</p></item>
+ the new dimensions. No API function generates this event.</p>
+ </desc>
+ </datatype>
- <tag><c><![CDATA[{exec, channel_id(),
- WantReply :: boolean(), Cmd :: string()}]]></c></tag>
- <item><p>This message requests that the server starts
- execution of the given command. This event is sent as a result of calling <seealso
- marker="ssh_connection#exec-4">ssh_connection:exec/4 </seealso>.
- </p></item>
- </taglist>
- </item>
- </taglist>
- </section>
+ <datatype_title>Signals (RFC 4254, section 6.9)</datatype_title>
+ <datatype>
+ <name name="signal_ch_msg"/>
+ <desc>
+ <p>A signal can be delivered to the remote process/service
+ using the following message. Some systems do not support
+ signals, in which case they are to ignore this message. There is
+ currently no function to generate this event as the signals
+ referred to are on OS-level and not something generated by an
+ Erlang program.</p>
+ </desc>
+ </datatype>
+
+
+ <datatype_title>Returning Exit Status (RFC 4254, section 6.10)</datatype_title>
+ <datatype>
+ <name name="exit_status_ch_msg"/>
+ <desc>
+ <p>When the command running at the other end terminates, the
+ following message can be sent to return the exit status of the
+ command. A zero <c>exit_status</c> usually means that the command
+ terminated successfully. This event is sent as a result of calling
+ <seealso marker="ssh_connection#exit_status-3">
+ ssh_connection:exit_status/3</seealso>.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="exit_signal_ch_msg"/>
+ <desc>
+ <p>A remote execution can terminate violently because of a signal.
+ Then this message can be received. For details on valid string
+ values, see <url href="https://tools.ietf.org/html/rfc4254#section-6.10">RFC 4254</url>
+ Section 6.10, which shows a special case of these signals.</p>
+ </desc>
+ </datatype>
+
+ </datatypes>
+
<funcs>
<func>
- <name since="">adjust_window(ConnectionRef, ChannelId, NumOfBytes) -> ok</name>
+ <name since="" name="adjust_window" arity="3"/>
<fsummary>Adjusts the SSH flow control window.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- <v>NumOfBytes = integer()</v>
- </type>
- <desc>
+ <desc>
<p>Adjusts the SSH flow control window. This is to be done by both the
client- and server-side channel processes.</p>
@@ -221,17 +269,12 @@
</func>
<func>
- <name since="">close(ConnectionRef, ChannelId) -> ok</name>
+ <name since="" name="close" arity="2"/>
<fsummary>Sends a close message on the channel <c>ChannelId</c>.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- </type>
<desc>
<p>A server- or client-channel process can choose to close their session by
sending a close event.
</p>
-
<note><p>This function is called by the <c>ssh_client_channel</c>
behavior when the channel is terminated, see <seealso
marker="ssh_client_channel"> ssh_client_channel(3)</seealso>. Thus, channels implemented
@@ -240,57 +283,61 @@
</func>
<func>
- <name since="">exec(ConnectionRef, ChannelId, Command, TimeOut) -> ssh_request_status() |
- {error, reason()}</name>
+ <name since="" name="exec" arity="4"/>
<fsummary>Requests that the server starts the execution of the given command.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- <v>Command = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Is to be called by a client-channel process to request that the server starts
executing the given command. The result is several messages according to the
following pattern. The last message is a channel close message, as the <c>exec</c>
request is a one-time execution that closes the channel when it is done.</p>
- <taglist>
- <tag><c>N x {ssh_cm, connection_ref(),
- {data, channel_id(), ssh_data_type_code(), Data :: binary()}}</c></tag>
+ <!--taglist>
+ <tag><c>N x {ssh_cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>, {data, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>, </c><seealso marker="#type-ssh_data_type_code">ssh_data_type_code()</seealso><c>, Data :: binary()}}</c></tag>
<item><p>The result of executing the command can be only one line
or thousands of lines depending on the command.</p></item>
- <tag><c>0 or 1 x {ssh_cm, connection_ref(), {eof, channel_id()}}</c></tag>
+ <tag><c>0 or 1 x {ssh_cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>, {eof, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>}}</c></tag>
<item><p>Indicates that no more data is to be sent.</p></item>
- <tag><c>0 or 1 x {ssh_cm,
- connection_ref(), {exit_signal,
- channel_id(), ExitSignal :: string(), ErrorMsg :: string(), LanguageString :: string()}}</c></tag>
+ <tag><c>0 or 1 x {ssh_cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>, {exit_signal, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>, ExitSignal :: string(), ErrorMsg :: string(), LanguageString :: string()}}</c></tag>
<item><p>Not all systems send signals. For details on valid string
values, see RFC 4254, Section 6.10</p></item>
- <tag><c>0 or 1 x {ssh_cm, connection_ref(), {exit_status,
- channel_id(), ExitStatus :: integer()}}</c></tag>
+ <tag><c>0 or 1 x {ssh_cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>, {exit_status, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>, ExitStatus :: integer()}}</c></tag>
<item><p>It is recommended by the SSH Connection Protocol to send this
message, but that is not always the case.</p></item>
- <tag><c>1 x {ssh_cm, connection_ref(),
- {closed, channel_id()}}</c></tag>
+ <tag><c>1 x {ssh_cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>, {closed, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>}}</c></tag>
<item><p>Indicates that the <c>ssh_client_channel</c> started for the
execution of the command has now been shut down.</p></item>
+ </taglist-->
+
+ <taglist>
+ <tag>N x <seealso marker="#type-data_ch_msg">data message(s)</seealso></tag>
+ <item><p>The result of executing the command can be only one line
+ or thousands of lines depending on the command.</p></item>
+
+ <tag>0 or 1 x <seealso marker="#type-eof_ch_msg">eof message</seealso></tag>
+ <item><p>Indicates that no more data is to be sent.</p></item>
+
+ <tag>0 or 1 x <seealso marker="#type-exit_signal_ch_msg">exit signal message</seealso></tag>
+ <item><p>Not all systems send signals. For details on valid string
+ values, see RFC 4254, Section 6.10</p></item>
+
+ <tag>0 or 1 x <seealso marker="#type-exit_status_ch_msg">exit status message</seealso></tag>
+ <item><p>It is recommended by the SSH Connection Protocol to send this
+ message, but that is not always the case.</p></item>
+
+ <tag>1 x <seealso marker="#type-closed_ch_msg">closed status message</seealso></tag>
+ <item><p>Indicates that the <c>ssh_client_channel</c> started for the
+ execution of the command has now been shut down.</p></item>
</taglist>
</desc>
</func>
<func>
- <name since="">exit_status(ConnectionRef, ChannelId, Status) -> ok</name>
+ <name since="" name="exit_status" arity="3"/>
<fsummary>Sends the exit status of a command to the client.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref() </v>
- <v>ChannelId = channel_id()</v>
- <v>Status = integer()</v>
- </type>
<desc>
<p>Is to be called by a server-channel process to send the exit status of a command
to the client.</p>
@@ -298,16 +345,10 @@
</func>
<func>
- <name since="OTP 17.5">ptty_alloc(ConnectionRef, ChannelId, Options) -></name>
- <name since="OTP 17.4">ptty_alloc(ConnectionRef, ChannelId, Options, Timeout) -> > ssh_request_status() |
- {error, reason()}</name>
+ <name since="OTP 17.5" name="ptty_alloc" arity="3"/>
+ <name since="OTP 17.4" name="ptty_alloc" arity="4"/>
<fsummary>Sends an SSH Connection Protocol <c>pty_req</c>,
to allocate a pseudo-terminal.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- <v>Options = proplists:proplist()</v>
- </type>
<desc>
<p>Sends an SSH Connection Protocol <c>pty_req</c>, to allocate a pseudo-terminal.
Is to be called by an SSH client process.</p>
@@ -339,14 +380,8 @@
</func>
<func>
- <name since="">reply_request(ConnectionRef, WantReply, Status, ChannelId) -> ok</name>
+ <name since="" name="reply_request" arity="4"/>
<fsummary>Sends status replies to requests that want such replies.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>WantReply = boolean()</v>
- <v>Status = ssh_request_status()</v>
- <v>ChannelId = channel_id()</v>
- </type>
<desc>
<p>Sends status replies to requests where the requester has
stated that it wants a status report, that is, <c>WantReply = true</c>.
@@ -361,14 +396,15 @@
<name since="">send(ConnectionRef, ChannelId, Data, Timeout) -></name>
<name since="">send(ConnectionRef, ChannelId, Type, Data) -></name>
<name since="">send(ConnectionRef, ChannelId, Type, Data, TimeOut) ->
- ok | {error, timeout} | {error, closed}</name>
+ ok | Error</name>
<fsummary>Sends channel data.</fsummary>
<type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
+ <v>ConnectionRef = <seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso></v>
+ <v>ChannelId = <seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso></v>
<v>Data = binary()</v>
- <v>Type = ssh_data_type_code()</v>
+ <v>Type = <seealso marker="#type-ssh_data_type_code">ssh_data_type_code()</seealso></v>
<v>Timeout = timeout()</v>
+ <v>Error = {error, <seealso marker="#type-reason">reason()</seealso>}</v>
</type>
<desc>
<p>Is to be called by client- and server-channel processes to send data to each other.
@@ -380,29 +416,17 @@
</func>
<func>
- <name since="">send_eof(ConnectionRef, ChannelId) -> ok | {error, closed}</name>
+ <name since="" name="send_eof" arity="2"/>
<fsummary>Sends EOF on channel <c>ChannelId</c>.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- </type>
<desc>
<p>Sends EOF on channel <c>ChannelId</c>.</p>
</desc>
</func>
<func>
- <name since="">session_channel(ConnectionRef, Timeout) -></name>
- <name since="">session_channel(ConnectionRef, InitialWindowSize,
- MaxPacketSize, Timeout) -> {ok, channel_id()} | {error, reason()}</name>
+ <name since="" name="session_channel" arity="2"/>
+ <name since="" name="session_channel" arity="4"/>
<fsummary>Opens a channel for an SSH session.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>InitialWindowSize = integer()</v>
- <v>MaxPacketSize = integer()</v>
- <v>Timeout = timeout()</v>
- <v>Reason = term()</v>
- </type>
<desc>
<p>Opens a channel for an SSH session. The channel id returned from this function
is the id used as input to the other functions in this module.</p>
@@ -410,17 +434,9 @@
</func>
<func>
- <name since="">setenv(ConnectionRef, ChannelId, Var, Value, TimeOut) -> ssh_request_status() |
- {error, reason()}</name>
+ <name since="" name="setenv" arity="5"/>
<fsummary>Environment variables can be passed to the
shell/command to be started later.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- <v>Var = string()</v>
- <v>Value = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Environment variables can be passed before starting the
shell/command. Is to be called by a client channel processes.</p>
@@ -428,14 +444,9 @@
</func>
<func>
- <name since="">shell(ConnectionRef, ChannelId) -> ok | failure | {error, closed}
- </name>
+ <name since="" name="shell" arity="2"/>
<fsummary>Requests that the user default shell (typically defined in
/etc/passwd in Unix systems) is to be executed at the server end.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- </type>
<desc>
<p>Is to be called by a client channel process to request that the user default
shell (typically defined in /etc/passwd in Unix systems) is executed
@@ -448,15 +459,8 @@
</func>
<func>
- <name since="">subsystem(ConnectionRef, ChannelId, Subsystem, Timeout) -> ssh_request_status() |
- {error, reason()}</name>
+ <name since="" name="subsystem" arity="4"/>
<fsummary>Requests to execute a predefined subsystem on the server.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- <v>Subsystem = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Is to be called by a client-channel process for requesting to execute a predefined
subsystem on the server.
diff --git a/lib/ssh/doc/src/ssh_server_channel.xml b/lib/ssh/doc/src/ssh_server_channel.xml
index a4e18bbfbf..87c745c9fb 100644
--- a/lib/ssh/doc/src/ssh_server_channel.xml
+++ b/lib/ssh/doc/src/ssh_server_channel.xml
@@ -112,7 +112,7 @@
function and all channels are to handle the following message.</p>
<taglist>
- <tag><c>{ssh_channel_up, ssh:channel_id(), ssh:connection_ref()}</c></tag>
+ <tag><c>{ssh_channel_up, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>}</c></tag>
<item><p>This is the first message that the channel receives.
This is especially useful if the
server wants to send a message to the client without first
@@ -129,21 +129,21 @@
ChannelId, State}</name>
<fsummary>Handles <c>ssh</c> connection protocol messages.</fsummary>
<type>
- <v>Msg = ssh_connection:event()</v>
+ <v>Msg = <seealso marker="ssh_connection#type-event">ssh_connection:event()</seealso></v>
<v>ChannelId = <seealso marker="ssh#type-channel_id">ssh:channel_id()</seealso></v>
<v>State = term()</v>
</type>
<desc>
<p>Handles SSH Connection Protocol messages that may need
service-specific attention. For details,
- see <seealso marker="ssh_connection"> ssh_connection:event()</seealso>.
+ see <seealso marker="ssh_connection#type-event">ssh_connection:event()</seealso>.
</p>
<p>The following message is taken care of by the
<c>ssh_server_channel</c> behavior.</p>
<taglist>
- <tag><c>{closed, ssh:channel_id()}</c></tag>
+ <tag><c>{closed, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>}</c></tag>
<item><p>The channel behavior sends a close message to the
other side, if such a message has not already been sent.
Then it terminates the channel with reason <c>normal</c>.</p></item>
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index c89092798d..f9f1e0953b 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -37,60 +37,112 @@
SSH.</p>
</description>
- <section>
- <title>DATA TYPES</title>
- <p>Type definitions that are used more than once in this module,
- or abstractions to indicate the intended use of the data type, or both:
- </p>
-
- <taglist>
- <tag><c>reason()</c></tag>
- <item>
- <p>= <c>atom() | string() | tuple() </c>A description of the reason why an operation failed.</p>
- <p>
- The <c>atom()</c> value is formed from the sftp error codes in the protocol-level responses as defined in
- <url href="https://tools.ietf.org/id/draft-ietf-secsh-filexfer-13.txt">draft-ietf-secsh-filexfer-13.txt</url>
- section 9.1.
- </p>
- <p>
+ <datatypes>
+ <datatype>
+ <name name="sftp_option"/>
+ <desc>
+ </desc>
+ </datatype>
+
+ <datatype_title>Error cause</datatype_title>
+ <datatype>
+ <name name="reason"/>
+ <desc>
+ <p>A description of the reason why an operation failed.</p>
+ <p>The <c>atom()</c> value is formed from the sftp error codes in the protocol-level responses as defined in
+ <url href="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-49">draft-ietf-secsh-filexfer-13</url>
+ section 9.1.
The codes are named as <c>SSH_FX_*</c> which are transformed into lowercase of the star-part.
E.g. the error code <c>SSH_FX_NO_SUCH_FILE</c>
will cause the <c>reason()</c> to be <c>no_such_file</c>.
</p>
<p>The <c>string()</c> reason is the error information from the server in case of an exit-signal. If that information is empty, the reason is the exit signal name.
</p>
- <p>The <c>tuple()</c> reason are other errors like the <c>{exit_status,integer()}</c> if the exit status is not 0.
+ <p>The <c>tuple()</c> reason are other errors like for example <c>{exit_status,1}</c>.
</p>
- </item>
+ </desc>
+ </datatype>
- <tag><c>connection_ref() =</c></tag>
- <item><p><c>opaque()</c> - as returned by
- <seealso marker="ssh#connect-3"><c>ssh:connect/3</c></seealso></p></item>
+ <datatype_title>Crypto operations for open_tar</datatype_title>
+ <datatype>
+ <name name="tar_crypto_spec"/>
+ <name name="encrypt_spec"/>
+ <name name="decrypt_spec"/>
+ <desc>
+ <p>Specifies the encryption or decryption applied to tar files when using
+ <seealso marker="#open_tar/3">open_tar/3</seealso> or
+ <seealso marker="#open_tar/4">open_tar/4</seealso>.
+ </p>
+ <p>The encryption or decryption is applied to the generated stream of
+ bytes prior to sending the resulting stream to the SFTP server.
+ </p>
+ <p>For code examples see Section
+ <seealso marker="using_ssh#example-with-encryption">Example with encryption</seealso>
+ in the ssh Users Guide.
+ </p>
+ </desc>
+ </datatype>
- <tag><c>timeout()</c></tag>
- <item><p>= <c>infinity | integer()</c> in milliseconds. Default infinity.</p></item>
- </taglist>
- </section>
+ <datatype>
+ <name name="init_fun"/>
+ <name name="chunk_size"/>
+ <name name="crypto_state"/>
+ <desc>
+ <p>The <c>init_fun()</c> in the
+ <seealso marker="#type-tar_crypto_spec">tar_crypto_spec</seealso>
+ is applied once prior to any other <c>crypto</c>
+ operation. The intention is that this function initiates the encryption or
+ decryption for example by calling
+ <seealso marker="crypto:crypto#crypto_init/4">crypto:crypto_init/4</seealso>
+ or similar. The <c>crypto_state()</c> is the state such a function may return.
+ </p>
+ <p>If the selected cipher needs to have the input data partioned into
+ blocks of a certain size, the <c>init_fun()</c> should return the second
+ form of return value with the <c>chunk_size()</c> set to the block size.
+ If the <c>chunk_size()</c> is <c>undefined</c>, the size of the <c>PlainBin</c>s varies,
+ because this is intended for stream crypto, whereas a fixed <c>chunk_size()</c> is intended for block crypto.
+ A <c>chunk_size()</c> can be changed in the return from the <c>crypto_fun()</c>.
+ The value can be changed between <c>pos_integer()</c> and <c>undefined</c>.
+ </p>
+ </desc>
+ </datatype>
- <section>
- <title>Time-outs</title>
- <p>If the request functions for the SFTP channel return <c>{error, timeout}</c>,
- no answer was received from the server within the expected time.</p>
- <p>The request may have reached the server and may have been performed.
- However, no answer was received from the server within the expected time.</p>
- </section>
+ <datatype>
+ <name name="crypto_fun"/>
+ <name name="crypto_result"/>
+ <desc>
+ <p>The initial <c>crypto_state()</c> returned from the
+ <seealso marker="#type-init_fun">init_fun()</seealso>
+ is folded into repeated applications of the <c>crypto_fun()</c> in the
+ <seealso marker="#type-tar_crypto_spec">tar_crypto_spec</seealso>.
+ The binary returned from that fun is sent to the remote SFTP server and
+ the new <c>crypto_state()</c> is used in the next call of the
+ <c>crypto_fun()</c>.
+ </p>
+ <p>If the <c>crypto_fun()</c> reurns a <c>chunk_size()</c>, that value
+ is as block size for further blocks in calls to <c>crypto_fun()</c>.
+ </p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="final_fun"/>
+ <desc>
+ <p>If doing encryption,
+ the <c>final_fun()</c> in the
+ <seealso marker="#type-tar_crypto_spec">tar_crypto_spec</seealso>
+ is applied to the last piece of data.
+ The <c>final_fun()</c> is responsible for padding (if needed) and
+ encryption of that last piece.
+ </p>
+ </desc>
+ </datatype>
+ </datatypes>
<funcs>
<func>
- <name since="">apread(ChannelPid, Handle, Position, Len) -> {async, N} | {error, reason()}</name>
+ <name name="apread" arity="4" since=""/>
<fsummary>Reads asynchronously from an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Len = integer()</v>
- <v>N = term()</v>
- </type>
<desc><p>The <c><![CDATA[apread/4]]></c> function reads from a specified position,
combining the <seealso marker="#position-3"><c>position/3</c></seealso> and
<seealso marker="#aread-3"><c>aread/3</c></seealso> functions.</p>
@@ -98,17 +150,8 @@
</func>
<func>
- <name since="">apwrite(ChannelPid, Handle, Position, Data) -> {async, N} | {error, reason()}</name>
+ <name name="apwrite" arity="4" since=""/>
<fsummary>Writes asynchronously to an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Len = integer()</v>
- <v>Data = binary()</v>
- <v>Timeout = timeout()</v>
- <v>N = term()</v>
- </type>
<desc><p>The <c><![CDATA[apwrite/4]]></c> function writes to a specified position,
combining the <seealso marker="#position-3"><c>position/3</c></seealso> and
<seealso marker="#awrite-3"><c>awrite/3</c></seealso> functions.</p>
@@ -116,15 +159,8 @@
</func>
<func>
- <name since="">aread(ChannelPid, Handle, Len) -> {async, N} | {error, reason()}</name>
+ <name name="aread" arity="3" since=""/>
<fsummary>Reads asynchronously from an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Len = integer()</v>
- <v>N = term()</v>
- </type>
<desc>
<p>Reads from an open file, without waiting for the result. If the
handle is valid, the function returns <c><![CDATA[{async, N}]]></c>, where <c>N</c>
@@ -137,16 +173,8 @@
</func>
<func>
- <name since="">awrite(ChannelPid, Handle, Data) -> {async, N} | {error, reason()}</name>
+ <name name="awrite" arity="3" since=""/>
<fsummary>Writes asynchronously to an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Len = integer()</v>
- <v>Data = binary()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Writes to an open file, without waiting for the result. If the
handle is valid, the function returns <c><![CDATA[{async, N}]]></c>, where <c>N</c>
@@ -159,28 +187,18 @@
</func>
<func>
- <name since="">close(ChannelPid, Handle) -></name>
- <name since="">close(ChannelPid, Handle, Timeout) -> ok | {error, reason()}</name>
+ <name name="close" arity="2" since=""/>
+ <name name="close" arity="3" since=""/>
<fsummary>Closes an open handle.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Closes a handle to an open file or directory on the server.</p>
</desc>
</func>
<func>
- <name since="">delete(ChannelPid, Name) -></name>
- <name since="">delete(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name>
+ <name name="delete" arity="2" since=""/>
+ <name name="delete" arity="3" since=""/>
<fsummary>Deletes a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Deletes the file specified by <c><![CDATA[Name]]></c>.
</p>
@@ -188,14 +206,9 @@
</func>
<func>
- <name since="">del_dir(ChannelPid, Name) -></name>
- <name since="">del_dir(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name>
+ <name name="del_dir" arity="2" since=""/>
+ <name name="del_dir" arity="3" since=""/>
<fsummary>Deletes an empty directory.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Deletes a directory specified by <c><![CDATA[Name]]></c>.
The directory must be empty before it can be successfully deleted.
@@ -204,16 +217,9 @@
</func>
<func>
- <name since="">list_dir(ChannelPid, Path) -></name>
- <name since="">list_dir(ChannelPid, Path, Timeout) -> {ok, Filenames} | {error, reason()}</name>
+ <name name="list_dir" arity="2" since=""/>
+ <name name="list_dir" arity="3" since=""/>
<fsummary>Lists the directory.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Path = string()</v>
- <v>Filenames = [Filename]</v>
- <v>Filename = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Lists the given directory on the server, returning the
filenames as a list of strings.</p>
@@ -221,14 +227,9 @@
</func>
<func>
- <name since="">make_dir(ChannelPid, Name) -></name>
- <name since="">make_dir(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name>
+ <name name="make_dir" arity="2" since=""/>
+ <name name="make_dir" arity="3" since=""/>
<fsummary>Creates a directory.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Creates a directory specified by <c><![CDATA[Name]]></c>. <c><![CDATA[Name]]></c>
must be a full path to a new directory. The directory can only be
@@ -237,14 +238,9 @@
</func>
<func>
- <name since="">make_symlink(ChannelPid, Name, Target) -></name>
- <name since="">make_symlink(ChannelPid, Name, Target, Timeout) -> ok | {error, reason()}</name>
+ <name name="make_symlink" arity="3" since=""/>
+ <name name="make_symlink" arity="4" since=""/>
<fsummary>Creates a symbolic link.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Target = string()</v>
- </type>
<desc>
<p>Creates a symbolic link pointing to <c><![CDATA[Target]]></c> with the
name <c><![CDATA[Name]]></c>.
@@ -252,32 +248,19 @@
</desc>
</func>
- <func>
- <name since="">open(ChannelPid, File, Mode) -></name>
- <name since="">open(ChannelPid, File, Mode, Timeout) -> {ok, Handle} | {error, reason()}</name>
+ <func>
+ <name name="open" arity="3" since=""/>
+ <name name="open" arity="4" since=""/>
<fsummary>Opens a file and returns a handle.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>File = string()</v>
- <v>Mode = [Modeflag]</v>
- <v>Modeflag = read | write | creat | trunc | append | binary</v>
- <v>Timeout = timeout()</v>
- <v>Handle = term()</v>
- </type>
<desc>
<p>Opens a file on the server and returns a handle, which
can be used for reading or writing.</p>
</desc>
</func>
<func>
- <name since="">opendir(ChannelPid, Path) -></name>
- <name since="">opendir(ChannelPid, Path, Timeout) -> {ok, Handle} | {error, reason()}</name>
+ <name name="opendir" arity="2" since=""/>
+ <name name="opendir" arity="3" since=""/>
<fsummary>Opens a directory and returns a handle.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Path = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Opens a handle to a directory on the server. The handle
can be used for reading directory contents.</p>
@@ -285,72 +268,36 @@
</func>
<func>
- <name since="OTP 17.4">open_tar(ChannelPid, Path, Mode) -></name>
- <name since="OTP 17.4">open_tar(ChannelPid, Path, Mode, Timeout) -> {ok, Handle} | {error, reason()}</name>
+ <name name="open_tar" arity="3" since="OTP 17.4"/>
+ <name name="open_tar" arity="4" since="OTP 17.4"/>
<fsummary>Opens a tar file on the server to which <c>ChannelPid</c>
is connected and returns a handle.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Path = string()</v>
- <v>Mode = [read] | [write] | [read,EncryptOpt] | [write,DecryptOpt]</v>
- <v>EncryptOpt = {crypto,{InitFun,EncryptFun,CloseFun}}</v>
- <v>DecryptOpt = {crypto,{InitFun,DecryptFun}}</v>
- <v>InitFun = (fun() -> {ok,CryptoState}) | (fun() -> {ok,CryptoState,ChunkSize})</v>
- <v>CryptoState = any()</v>
- <v>ChunkSize = undefined | pos_integer()</v>
- <v>EncryptFun = (fun(PlainBin,CryptoState) -> EncryptResult)</v>
- <v>EncryptResult = {ok,EncryptedBin,CryptoState} | {ok,EncryptedBin,CryptoState,ChunkSize}</v>
- <v>PlainBin = binary()</v>
- <v>EncryptedBin = binary()</v>
- <v>DecryptFun = (fun(EncryptedBin,CryptoState) -> DecryptResult)</v>
- <v>DecryptResult = {ok,PlainBin,CryptoState} | {ok,PlainBin,CryptoState,ChunkSize}</v>
- <v>CloseFun = (fun(PlainBin,CryptoState) -> {ok,EncryptedBin})</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Opens a handle to a tar file on the server, associated with <c>ChannelPid</c>.
- The handle can be used for remote tar creation and extraction, as defined by the
- <seealso marker="stdlib:erl_tar#init-3">erl_tar:init/3</seealso> function.
- </p>
-
- <p> For code exampel see Section
- <seealso marker="using_ssh">SFTP Client with TAR Compression and Encryption</seealso> in
- the ssh Users Guide. </p>
-
- <p>The <c>crypto</c> mode option is applied to the generated stream of bytes prior to sending
- them to the SFTP server. This is intended for encryption but can be used for other
- purposes.
+ The handle can be used for remote tar creation and extraction. The actual writing
+ and reading is performed by calls to
+ <seealso marker="stdlib:erl_tar#add-3">erl_tar:add/3,4</seealso> and
+ <seealso marker="stdlib:erl_tar#extract-2">erl_tar:extract/2</seealso>.
+ Note: The
+ <seealso marker="stdlib:erl_tar#init-3">erl_tar:init/3</seealso> function should not
+ be called, that one is called by this open_tar function.
</p>
- <p>The <c>InitFun</c> is applied once
- prior to any other <c>crypto</c> operation. The returned <c>CryptoState</c> is then folded into
- repeated applications of the <c>EncryptFun</c> or <c>DecryptFun</c>. The binary returned
- from those funs are sent further to the remote SFTP server. Finally, if doing encryption,
- the <c>CloseFun</c> is applied to the last piece of data. The <c>CloseFun</c> is
- responsible for padding (if needed) and encryption of that last piece.
+ <p>For code examples see Section
+ <seealso marker="using_ssh#sftp-client-with-tar-compression">SFTP Client with TAR Compression</seealso>
+ in the ssh Users Guide.
</p>
- <p>The <c>ChunkSize</c> defines the size of the <c>PlainBin</c>s that <c>EncodeFun</c> is applied
- to. If the <c>ChunkSize</c> is <c>undefined</c>, the size of the <c>PlainBin</c>s varies,
- because this is intended for stream crypto, whereas a fixed <c>ChunkSize</c> is intended for block crypto.
- <c>ChunkSize</c>s can be changed in the return from the <c>EncryptFun</c> or
- <c>DecryptFun</c>. The value can be changed between <c>pos_integer()</c> and <c>undefined</c>.
+ <p>The <c>crypto</c> mode option is explained in the data types section above, see
+ <seealso marker="#Crypto operations for open_tar">Crypto operations for open_tar</seealso>.
+ Encryption is assumed if the <c>Mode</c> contains <c>write</c>, and
+ decryption if the <c>Mode</c> contains <c>read</c>.
</p>
-
</desc>
</func>
<func>
- <name since="">position(ChannelPid, Handle, Location) -></name>
- <name since="">position(ChannelPid, Handle, Location, Timeout) -> {ok, NewPosition | {error, reason()}</name>
+ <name name="position" arity="3" since=""/>
+ <name name="position" arity="4" since=""/>
<fsummary>Sets the file position of a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Location = Offset
- | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof</v>
- <v>Offset = integer()</v>
- <v>Timeout = timeout()</v>
- <v>NewPosition = integer()</v>
- </type>
<desc>
<p>Sets the file position of the file referenced by <c><![CDATA[Handle]]></c>.
Returns <c><![CDATA[{ok, NewPosition}]]></c> (as an absolute offset) if
@@ -384,17 +331,9 @@
</func>
<func>
- <name since="">pread(ChannelPid, Handle, Position, Len) -></name>
- <name since="">pread(ChannelPid, Handle, Position, Len, Timeout) -> {ok, Data} | eof | {error, reason()}</name>
+ <name name="pread" arity="4" since=""/>
+ <name name="pread" arity="5" since=""/>
<fsummary>Reads from an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Len = integer()</v>
- <v>Timeout = timeout()</v>
- <v>Data = string() | binary()</v>
- </type>
<desc><p>The <c><![CDATA[pread/3,4]]></c> function reads from a specified position,
combining the <seealso marker="#position-3"><c>position/3</c></seealso> and
<seealso marker="#read-3"><c>read/3,4</c></seealso> functions.</p>
@@ -402,16 +341,9 @@
</func>
<func>
- <name since="">pwrite(ChannelPid, Handle, Position, Data) -> ok</name>
- <name since="">pwrite(ChannelPid, Handle, Position, Data, Timeout) -> ok | {error, reason()}</name>
+ <name name="pwrite" arity="4" since=""/>
+ <name name="pwrite" arity="5" since=""/>
<fsummary>Writes to an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Data = iolist()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc><p>The <c><![CDATA[pwrite/3,4]]></c> function writes to a specified position,
combining the <seealso marker="#position-3"><c>position/3</c></seealso> and
<seealso marker="#write-3"><c>write/3,4</c></seealso> functions.</p>
@@ -419,16 +351,9 @@
</func>
<func>
- <name since="">read(ChannelPid, Handle, Len) -></name>
- <name since="">read(ChannelPid, Handle, Len, Timeout) -> {ok, Data} | eof | {error, reason()}</name>
+ <name name="read" arity="3" since=""/>
+ <name name="read" arity="4" since=""/>
<fsummary>Reads from an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Len = integer()</v>
- <v>Timeout = timeout()</v>
- <v>Data = string() | binary()</v>
- </type>
<desc>
<p>Reads <c><![CDATA[Len]]></c> bytes from the file referenced by
<c><![CDATA[Handle]]></c>. Returns <c><![CDATA[{ok, Data}]]></c>, <c><![CDATA[eof]]></c>, or
@@ -440,32 +365,19 @@
</desc>
</func>
- <func>
- <name since="">read_file(ChannelPid, File) -></name>
- <name since="">read_file(ChannelPid, File, Timeout) -> {ok, Data} | {error, reason()}</name>
+ <func>
+ <name name="read_file" arity="2" since=""/>
+ <name name="read_file" arity="3" since=""/>
<fsummary>Reads a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>File = string()</v>
- <v>Data = binary()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Reads a file from the server, and returns the data in a binary.</p>
</desc>
</func>
- <func>
- <name since="">read_file_info(ChannelPid, Name) -></name>
- <name since="">read_file_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, reason()}</name>
+ <func>
+ <name name="read_file_info" arity="2" since=""/>
+ <name name="read_file_info" arity="3" since=""/>
<fsummary>Gets information about a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Handle = term()</v>
- <v>Timeout = timeout()</v>
- <v>FileInfo = record()</v>
- </type>
<desc>
<p>Returns a <c><![CDATA[file_info]]></c> record from the file system object specified by
<c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>. See
@@ -474,38 +386,26 @@
</p>
<p>
Depending on the underlying OS:es links might be followed and info on the final file, directory
- etc is returned. See <seealso marker="#read_link_info-2">ssh_sftp::read_link_info/2</seealso>
+ etc is returned. See <seealso marker="#read_link_info-2">read_link_info/2</seealso>
on how to get information on links instead.
</p>
</desc>
</func>
<func>
- <name since="">read_link(ChannelPid, Name) -></name>
- <name since="">read_link(ChannelPid, Name, Timeout) -> {ok, Target} | {error, reason()}</name>
+ <name name="read_link" arity="2" since=""/>
+ <name name="read_link" arity="3" since=""/>
<fsummary>Reads symbolic link.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Target = string()</v>
- </type>
<desc>
<p>Reads the link target from the symbolic link specified by <c><![CDATA[name]]></c>.
</p>
</desc>
</func>
- <func>
- <name since="">read_link_info(ChannelPid, Name) -> {ok, FileInfo} | {error, reason()}</name>
- <name since="">read_link_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, reason()}</name>
+ <func>
+ <name since="" name="read_link_info" arity="2"/>
+ <name since="" name="read_link_info" arity="3"/>
<fsummary>Gets information about a symbolic link.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Handle = term()</v>
- <v>Timeout = timeout()</v>
- <v>FileInfo = record()</v>
- </type>
<desc>
<p>Returns a <c><![CDATA[file_info]]></c> record from the symbolic
link specified by <c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>.
@@ -517,15 +417,9 @@
</func>
<func>
- <name since="">rename(ChannelPid, OldName, NewName) -> </name>
- <name since="">rename(ChannelPid, OldName, NewName, Timeout) -> ok | {error, reason()}</name>
+ <name since="" name="rename" arity="3"/>
+ <name since="" name="rename" arity="4"/>
<fsummary>Renames a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>OldName = string()</v>
- <v>NewName = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Renames a file named <c><![CDATA[OldName]]></c> and gives it the name
<c><![CDATA[NewName]]></c>.
@@ -535,25 +429,27 @@
<func>
<name since="">start_channel(ConnectionRef) -></name>
- <name since="">start_channel(ConnectionRef, Options) ->
- {ok, Pid} | {error, reason()|term()}</name>
+ <name since="">start_channel(ConnectionRef, SftpOptions) ->
+ {ok, ChannelPid} | Error</name>
+ <name since="">start_channel(Host) -></name>
<name since="">start_channel(Host, Options) -></name>
- <name since="">start_channel(Host, Port, Options) ->
- {ok, Pid, ConnectionRef} | {error, reason()|term()}</name>
-
+ <name since="">start_channel(Host, Port, Options) -></name>
<name since="">start_channel(TcpSocket) -></name>
<name since="">start_channel(TcpSocket, Options) ->
- {ok, Pid, ConnectionRef} | {error, reason()|term()}</name>
+ {ok, ChannelPid, ConnectionRef} | Error</name>
<fsummary>Starts an SFTP client.</fsummary>
<type>
- <v>Host = string()</v>
- <v>ConnectionRef = connection_ref()</v>
- <v>Port = integer()</v>
- <v>TcpSocket = port()</v>
- <d>The socket is supposed to be from <seealso marker="kernel:gen_tcp#connect-3">gen_tcp:connect</seealso> or <seealso marker="kernel:gen_tcp#accept-1">gen_tcp:accept</seealso> with option <c>{active,false}</c></d>
- <v>Options = [{Option, Value}]</v>
+ <v>Host = <seealso marker="ssh:ssh#type-host">ssh:host()</seealso></v>
+ <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v>
+ <v>TcpSocket = <seealso marker="ssh:ssh#type-open_socket">ssh:open_socket()</seealso></v>
+ <v>Options = [ <seealso marker="#type-sftp_option">sftp_option()</seealso>
+ | <seealso marker="ssh:ssh#type-client_option">ssh:client_option()</seealso> ]</v>
+ <v>SftpOptions = [ <seealso marker="#type-sftp_option">sftp_option()</seealso> ]</v>
+ <v>ChannelPid = pid()</v>
+ <v>ConnectionRef = <seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso></v>
+ <v>Error = {error, <seealso marker="#type-reason">reason()</seealso>}</v>
</type>
<desc>
<p>If no connection reference is provided, a connection is set
@@ -594,11 +490,8 @@
</func>
<func>
- <name since="">stop_channel(ChannelPid) -> ok</name>
+ <name since="" name="stop_channel" arity="1"/>
<fsummary>Stops the SFTP client channel.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- </type>
<desc>
<p>Stops an SFTP channel. Does not close the SSH connection.
Use <seealso marker="ssh#close-1">ssh:close/1</seealso> to close it.</p>
@@ -606,16 +499,9 @@
</func>
<func>
- <name since="">write(ChannelPid, Handle, Data) -></name>
- <name since="">write(ChannelPid, Handle, Data, Timeout) -> ok | {error, reason()}</name>
+ <name since="" name="write" arity="3"/>
+ <name since="" name="write" arity="4"/>
<fsummary>Writes to an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Data = iolist()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Writes <c><![CDATA[data]]></c> to the file referenced by <c><![CDATA[Handle]]></c>.
The file is to be opened with <c><![CDATA[write]]></c> or <c><![CDATA[append]]></c>
@@ -625,15 +511,9 @@
</func>
<func>
- <name since="">write_file(ChannelPid, File, Iolist) -></name>
- <name since="">write_file(ChannelPid, File, Iolist, Timeout) -> ok | {error, reason()}</name>
+ <name since="" name="write_file" arity="3"/>
+ <name since="" name="write_file" arity="4"/>
<fsummary>Writes a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>File = string()</v>
- <v>Iolist = iolist()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Writes a file to the server. The file is created if it does not exist
but overwritten if it exists.</p>
@@ -641,15 +521,9 @@
</func>
<func>
- <name since="">write_file_info(ChannelPid, Name, Info) -></name>
- <name since="">write_file_info(ChannelPid, Name, Info, Timeout) -> ok | {error, reason()}</name>
+ <name since="" name="write_file_info" arity="3"/>
+ <name since="" name="write_file_info" arity="4"/>
<fsummary>Writes information for a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Info = record()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Writes file information from a <c><![CDATA[file_info]]></c> record to the
file specified by <c><![CDATA[Name]]></c>. See
diff --git a/lib/ssh/doc/src/ssh_sftpd.xml b/lib/ssh/doc/src/ssh_sftpd.xml
index ee72784add..0d7b340399 100644
--- a/lib/ssh/doc/src/ssh_sftpd.xml
+++ b/lib/ssh/doc/src/ssh_sftpd.xml
@@ -35,36 +35,23 @@
<p>Specifies a channel process to handle an SFTP subsystem.</p>
</description>
- <section>
- <title>DATA TYPES</title>
- <taglist>
- <tag><c>subsystem_spec() =</c></tag>
- <item><p><c>{subsystem_name(), {channel_callback(), channel_init_args()}}</c></p></item>
- <tag><c>subsystem_name() =</c></tag>
- <item><p><c>"sftp"</c></p></item>
- <tag><c>channel_callback() =</c></tag>
- <item><p><c>atom()</c> - Name of the Erlang module implementing the subsystem using the
- <seealso marker="ssh_server_channel">ssh_server_channel</seealso> (replaces ssh_daemon_channel) behaviour.</p></item>
- <tag><c>channel_init_args() =</c></tag>
- <item><p><c>list()</c> - The one given as argument to function <c>subsystem_spec/1</c>.</p></item>
- </taglist>
- </section>
<funcs>
<func>
- <name since="">subsystem_spec(Options) -> subsystem_spec()</name>
+ <name name="subsystem_spec" arity="1" since=""/>
<fsummary>Returns the subsystem specification that allows an SSH daemon to handle the subsystem "sftp".</fsummary>
- <type>
- <v>Options = [{Option, Value}]</v>
- </type>
<desc>
<p>Is to be used together with <c>ssh:daemon/[1,2,3]</c></p>
+ <p>The <c>Name</c> is <c>"sftp"</c> and
+ <c>CbMod</c> is the name of the Erlang module implementing the subsystem using the
+ <seealso marker="ssh_server_channel">ssh_server_channel</seealso> (replaces ssh_daemon_channel) behaviour.
+ </p>
<p>Options:</p>
<taglist>
- <tag><c><![CDATA[{cwd, String}]]></c></tag>
+ <tag><c>cwd</c></tag>
<item>
<p>Sets the initial current working directory for the server.</p>
</item>
- <tag><c><![CDATA[{file_handler, CallbackModule}]]></c></tag>
+ <tag><c>file_handler</c></tag>
<item>
<p>Determines which module to call for accessing
the file server. The default value is <c>ssh_sftpd_file</c>, which uses the
@@ -72,13 +59,13 @@
APIs to access the standard OTP file server. This option can be used to plug in
other file servers.</p>
</item>
- <tag><c><![CDATA[{max_files, Integer}]]></c></tag>
+ <tag><c>max_files</c></tag>
<item>
<p>The default value is <c>0</c>, which means that there is no upper limit.
If supplied, the number of filenames returned to the SFTP client per <c>READDIR</c>
request is limited to at most the given value.</p>
</item>
- <tag><c><![CDATA[{root, String}]]></c></tag>
+ <tag><c>root</c></tag>
<item>
<p>Sets the SFTP root directory. Then the user cannot see any files
above this root. If, for example, the root directory is set to <c>/tmp</c>,
@@ -86,7 +73,7 @@
<c>cd /etc</c>, the user moves to <c>/tmp/etc</c>.
</p>
</item>
- <tag><c><![CDATA[{sftpd_vsn, integer()}]]></c></tag>
+ <tag><c>sftpd_vsn</c></tag>
<item>
<p>Sets the SFTP version to use. Defaults to 5. Version 6 is under
development and limited.</p>
diff --git a/lib/ssh/doc/src/using_ssh.xml b/lib/ssh/doc/src/using_ssh.xml
index 4455d5ecc5..5c56dee81d 100644
--- a/lib/ssh/doc/src/using_ssh.xml
+++ b/lib/ssh/doc/src/using_ssh.xml
@@ -232,9 +232,10 @@
</section>
<section>
- <title>SFTP Client with TAR Compression and Encryption</title>
-
- <p>Example of writing and then reading a tar file follows:</p>
+ <title>SFTP Client with TAR Compression</title>
+ <section>
+ <title>Basic example</title>
+ <p>This is an example of writing and then reading a tar file:</p>
<code type="erl">
{ok,HandleWrite} = ssh_sftp:open_tar(ChannelPid, ?tar_file_name, [write]),
ok = erl_tar:add(HandleWrite, .... ),
@@ -248,8 +249,12 @@
{ok,NameValueList} = erl_tar:extract(HandleRead,[memory]),
ok = erl_tar:close(HandleRead),
</code>
+ </section>
- <p>The previous write and read example can be extended with encryption and decryption as follows:</p>
+ <section>
+ <title>Example with encryption</title>
+ <p>The previous <seealso marker="using_ssh#basic-example">Basic example</seealso>
+ can be extended with encryption and decryption as follows:</p>
<code type="erl">
%% First three parameters depending on which crypto type we select:
Key = &lt;&lt;"This is a 256 bit key. abcdefghi">>,
@@ -297,6 +302,7 @@ Cr = {InitFun,DecryptFun},
ok = erl_tar:close(HandleRead),
</code>
</section>
+ </section>
<section>
<marker id="usersguide_creating_a_subsystem"/>
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index 6d64a45112..9627b70eeb 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -99,7 +99,7 @@ APP_TARGET= $(EBIN)/$(APP_FILE)
APPUP_SRC= $(APPUP_FILE).src
APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
-INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl ssh.hrl ssh_userauth.hrl ssh_xfer.hrl
+INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl ssh.hrl ssh_xfer.hrl
# ----------------------------------------------------
# FLAGS
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 410061cded..2193c14611 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -44,10 +44,10 @@
{env, []},
{mod, {ssh_app, []}},
{runtime_dependencies, [
- "crypto-4.2",
- "erts-6.0",
- "kernel-3.0",
- "public_key-1.5.2",
- "stdlib-3.3"
+ "crypto-4.5",
+ "erts-9.0",
+ "kernel-5.3",
+ "public_key-1.6.1",
+ "stdlib-3.4.1"
]}]}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index ff5aee14d7..32f10c797d 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -66,6 +66,8 @@
cipher_alg/0,
mac_alg/0,
compression_alg/0,
+ host/0,
+ open_socket/0,
ip_port/0
]).
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index 923e9309f4..a991f72cf2 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -68,6 +68,25 @@
-define(string(X), ?string_utf8(X)).
-define(binary(X), << ?STRING(X) >>).
+-define('2bin'(X), (if is_binary(X) -> X;
+ is_list(X) -> list_to_binary(X);
+ X==undefined -> <<>>
+ end) ).
+
+%% encoding macros
+-define('E...'(X), ?'2bin'(X)/binary ).
+-define(Eboolean(X), ?BOOLEAN(case X of
+ true -> ?TRUE;
+ false -> ?FALSE
+ end) ).
+-define(Ebyte(X), ?BYTE(X) ).
+-define(Euint32(X), ?UINT32(X) ).
+-define(Estring(X), ?STRING(?'2bin'(X)) ).
+-define(Estring_utf8(X), ?string_utf8(X)/binary ).
+-define(Ename_list(X), ?STRING(ssh_bits:name_list(X)) ).
+-define(Empint(X), (ssh_bits:mpint(X))/binary ).
+-define(Ebinary(X), ?STRING(X) ).
+
%% Cipher details
-define(SSH_CIPHER_NONE, 0).
-define(SSH_CIPHER_3DES, 3).
@@ -293,7 +312,8 @@
| gen_tcp:listen_option()
| ?COMMON_OPTION .
--type subsystem_daemon_option() :: {subsystems, subsystem_spec()}.
+-type subsystem_daemon_option() :: {subsystems, subsystem_specs()}.
+-type subsystem_specs() :: [ subsystem_spec() ].
-type shell_daemon_option() :: {shell, mod_fun_args() | 'shell_fun/1'() | 'shell_fun/2'() }.
-type 'shell_fun/1'() :: fun((User::string()) -> pid()) .
@@ -396,11 +416,13 @@
recv_mac_size = 0,
encrypt = none, %% encrypt algorithm
+ encrypt_cipher, %% cipher. could be different from the algorithm
encrypt_keys, %% encrypt keys
encrypt_block_size = 8,
encrypt_ctx,
decrypt = none, %% decrypt algorithm
+ decrypt_cipher, %% cipher. could be different from the algorithm
decrypt_keys, %% decrypt keys
decrypt_block_size = 8,
decrypt_ctx, %% Decryption context
@@ -457,14 +479,6 @@
recv_ext_info
}).
--record(ssh_key,
- {
- type,
- public,
- private,
- comment = ""
- }).
-
-record(ssh_pty, {term = "", % e.g. "xterm"
width = 80,
height = 25,
@@ -472,13 +486,6 @@
pixel_height = 768,
modes = <<>>}).
-%% assertion macro
--define(ssh_assert(Expr, Reason),
- case Expr of
- true -> ok;
- _ -> exit(Reason)
- end).
-
%% dbg help macros
-define(wr_record(N,BlackList),
diff --git a/lib/ssh/src/ssh_client_channel.erl b/lib/ssh/src/ssh_client_channel.erl
index f985d8e273..3bd1e1fdf1 100644
--- a/lib/ssh/src/ssh_client_channel.erl
+++ b/lib/ssh/src/ssh_client_channel.erl
@@ -52,7 +52,7 @@
-callback handle_msg(Msg ::term(), State :: term()) ->
{ok, State::term()} | {stop, ChannelId::ssh:channel_id(), State::term()}.
--callback handle_ssh_msg({ssh_cm, ConnectionRef::ssh:connection_ref(), SshMsg::term()},
+-callback handle_ssh_msg(ssh_connection:event(),
State::term()) -> {ok, State::term()} |
{stop, ChannelId::ssh:channel_id(),
State::term()}.
diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl
index 9a060b8304..d6b50613f9 100644
--- a/lib/ssh/src/ssh_connect.hrl
+++ b/lib/ssh/src/ssh_connect.hrl
@@ -263,11 +263,8 @@
-record(connection, {
requests = [], %% [{ChannelId, Pid}...] awaiting reply on request,
channel_cache,
- port_bindings,
channel_id_seed,
cli_spec,
- address,
- port,
options,
exec,
system_supervisor,
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index 83f85b1d8e..c5316bf133 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -60,13 +60,121 @@
request_failure_msg/0,
request_success_msg/1,
- bind/4, unbind/3, unbind_channel/2,
- bound_channel/3, encode_ip/1
+ encode_ip/1
]).
-type connection_ref() :: ssh:connection_ref().
-type channel_id() :: ssh:channel_id().
+-type req_status() :: success | failure .
+-type reason() :: closed | timeout .
+
+-type result() :: req_status() | {error, reason()} .
+
+-type ssh_data_type_code() :: non_neg_integer(). % Only 0 and 1 are used
+
+
+%%% The SSH Connection Protocol
+
+-export_type([event/0,
+ channel_msg/0,
+ want_reply/0,
+ data_ch_msg/0,
+ eof_ch_msg/0,
+ signal_ch_msg/0,
+ exit_signal_ch_msg/0,
+ exit_status_ch_msg/0,
+ closed_ch_msg/0,
+ env_ch_msg/0,
+ pty_ch_msg/0,
+ shell_ch_msg/0,
+ window_change_ch_msg/0,
+ exec_ch_msg/0
+ ]).
+
+-type event() :: {ssh_cm, ssh:connection_ref(), channel_msg()}.
+-type channel_msg() :: data_ch_msg()
+ | eof_ch_msg()
+ | closed_ch_msg()
+ | pty_ch_msg()
+ | env_ch_msg()
+ | shell_ch_msg()
+ | exec_ch_msg()
+ | signal_ch_msg()
+ | window_change_ch_msg()
+ | exit_status_ch_msg()
+ | exit_signal_ch_msg()
+ .
+
+-type want_reply() :: boolean().
+
+-type data_ch_msg() :: {data,
+ ssh:channel_id(),
+ ssh_data_type_code(),
+ Data :: binary()
+ } .
+-type eof_ch_msg() :: {eof,
+ ssh:channel_id()
+ } .
+-type signal_ch_msg() :: {signal,
+ ssh:channel_id(),
+ SignalName :: string()
+ } .
+-type exit_signal_ch_msg() :: {exit_signal, ssh:channel_id(),
+ ExitSignal :: string(),
+ ErrorMsg :: string(),
+ LanguageString :: string()} .
+-type exit_status_ch_msg() :: {exit_status,
+ ssh:channel_id(),
+ ExitStatus :: non_neg_integer()
+ } .
+-type closed_ch_msg() :: {closed,
+ ssh:channel_id()
+ } .
+-type env_ch_msg() :: {env,
+ ssh:channel_id(),
+ want_reply(),
+ Var :: string(),
+ Value :: string()
+ } .
+-type pty_ch_msg() :: {pty,
+ ssh:channel_id(),
+ want_reply(),
+ {Terminal :: string(),
+ CharWidth :: non_neg_integer(),
+ RowHeight :: non_neg_integer(),
+ PixelWidth :: non_neg_integer(),
+ PixelHeight :: non_neg_integer(),
+ TerminalModes :: [term_mode()]
+ }
+ } .
+
+-type term_mode() :: {Opcode :: atom() | byte(),
+ Value :: non_neg_integer()} .
+
+-type shell_ch_msg() :: {shell,
+ ssh:channel_id(),
+ want_reply()
+ } .
+-type window_change_ch_msg() :: {window_change,
+ ssh:channel_id(),
+ CharWidth :: non_neg_integer(),
+ RowHeight :: non_neg_integer(),
+ PixelWidth :: non_neg_integer(),
+ PixelHeight :: non_neg_integer()
+ } .
+-type exec_ch_msg() :: {exec,
+ ssh:channel_id(),
+ want_reply(),
+ Command :: string()
+ } .
+
+%%% This function is soley to convince all
+%%% checks that the type event() exists...
+-export([dummy/1]).
+-spec dummy(event()) -> false.
+dummy(_) -> false.
+
%%--------------------------------------------------------------------
%%% API
%%--------------------------------------------------------------------
@@ -77,14 +185,21 @@
%% application, a system command, or some built-in subsystem.
%% --------------------------------------------------------------------
--spec session_channel(connection_ref(), timeout()) ->
- {ok, channel_id()} | {error, timeout | closed}.
+-spec session_channel(ConnectionRef, Timeout) -> Result when
+ ConnectionRef :: ssh:connection_ref(),
+ Timeout :: timeout(),
+ Result :: {ok, ssh:channel_id()} | {error, reason()} .
session_channel(ConnectionHandler, Timeout) ->
session_channel(ConnectionHandler, ?DEFAULT_WINDOW_SIZE, ?DEFAULT_PACKET_SIZE, Timeout).
--spec session_channel(connection_ref(), integer(), integer(), timeout()) ->
- {ok, channel_id()} | {error, timeout | closed}.
+
+-spec session_channel(ConnectionRef, InitialWindowSize, MaxPacketSize, Timeout) -> Result when
+ ConnectionRef :: ssh:connection_ref(),
+ InitialWindowSize :: pos_integer(),
+ MaxPacketSize :: pos_integer(),
+ Timeout :: timeout(),
+ Result :: {ok, ssh:channel_id()} | {error, reason()} .
session_channel(ConnectionHandler, InitialWindowSize, MaxPacketSize, Timeout) ->
case ssh_connection_handler:open_channel(ConnectionHandler, "session", <<>>,
@@ -100,8 +215,11 @@ session_channel(ConnectionHandler, InitialWindowSize, MaxPacketSize, Timeout) ->
%% Description: Will request that the server start the
%% execution of the given command.
%%--------------------------------------------------------------------
--spec exec(connection_ref(), channel_id(), string(), timeout()) ->
- success | failure | {error, timeout | closed}.
+-spec exec(ConnectionRef, ChannelId, Command, Timeout) -> result() when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Command :: string(),
+ Timeout :: timeout().
exec(ConnectionHandler, ChannelId, Command, TimeOut) ->
ssh_connection_handler:request(ConnectionHandler, self(), ChannelId, "exec",
@@ -112,8 +230,10 @@ exec(ConnectionHandler, ChannelId, Command, TimeOut) ->
%% defined in /etc/passwd in UNIX systems) be started at the other
%% end.
%%--------------------------------------------------------------------
--spec shell(connection_ref(), channel_id()) ->
- ok | success | failure | {error, timeout}.
+-spec shell(ConnectionRef, ChannelId) -> Result when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Result :: ok | success | failure | {error, timeout} .
shell(ConnectionHandler, ChannelId) ->
ssh_connection_handler:request(ConnectionHandler, self(), ChannelId,
@@ -122,8 +242,11 @@ shell(ConnectionHandler, ChannelId) ->
%%
%% Description: Executes a predefined subsystem.
%%--------------------------------------------------------------------
--spec subsystem(connection_ref(), channel_id(), string(), timeout()) ->
- success | failure | {error, timeout | closed}.
+-spec subsystem(ConnectionRef, ChannelId, Subsystem, Timeout) -> result() when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Subsystem :: string(),
+ Timeout :: timeout().
subsystem(ConnectionHandler, ChannelId, SubSystem, TimeOut) ->
ssh_connection_handler:request(ConnectionHandler, self(),
@@ -134,12 +257,13 @@ subsystem(ConnectionHandler, ChannelId, SubSystem, TimeOut) ->
%%--------------------------------------------------------------------
-spec send(connection_ref(), channel_id(), iodata()) ->
ok | {error, timeout | closed}.
+
send(ConnectionHandler, ChannelId, Data) ->
send(ConnectionHandler, ChannelId, 0, Data, infinity).
--spec send(connection_ref(), channel_id(), integer()| iodata(), timeout() | iodata()) ->
- ok | {error, timeout | closed}.
+-spec send(connection_ref(), channel_id(), iodata(), timeout()) -> ok | {error, reason()};
+ (connection_ref(), channel_id(), ssh_data_type_code(), iodata()) -> ok | {error, reason()}.
send(ConnectionHandler, ChannelId, Data, TimeOut) when is_integer(TimeOut) ->
send(ConnectionHandler, ChannelId, 0, Data, TimeOut);
@@ -151,14 +275,15 @@ send(ConnectionHandler, ChannelId, Type, Data) ->
send(ConnectionHandler, ChannelId, Type, Data, infinity).
--spec send(connection_ref(), channel_id(), integer(), iodata(), timeout()) ->
- ok | {error, timeout | closed}.
+-spec send(connection_ref(), channel_id(), ssh_data_type_code(), iodata(), timeout()) -> ok | {error, reason()}.
send(ConnectionHandler, ChannelId, Type, Data, TimeOut) ->
ssh_connection_handler:send(ConnectionHandler, ChannelId,
Type, Data, TimeOut).
%%--------------------------------------------------------------------
--spec send_eof(connection_ref(), channel_id()) -> ok | {error, closed}.
+-spec send_eof(ConnectionRef, ChannelId) -> ok | {error, closed} when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id().
%%
%%
%% Description: Sends eof on the channel <ChannelId>.
@@ -167,7 +292,10 @@ send_eof(ConnectionHandler, Channel) ->
ssh_connection_handler:send_eof(ConnectionHandler, Channel).
%%--------------------------------------------------------------------
--spec adjust_window(connection_ref(), channel_id(), integer()) -> ok.
+-spec adjust_window(ConnectionRef, ChannelId, NumOfBytes) -> ok when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ NumOfBytes :: integer().
%%
%%
%% Description: Adjusts the ssh flowcontrol window.
@@ -176,8 +304,12 @@ adjust_window(ConnectionHandler, Channel, Bytes) ->
ssh_connection_handler:adjust_window(ConnectionHandler, Channel, Bytes).
%%--------------------------------------------------------------------
--spec setenv(connection_ref(), channel_id(), string(), string(), timeout()) ->
- success | failure | {error, timeout | closed}.
+-spec setenv(ConnectionRef, ChannelId, Var, Value, Timeout) -> result() when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Var :: string(),
+ Value :: string(),
+ Timeout :: timeout().
%%
%%
%% Description: Environment variables may be passed to the shell/command to be
@@ -189,7 +321,9 @@ setenv(ConnectionHandler, ChannelId, Var, Value, TimeOut) ->
%%--------------------------------------------------------------------
--spec close(connection_ref(), channel_id()) -> ok.
+-spec close(ConnectionRef, ChannelId) -> ok when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id().
%%
%%
%% Description: Sends a close message on the channel <ChannelId>.
@@ -198,7 +332,11 @@ close(ConnectionHandler, ChannelId) ->
ssh_connection_handler:close(ConnectionHandler, ChannelId).
%%--------------------------------------------------------------------
--spec reply_request(connection_ref(), boolean(), success | failure, channel_id()) -> ok.
+-spec reply_request(ConnectionRef, WantReply, Status, ChannelId) -> ok when
+ ConnectionRef :: ssh:connection_ref(),
+ WantReply :: boolean(),
+ Status :: req_status(),
+ ChannelId :: ssh:channel_id().
%%
%%
%% Description: Send status replies to requests that want such replies.
@@ -211,15 +349,20 @@ reply_request(_,false, _, _) ->
%%--------------------------------------------------------------------
%% Description: Sends a ssh connection protocol pty_req.
%%--------------------------------------------------------------------
--spec ptty_alloc(connection_ref(), channel_id(), proplists:proplist()) ->
- success | failure | {error, timeout}.
+-spec ptty_alloc(ConnectionRef, ChannelId, Options) -> result() when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Options :: proplists:proplist().
ptty_alloc(ConnectionHandler, Channel, Options) ->
ptty_alloc(ConnectionHandler, Channel, Options, infinity).
--spec ptty_alloc(connection_ref(), channel_id(), proplists:proplist(), timeout()) ->
- success | failure | {error, timeout | closed}.
+-spec ptty_alloc(ConnectionRef, ChannelId, Options, Timeout) -> result() when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Options :: proplists:proplist(),
+ Timeout :: timeout().
ptty_alloc(ConnectionHandler, Channel, Options0, TimeOut) ->
TermData = backwards_compatible(Options0, []), % FIXME
@@ -252,6 +395,10 @@ signal(ConnectionHandler, Channel, Sig) ->
"signal", false, [?string(Sig)], 0).
+-spec exit_status(ConnectionRef, ChannelId, Status) -> ok when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Status :: integer().
exit_status(ConnectionHandler, Channel, Status) ->
ssh_connection_handler:request(ConnectionHandler, Channel,
"exit-status", false, [?uint32(Status)], 0).
@@ -713,29 +860,6 @@ request_success_msg(Data) ->
%%%----------------------------------------------------------------
%%%
%%%
-bind(IP, Port, ChannelPid, Connection) ->
- Binds = [{{IP, Port}, ChannelPid}
- | lists:keydelete({IP, Port}, 1,
- Connection#connection.port_bindings)],
- Connection#connection{port_bindings = Binds}.
-
-unbind(IP, Port, Connection) ->
- Connection#connection{
- port_bindings =
- lists:keydelete({IP, Port}, 1,
- Connection#connection.port_bindings)}.
-unbind_channel(ChannelPid, Connection) ->
- Binds = [{Bind, ChannelP} || {Bind, ChannelP}
- <- Connection#connection.port_bindings,
- ChannelP =/= ChannelPid],
- Connection#connection{port_bindings = Binds}.
-
-bound_channel(IP, Port, Connection) ->
- case lists:keysearch({IP, Port}, 1, Connection#connection.port_bindings) of
- {value, {{IP, Port}, ChannelPid}} -> ChannelPid;
- _ -> undefined
- end.
-
encode_ip(Addr) when is_tuple(Addr) ->
case catch inet_parse:ntoa(Addr) of
{'EXIT',_} -> false;
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 8f32966a12..e984cbb21b 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -386,16 +386,24 @@ init_connection_handler(Role, Socket, Opts) ->
D);
{stop, Error} ->
- Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
- C = #connection{system_supervisor = proplists:get_value(system_sup, Sups),
- sub_system_supervisor = proplists:get_value(subsystem_sup, Sups),
- connection_supervisor = proplists:get_value(connection_sup, Sups)
- },
+ D = try
+ %% Only servers have supervisorts defined in Opts
+ Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
+ #connection{system_supervisor = proplists:get_value(system_sup, Sups),
+ sub_system_supervisor = proplists:get_value(subsystem_sup, Sups),
+ connection_supervisor = proplists:get_value(connection_sup, Sups)
+ }
+ of
+ C ->
+ #data{connection_state=C}
+ catch
+ _:_ ->
+ #data{connection_state=#connection{}}
+ end,
gen_statem:enter_loop(?MODULE,
[],
{init_error,Error},
- #data{connection_state=C,
- socket=Socket})
+ D#data{socket=Socket})
end.
@@ -406,7 +414,6 @@ init([Role,Socket,Opts]) ->
{Protocol, Callback, CloseTag} = ?GET_OPT(transport, Opts),
C = #connection{channel_cache = ssh_client_channel:cache_create(),
channel_id_seed = 0,
- port_bindings = [],
requests = [],
options = Opts},
D0 = #data{starter = ?GET_INTERNAL_OPT(user_pid, Opts),
@@ -1550,7 +1557,7 @@ terminate({shutdown,"Connection closed"}, _StateName, D) ->
terminate({shutdown,{init,Reason}}, StateName, D) ->
%% Error in initiation. "This error should not occur".
- log(error, D, io_lib:format("Shutdown in init (StateName=~p): ~p~n",[StateName,Reason])),
+ log(error, D, "Shutdown in init (StateName=~p): ~p~n", [StateName,Reason]),
stop_subsystem(D),
close_transport(D);
@@ -1952,12 +1959,12 @@ send_disconnect(Code, Reason, DetailedText, Module, Line, StateName, D0) ->
call_disconnectfun_and_log_cond(LogMsg, DetailedText, Module, Line, StateName, D) ->
case disconnect_fun(LogMsg, D) of
void ->
- log(info, D,
- io_lib:format("~s~n"
- "State = ~p~n"
- "Module = ~p, Line = ~p.~n"
- "Details:~n ~s~n",
- [LogMsg, StateName, Module, Line, DetailedText]));
+ log(info, D,
+ "~s~n"
+ "State = ~p~n"
+ "Module = ~p, Line = ~p.~n"
+ "Details:~n ~s~n",
+ [LogMsg, StateName, Module, Line, DetailedText]);
_ ->
ok
end.
@@ -2021,6 +2028,9 @@ fold_keys(Keys, Fun, Extra) ->
end, [], Keys).
%%%----------------------------------------------------------------
+log(Tag, D, Format, Args) ->
+ log(Tag, D, io_lib:format(Format,Args)).
+
log(Tag, D, Reason) ->
case atom_to_list(Tag) of % Dialyzer-technical reasons...
"error" -> do_log(error_msg, Reason, D);
@@ -2028,36 +2038,56 @@ log(Tag, D, Reason) ->
"info" -> do_log(info_msg, Reason, D)
end.
-do_log(F, Reason, #data{ssh_params = #ssh{role = Role} = S
- }) ->
- VSN =
- case application:get_key(ssh,vsn) of
- {ok,Vsn} -> Vsn;
- undefined -> ""
- end,
- PeerVersion =
- case Role of
- server -> S#ssh.c_version;
- client -> S#ssh.s_version
- end,
- CryptoInfo =
- try
- [{_,_,CI}] = crypto:info_lib(),
- <<"(",CI/binary,")">>
+
+do_log(F, Reason0, #data{ssh_params = S}) ->
+ Reason =
+ try io_lib:format("~s",[Reason0])
+ of _ -> Reason0
catch
- _:_ -> ""
- end,
- Other =
- case Role of
- server -> "Client";
- client -> "Server"
+ _:_ -> io_lib:format("~p",[Reason0])
end,
- error_logger:F("Erlang SSH ~p ~s ~s.~n"
- "~s: ~p~n"
- "~s~n",
- [Role, VSN, CryptoInfo,
- Other, PeerVersion,
- Reason]).
+ case S of
+ #ssh{role = Role} when Role==server ;
+ Role==client ->
+ {PeerRole,PeerVersion} =
+ case Role of
+ server -> {"Client", S#ssh.c_version};
+ client -> {"Server", S#ssh.s_version}
+ end,
+ error_logger:F("Erlang SSH ~p ~s ~s.~n"
+ "~s: ~p~n"
+ "~s~n",
+ [Role,
+ ssh_log_version(), crypto_log_info(),
+ PeerRole, PeerVersion,
+ Reason]);
+ _ ->
+ error_logger:F("Erlang SSH ~s ~s.~n"
+ "~s~n",
+ [ssh_log_version(), crypto_log_info(),
+ Reason])
+ end.
+
+crypto_log_info() ->
+ try
+ [{_,_,CI}] = crypto:info_lib(),
+ case crypto:info_fips() of
+ enabled ->
+ <<"(",CI/binary,". FIPS enabled)">>;
+ not_enabled ->
+ <<"(",CI/binary,". FIPS available but not enabled)">>;
+ _ ->
+ <<"(",CI/binary,")">>
+ end
+ catch
+ _:_ -> ""
+ end.
+
+ssh_log_version() ->
+ case application:get_key(ssh,vsn) of
+ {ok,Vsn} -> Vsn;
+ undefined -> ""
+ end.
%%%----------------------------------------------------------------
not_connected_filter({connection_reply, _Data}) -> true;
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index d95e58c1bb..7c86a81108 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -34,24 +34,6 @@
-export([dbg_trace/3]).
--define('2bin'(X), (if is_binary(X) -> X;
- is_list(X) -> list_to_binary(X);
- X==undefined -> <<>>
- end) ).
-
--define('E...'(X), ?'2bin'(X)/binary ).
--define(Eboolean(X), ?BOOLEAN(case X of
- true -> ?TRUE;
- false -> ?FALSE
- end) ).
--define(Ebyte(X), ?BYTE(X) ).
--define(Euint32(X), ?UINT32(X) ).
--define(Estring(X), ?STRING(?'2bin'(X)) ).
--define(Estring_utf8(X), ?string_utf8(X)/binary ).
--define(Ename_list(X), ?STRING(ssh_bits:name_list(X)) ).
--define(Empint(X), (ssh_bits:mpint(X))/binary ).
--define(Ebinary(X), ?STRING(X) ).
-
ucl(B) ->
try unicode:characters_to_list(B) of
L when is_list(L) -> L;
diff --git a/lib/ssh/src/ssh_server_channel.erl b/lib/ssh/src/ssh_server_channel.erl
index 555080e9ee..1905c40c98 100644
--- a/lib/ssh/src/ssh_server_channel.erl
+++ b/lib/ssh/src/ssh_server_channel.erl
@@ -37,7 +37,7 @@
-callback handle_msg(Msg ::term(), State :: term()) ->
{ok, State::term()} | {stop, ChannelId::ssh:channel_id(), State::term()}.
--callback handle_ssh_msg({ssh_cm, ConnectionRef::ssh:connection_ref(), SshMsg::term()},
+-callback handle_ssh_msg(ssh_connection:event(),
State::term()) -> {ok, State::term()} |
{stop, ChannelId::ssh:channel_id(),
State::term()}.
diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl
index 1b2ba5a50b..4b6e187c3a 100644
--- a/lib/ssh/src/ssh_sftp.erl
+++ b/lib/ssh/src/ssh_sftp.erl
@@ -92,24 +92,63 @@
-define(XF(S), S#state.xf).
-define(REQID(S), S#state.req_id).
+-type sftp_option() :: {timeout, timeout()}
+ | {sftp_vsn, pos_integer()}
+ | {window_size, pos_integer()}
+ | {packet_size, pos_integer()} .
+
+-type reason() :: atom() | string() | tuple() .
+
%%====================================================================
%% API
%%====================================================================
+
+
+%%%================================================================
+%%%
+
+%%%----------------------------------------------------------------
+%%% start_channel/1
+
start_channel(Cm) when is_pid(Cm) ->
start_channel(Cm, []);
+
start_channel(Socket) when is_port(Socket) ->
start_channel(Socket, []);
-start_channel(Host) when is_list(Host) ->
+
+start_channel(Host) ->
start_channel(Host, []).
+
+%%%----------------------------------------------------------------
+%%% start_channel/2
+
+%%% -spec:s are as if Dialyzer handled signatures for separate
+%%% function clauses.
+
+-spec start_channel(ssh:open_socket(),
+ [ssh:client_options() | sftp_option()]
+ )
+ -> {ok,pid(),ssh:connection_ref()} | {error,reason()};
+
+ (ssh:connection_ref(),
+ [sftp_option()]
+ )
+ -> {ok,pid()} | {ok,pid(),ssh:connection_ref()} | {error,reason()};
+
+ (ssh:host(),
+ [ssh:client_options() | sftp_option()]
+ )
+ -> {ok,pid(),ssh:connection_ref()} | {error,reason()} .
+
start_channel(Socket, UserOptions) when is_port(Socket) ->
- {SshOpts, _ChanOpts, SftpOpts} = handle_options(UserOptions),
+ {SshOpts, ChanOpts, SftpOpts} = handle_options(UserOptions),
Timeout = % A mixture of ssh:connect and ssh_sftp:start_channel:
proplists:get_value(connect_timeout, SshOpts,
proplists:get_value(timeout, SftpOpts, infinity)),
case ssh:connect(Socket, SshOpts, Timeout) of
{ok,Cm} ->
- case start_channel(Cm, UserOptions) of
+ case start_channel(Cm, ChanOpts ++ SftpOpts) of
{ok, Pid} ->
{ok, Pid, Cm};
Error ->
@@ -144,6 +183,16 @@ start_channel(Cm, UserOptions) when is_pid(Cm) ->
start_channel(Host, UserOptions) ->
start_channel(Host, 22, UserOptions).
+
+%%%----------------------------------------------------------------
+%%% start_channel/3
+
+-spec start_channel(ssh:host(),
+ inet:port_number(),
+ [ssh:client_option() | sftp_option()]
+ )
+ -> {ok,pid(),ssh:connection_ref()} | {error,reason()}.
+
start_channel(Host, Port, UserOptions) ->
{SshOpts, ChanOpts, SftpOpts} = handle_options(UserOptions),
Timeout = % A mixture of ssh:connect and ssh_sftp:start_channel:
@@ -168,6 +217,15 @@ start_channel(Host, Port, UserOptions) ->
Error
end.
+%%% Helper for start_channel
+
+wait_for_version_negotiation(Pid, Timeout) ->
+ call(Pid, wait_for_version_negotiation, Timeout).
+
+%%%----------------------------------------------------------------
+-spec stop_channel(ChannelPid) -> ok when
+ ChannelPid :: pid().
+
stop_channel(Pid) ->
case is_process_alive(Pid) of
true ->
@@ -185,20 +243,63 @@ stop_channel(Pid) ->
ok
end.
-wait_for_version_negotiation(Pid, Timeout) ->
- call(Pid, wait_for_version_negotiation, Timeout).
-
+%%%----------------------------------------------------------------
+-spec open(ChannelPid, Name, Mode) -> {ok, Handle} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Mode :: [read | write | append | binary | raw],
+ Handle :: term(),
+ Error :: {error, reason()} .
open(Pid, File, Mode) ->
open(Pid, File, Mode, ?FILEOP_TIMEOUT).
+-spec open(ChannelPid, Name, Mode, Timeout) -> {ok, Handle} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Mode :: [read | write | append | binary | raw],
+ Timeout :: timeout(),
+ Handle :: term(),
+ Error :: {error, reason()} .
open(Pid, File, Mode, FileOpTimeout) ->
call(Pid, {open, false, File, Mode}, FileOpTimeout).
+
+-type tar_crypto_spec() :: encrypt_spec() | decrypt_spec() .
+
+-type encrypt_spec() :: {init_fun(), crypto_fun(), final_fun()} .
+-type decrypt_spec() :: {init_fun(), crypto_fun()} .
+
+-type init_fun() :: fun(() -> {ok,crypto_state()})
+ | fun(() -> {ok,crypto_state(),chunk_size()}) .
+
+-type crypto_fun() :: fun((TextIn::binary(), crypto_state()) -> crypto_result()) .
+-type crypto_result() :: {ok,TextOut::binary(),crypto_state()}
+ | {ok,TextOut::binary(),crypto_state(),chunk_size()} .
+
+-type final_fun() :: fun((FinalTextIn::binary(),crypto_state()) -> {ok,FinalTextOut::binary()}) .
+
+-type chunk_size() :: undefined | pos_integer().
+-type crypto_state() :: any() .
+
+
+-spec open_tar(ChannelPid, Path, Mode) -> {ok, Handle} | Error when
+ ChannelPid :: pid(),
+ Path :: string(),
+ Mode :: [read | write | {crypto, tar_crypto_spec()} ],
+ Handle :: term(),
+ Error :: {error, reason()} .
open_tar(Pid, File, Mode) ->
open_tar(Pid, File, Mode, ?FILEOP_TIMEOUT).
+-spec open_tar(ChannelPid, Path, Mode, Timeout) -> {ok, Handle} | Error when
+ ChannelPid :: pid(),
+ Path :: string(),
+ Mode :: [read | write | {crypto, tar_crypto_spec()} ],
+ Timeout :: timeout(),
+ Handle :: term(),
+ Error :: {error, reason()} .
open_tar(Pid, File, Mode, FileOpTimeout) ->
case {lists:member(write,Mode),
lists:member(read,Mode),
- Mode -- [read,write]} of
+ Mode -- [write,read]} of
{true,false,[]} ->
{ok,Handle} = open(Pid, File, [write], FileOpTimeout),
erl_tar:init(Pid, write,
@@ -264,13 +365,33 @@ open_tar(Pid, File, Mode, FileOpTimeout) ->
end.
+-spec opendir(ChannelPid, Path) -> {ok, Handle} | Error when
+ ChannelPid :: pid(),
+ Path :: string(),
+ Handle :: term(),
+ Error :: {error, reason()} .
opendir(Pid, Path) ->
opendir(Pid, Path, ?FILEOP_TIMEOUT).
+-spec opendir(ChannelPid, Path, Timeout) -> {ok, Handle} | Error when
+ ChannelPid :: pid(),
+ Path :: string(),
+ Timeout :: timeout(),
+ Handle :: term(),
+ Error :: {error, reason()} .
opendir(Pid, Path, FileOpTimeout) ->
call(Pid, {opendir, false, Path}, FileOpTimeout).
+-spec close(ChannelPid, Handle) -> ok | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Error :: {error, reason()} .
close(Pid, Handle) ->
close(Pid, Handle, ?FILEOP_TIMEOUT).
+-spec close(ChannelPid, Handle, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Timeout :: timeout(),
+ Error :: {error, reason()} .
close(Pid, Handle, FileOpTimeout) ->
call(Pid, {close,false,Handle}, FileOpTimeout).
@@ -279,47 +400,149 @@ readdir(Pid,Handle) ->
readdir(Pid,Handle, FileOpTimeout) ->
call(Pid, {readdir,false,Handle}, FileOpTimeout).
+-spec pread(ChannelPid, Handle, Position, Len) -> {ok, Data} | eof | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Position :: integer(),
+ Len :: integer(),
+ Data :: string() | binary(),
+ Error :: {error, reason()}.
pread(Pid, Handle, Offset, Len) ->
pread(Pid, Handle, Offset, Len, ?FILEOP_TIMEOUT).
+
+-spec pread(ChannelPid, Handle, Position, Len, Timeout) -> {ok, Data} | eof | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Position :: integer(),
+ Len :: integer(),
+ Timeout :: timeout(),
+ Data :: string() | binary(),
+ Error :: {error, reason()}.
pread(Pid, Handle, Offset, Len, FileOpTimeout) ->
call(Pid, {pread,false,Handle, Offset, Len}, FileOpTimeout).
+
+-spec read(ChannelPid, Handle, Len) -> {ok, Data} | eof | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Len :: integer(),
+ Data :: string() | binary(),
+ Error :: {error, reason()}.
read(Pid, Handle, Len) ->
read(Pid, Handle, Len, ?FILEOP_TIMEOUT).
+
+-spec read(ChannelPid, Handle, Len, Timeout) -> {ok, Data} | eof | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Len :: integer(),
+ Timeout :: timeout(),
+ Data :: string() | binary(),
+ Error :: {error, reason()}.
read(Pid, Handle, Len, FileOpTimeout) ->
call(Pid, {read,false,Handle, Len}, FileOpTimeout).
+
%% TODO this ought to be a cast! Is so in all practical meaning
%% even if it is obscure!
+-spec apread(ChannelPid, Handle, Position, Len) -> {async, N} | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Position :: integer(),
+ Len :: integer(),
+ Error :: {error, reason()},
+ N :: term() .
apread(Pid, Handle, Offset, Len) ->
call(Pid, {pread,true,Handle, Offset, Len}, infinity).
%% TODO this ought to be a cast!
+-spec aread(ChannelPid, Handle, Len) -> {async, N} | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Len :: integer(),
+ Error :: {error, reason()},
+ N :: term() .
aread(Pid, Handle, Len) ->
call(Pid, {read,true,Handle, Len}, infinity).
+
+-spec pwrite(ChannelPid, Handle, Position, Data) -> ok | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Position :: integer(),
+ Data :: iolist(),
+ Error :: {error, reason()}.
pwrite(Pid, Handle, Offset, Data) ->
pwrite(Pid, Handle, Offset, Data, ?FILEOP_TIMEOUT).
+
+-spec pwrite(ChannelPid, Handle, Position, Data, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Position :: integer(),
+ Data :: iolist(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
pwrite(Pid, Handle, Offset, Data, FileOpTimeout) ->
call(Pid, {pwrite,false,Handle,Offset,Data}, FileOpTimeout).
+
+-spec write(ChannelPid, Handle, Data) -> ok | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Data :: iodata(),
+ Error :: {error, reason()}.
write(Pid, Handle, Data) ->
write(Pid, Handle, Data, ?FILEOP_TIMEOUT).
+
+-spec write(ChannelPid, Handle, Data, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Data :: iodata(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
write(Pid, Handle, Data, FileOpTimeout) ->
call(Pid, {write,false,Handle,Data}, FileOpTimeout).
%% TODO this ought to be a cast! Is so in all practical meaning
%% even if it is obscure!
+-spec apwrite(ChannelPid, Handle, Position, Data) -> {async, N} | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Position :: integer(),
+ Data :: binary(),
+ Error :: {error, reason()},
+ N :: term() .
apwrite(Pid, Handle, Offset, Data) ->
call(Pid, {pwrite,true,Handle,Offset,Data}, infinity).
%% TODO this ought to be a cast! Is so in all practical meaning
%% even if it is obscure!
+-spec awrite(ChannelPid, Handle, Data) -> {async, N} | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Data :: binary(),
+ Error :: {error, reason()},
+ N :: term() .
awrite(Pid, Handle, Data) ->
call(Pid, {write,true,Handle,Data}, infinity).
+-spec position(ChannelPid, Handle, Location) -> {ok, NewPosition} | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Location :: Offset | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof,
+ Offset :: integer(),
+ NewPosition :: integer(),
+ Error :: {error, reason()}.
position(Pid, Handle, Pos) ->
position(Pid, Handle, Pos, ?FILEOP_TIMEOUT).
+
+-spec position(ChannelPid, Handle, Location, Timeout) -> {ok, NewPosition} | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Location :: Offset | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof,
+ Timeout :: timeout(),
+ Offset :: integer(),
+ NewPosition :: integer(),
+ Error :: {error, reason()}.
position(Pid, Handle, Pos, FileOpTimeout) ->
call(Pid, {position, Handle, Pos}, FileOpTimeout).
@@ -328,8 +551,21 @@ real_path(Pid, Path) ->
real_path(Pid, Path, FileOpTimeout) ->
call(Pid, {real_path, false, Path}, FileOpTimeout).
+
+-spec read_file_info(ChannelPid, Name) -> {ok, FileInfo} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ FileInfo :: file:file_info(),
+ Error :: {error, reason()}.
read_file_info(Pid, Name) ->
read_file_info(Pid, Name, ?FILEOP_TIMEOUT).
+
+-spec read_file_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Timeout :: timeout(),
+ FileInfo :: file:file_info(),
+ Error :: {error, reason()}.
read_file_info(Pid, Name, FileOpTimeout) ->
call(Pid, {read_file_info,false,Name}, FileOpTimeout).
@@ -338,18 +574,57 @@ get_file_info(Pid, Handle) ->
get_file_info(Pid, Handle, FileOpTimeout) ->
call(Pid, {get_file_info,false,Handle}, FileOpTimeout).
+
+-spec write_file_info(ChannelPid, Name, FileInfo) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ FileInfo :: file:file_info(),
+ Error :: {error, reason()}.
write_file_info(Pid, Name, Info) ->
write_file_info(Pid, Name, Info, ?FILEOP_TIMEOUT).
+
+-spec write_file_info(ChannelPid, Name, FileInfo, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ FileInfo :: file:file_info(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
write_file_info(Pid, Name, Info, FileOpTimeout) ->
call(Pid, {write_file_info,false,Name, Info}, FileOpTimeout).
+
+-spec read_link_info(ChannelPid, Name) -> {ok, FileInfo} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ FileInfo :: file:file_info(),
+ Error :: {error, reason()}.
read_link_info(Pid, Name) ->
read_link_info(Pid, Name, ?FILEOP_TIMEOUT).
+
+-spec read_link_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ FileInfo :: file:file_info(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
read_link_info(Pid, Name, FileOpTimeout) ->
call(Pid, {read_link_info,false,Name}, FileOpTimeout).
+
+-spec read_link(ChannelPid, Name) -> {ok, Target} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Target :: string(),
+ Error :: {error, reason()}.
read_link(Pid, LinkName) ->
read_link(Pid, LinkName, ?FILEOP_TIMEOUT).
+
+-spec read_link(ChannelPid, Name, Timeout) -> {ok, Target} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Target :: string(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
read_link(Pid, LinkName, FileOpTimeout) ->
case call(Pid, {read_link,false,LinkName}, FileOpTimeout) of
{ok, [{Name, _Attrs}]} ->
@@ -358,28 +633,79 @@ read_link(Pid, LinkName, FileOpTimeout) ->
ErrMsg
end.
+-spec make_symlink(ChannelPid, Name, Target) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Target :: string(),
+ Error :: {error, reason()} .
make_symlink(Pid, Name, Target) ->
make_symlink(Pid, Name, Target, ?FILEOP_TIMEOUT).
+-spec make_symlink(ChannelPid, Name, Target, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Target :: string(),
+ Timeout :: timeout(),
+ Error :: {error, reason()} .
make_symlink(Pid, Name, Target, FileOpTimeout) ->
call(Pid, {make_symlink,false, Name, Target}, FileOpTimeout).
+
+-spec rename(ChannelPid, OldName, NewName) -> ok | Error when
+ ChannelPid :: pid(),
+ OldName :: string(),
+ NewName :: string(),
+ Error :: {error, reason()}.
rename(Pid, FromFile, ToFile) ->
rename(Pid, FromFile, ToFile, ?FILEOP_TIMEOUT).
+
+-spec rename(ChannelPid, OldName, NewName, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ OldName :: string(),
+ NewName :: string(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
rename(Pid, FromFile, ToFile, FileOpTimeout) ->
call(Pid, {rename,false,FromFile, ToFile}, FileOpTimeout).
+-spec delete(ChannelPid, Name) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Error :: {error, reason()} .
delete(Pid, Name) ->
delete(Pid, Name, ?FILEOP_TIMEOUT).
+-spec delete(ChannelPid, Name, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Timeout :: timeout(),
+ Error :: {error, reason()} .
delete(Pid, Name, FileOpTimeout) ->
call(Pid, {delete,false,Name}, FileOpTimeout).
+-spec make_dir(ChannelPid, Name) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Error :: {error, reason()} .
make_dir(Pid, Name) ->
make_dir(Pid, Name, ?FILEOP_TIMEOUT).
+-spec make_dir(ChannelPid, Name, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Timeout :: timeout(),
+ Error :: {error, reason()} .
make_dir(Pid, Name, FileOpTimeout) ->
call(Pid, {make_dir,false,Name}, FileOpTimeout).
+-spec del_dir(ChannelPid, Name) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Error :: {error, reason()} .
del_dir(Pid, Name) ->
del_dir(Pid, Name, ?FILEOP_TIMEOUT).
+-spec del_dir(ChannelPid, Name, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Timeout :: timeout(),
+ Error :: {error, reason()} .
del_dir(Pid, Name, FileOpTimeout) ->
call(Pid, {del_dir,false,Name}, FileOpTimeout).
@@ -396,9 +722,21 @@ recv_window(Pid, FileOpTimeout) ->
call(Pid, recv_window, FileOpTimeout).
+-spec list_dir(ChannelPid, Path) -> {ok,FileNames} | Error when
+ ChannelPid :: pid(),
+ Path :: string(),
+ FileNames :: [FileName],
+ FileName :: string(),
+ Error :: {error, reason()} .
list_dir(Pid, Name) ->
list_dir(Pid, Name, ?FILEOP_TIMEOUT).
-
+-spec list_dir(ChannelPid, Path, Timeout) -> {ok,FileNames} | Error when
+ ChannelPid :: pid(),
+ Path :: string(),
+ Timeout :: timeout(),
+ FileNames :: [FileName],
+ FileName :: string(),
+ Error :: {error, reason()} .
list_dir(Pid, Name, FileOpTimeout) ->
case opendir(Pid, Name, FileOpTimeout) of
{ok,Handle} ->
@@ -429,9 +767,20 @@ do_list_dir(Pid, Handle, FileOpTimeout, Acc) ->
end.
+-spec read_file(ChannelPid, File) -> {ok, Data} | Error when
+ ChannelPid :: pid(),
+ File :: string(),
+ Data :: binary(),
+ Error :: {error, reason()}.
read_file(Pid, Name) ->
read_file(Pid, Name, ?FILEOP_TIMEOUT).
+-spec read_file(ChannelPid, File, Timeout) -> {ok, Data} | Error when
+ ChannelPid :: pid(),
+ File :: string(),
+ Data :: binary(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
read_file(Pid, Name, FileOpTimeout) ->
case open(Pid, Name, [read, binary], FileOpTimeout) of
{ok, Handle} ->
@@ -453,9 +802,20 @@ read_file_loop(Pid, Handle, PacketSz, FileOpTimeout, Acc) ->
Error
end.
+-spec write_file(ChannelPid, File, Data) -> ok | Error when
+ ChannelPid :: pid(),
+ File :: string(),
+ Data :: iodata(),
+ Error :: {error, reason()}.
write_file(Pid, Name, List) ->
write_file(Pid, Name, List, ?FILEOP_TIMEOUT).
+-spec write_file(ChannelPid, File, Data, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ File :: string(),
+ Data :: iodata(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
write_file(Pid, Name, List, FileOpTimeout) when is_list(List) ->
write_file(Pid, Name, list_to_binary(List), FileOpTimeout);
write_file(Pid, Name, Bin, FileOpTimeout) ->
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index aa9ba0f9bb..bf921f0ff3 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -58,7 +58,17 @@
%%====================================================================
%% API
%%====================================================================
--spec subsystem_spec(list()) -> subsystem_spec().
+-spec subsystem_spec(Options) -> Spec when
+ Options :: [ {cwd, string()} |
+ {file_handler, CallbackModule::string()} |
+ {max_files, integer()} |
+ {root, string()} |
+ {sftpd_vsn, integer()}
+ ],
+ Spec :: {Name, {CbMod,Options}},
+ Name :: string(),
+ CbMod :: atom() .
+
subsystem_spec(Options) ->
{"sftp", {?MODULE, Options}}.
@@ -508,7 +518,7 @@ close_our_file({_,Fd}, FileMod, FS0) ->
FS1.
%%% stat: do the stat
-stat(Vsn, ReqId, Data, State, F) ->
+stat(_Vsn, ReqId, Data, State, F) ->
<<?UINT32(BLen), BPath:BLen/binary, _/binary>> = Data,
stat(ReqId, unicode:characters_to_list(BPath), State, F).
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 9ff20454cd..a85926354e 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -61,14 +61,6 @@
-export([pack/3, adjust_algs_for_peer_version/2]).
-export([decompress/2, decrypt_blocks/3, is_valid_mac/3 ]). % FIXME: remove
--define(Estring(X), ?STRING((if is_binary(X) -> X;
- is_list(X) -> list_to_binary(X);
- X==undefined -> <<>>
- end))).
--define(Empint(X), (ssh_bits:mpint(X))/binary ).
--define(Ebinary(X), ?STRING(X) ).
--define(Euint32(X), ?UINT32(X) ).
-
%%%----------------------------------------------------------------------------
%%%
%%% There is a difference between supported and default algorithms. The
@@ -162,15 +154,15 @@ supported_algorithms(cipher) ->
select_crypto_supported(
[
{'[email protected]', [{ciphers,chacha20}, {macs,poly1305}]},
- {'[email protected]', [{ciphers,{aes_gcm,256}}]},
- {'aes256-ctr', [{ciphers,{aes_ctr,256}}]},
- {'aes192-ctr', [{ciphers,{aes_ctr,192}}]},
- {'[email protected]', [{ciphers,{aes_gcm,128}}]},
- {'aes128-ctr', [{ciphers,{aes_ctr,128}}]},
- {'AEAD_AES_256_GCM', [{ciphers,{aes_gcm,256}}]},
- {'AEAD_AES_128_GCM', [{ciphers,{aes_gcm,128}}]},
- {'aes128-cbc', [{ciphers,aes_cbc128}]},
- {'3des-cbc', [{ciphers,des3_cbc}]}
+ {'[email protected]', [{ciphers,aes_256_gcm}]},
+ {'aes256-ctr', [{ciphers,aes_256_ctr}]},
+ {'aes192-ctr', [{ciphers,aes_192_ctr}]},
+ {'[email protected]', [{ciphers,aes_128_gcm}]},
+ {'aes128-ctr', [{ciphers,aes_128_ctr}]},
+ {'AEAD_AES_256_GCM', [{ciphers,aes_256_gcm}]},
+ {'AEAD_AES_128_GCM', [{ciphers,aes_128_gcm}]},
+ {'aes128-cbc', [{ciphers,aes_128_cbc}]},
+ {'3des-cbc', [{ciphers,des_ede3_cbc}]}
]
));
supported_algorithms(mac) ->
@@ -179,8 +171,8 @@ supported_algorithms(mac) ->
[{'hmac-sha2-256', [{macs,hmac}, {hashs,sha256}]},
{'hmac-sha2-512', [{macs,hmac}, {hashs,sha512}]},
{'hmac-sha1', [{macs,hmac}, {hashs,sha}]},
- {'AEAD_AES_128_GCM', [{ciphers,{aes_gcm,128}}]},
- {'AEAD_AES_256_GCM', [{ciphers,{aes_gcm,256}}]}
+ {'AEAD_AES_128_GCM', [{ciphers,aes_128_gcm}]},
+ {'AEAD_AES_256_GCM', [{ciphers,aes_256_gcm}]}
]
));
supported_algorithms(compression) ->
@@ -1256,11 +1248,6 @@ get_length(aead, EncryptedBuffer, Ssh) ->
end.
-pkt_type('AEAD_AES_128_GCM') -> aead;
-pkt_type('AEAD_AES_256_GCM') -> aead;
-pkt_type('[email protected]') -> aead;
-pkt_type(_) -> common.
-
payload(<<PacketLen:32, PaddingLen:8, PayloadAndPadding/binary>>) ->
PayloadLen = PacketLen - PaddingLen - 1,
<<Payload:PayloadLen/binary, _/binary>> = PayloadAndPadding,
@@ -1323,231 +1310,163 @@ verify(PlainText, HashAlg, Sig, Key, _) ->
%%% Unit: bytes
--record(cipher_data, {
- key_bytes,
- iv_bytes,
- block_bytes
- }).
+-record(cipher, {
+ impl,
+ key_bytes,
+ iv_bytes,
+ block_bytes,
+ pkt_type = common
+ }).
%%% Start of a more parameterized crypto handling.
cipher('AEAD_AES_128_GCM') ->
- #cipher_data{key_bytes = 16,
- iv_bytes = 12,
- block_bytes = 16};
+ #cipher{impl = aes_128_gcm,
+ key_bytes = 16,
+ iv_bytes = 12,
+ block_bytes = 16,
+ pkt_type = aead};
cipher('AEAD_AES_256_GCM') ->
- #cipher_data{key_bytes = 32,
- iv_bytes = 12,
- block_bytes = 16};
+ #cipher{impl = aes_256_gcm,
+ key_bytes = 32,
+ iv_bytes = 12,
+ block_bytes = 16,
+ pkt_type = aead};
cipher('3des-cbc') ->
- #cipher_data{key_bytes = 24,
- iv_bytes = 8,
- block_bytes = 8};
+ #cipher{impl = des_ede3_cbc,
+ key_bytes = 24,
+ iv_bytes = 8,
+ block_bytes = 8};
cipher('aes128-cbc') ->
- #cipher_data{key_bytes = 16,
- iv_bytes = 16,
- block_bytes = 16};
+ #cipher{impl = aes_128_cbc,
+ key_bytes = 16,
+ iv_bytes = 16,
+ block_bytes = 16};
cipher('aes128-ctr') ->
- #cipher_data{key_bytes = 16,
- iv_bytes = 16,
- block_bytes = 16};
+ #cipher{impl = aes_128_ctr,
+ key_bytes = 16,
+ iv_bytes = 16,
+ block_bytes = 16};
cipher('aes192-ctr') ->
- #cipher_data{key_bytes = 24,
- iv_bytes = 16,
- block_bytes = 16};
+ #cipher{impl = aes_192_ctr,
+ key_bytes = 24,
+ iv_bytes = 16,
+ block_bytes = 16};
cipher('aes256-ctr') ->
- #cipher_data{key_bytes = 32,
- iv_bytes = 16,
- block_bytes = 16};
+ #cipher{impl = aes_256_ctr,
+ key_bytes = 32,
+ iv_bytes = 16,
+ block_bytes = 16};
cipher('[email protected]') -> % FIXME: Verify!!
- #cipher_data{key_bytes = 32,
- iv_bytes = 12,
- block_bytes = 8}.
-
+ #cipher{impl = chacha20_poly1305,
+ key_bytes = 32,
+ iv_bytes = 12,
+ block_bytes = 8,
+ pkt_type = aead};
+
+cipher(_) ->
+ #cipher{}.
+
+
+pkt_type(SshCipher) -> (cipher(SshCipher))#cipher.pkt_type.
+
+decrypt_magic(server) -> {"A", "C"};
+decrypt_magic(client) -> {"B", "D"}.
+
+encrypt_magic(client) -> decrypt_magic(server);
+encrypt_magic(server) -> decrypt_magic(client).
+
encrypt_init(#ssh{encrypt = none} = Ssh) ->
{ok, Ssh};
-encrypt_init(#ssh{encrypt = '[email protected]', role = client} = Ssh) ->
+
+encrypt_init(#ssh{encrypt = '[email protected]', role = Role} = Ssh) ->
%% [email protected] uses two independent crypto streams, one (chacha20)
%% for the length used in stream mode, and the other (chacha20-poly1305) as AEAD for
%% the payload and to MAC the length||payload.
%% See draft-josefsson-ssh-chacha20-poly1305-openssh-00
- <<K2:32/binary,K1:32/binary>> = hash(Ssh, "C", 512),
+ {_, KeyMagic} = encrypt_magic(Role),
+ <<K2:32/binary,K1:32/binary>> = hash(Ssh, KeyMagic, 8*64),
{ok, Ssh#ssh{encrypt_keys = {K1,K2}
% encrypt_block_size = 16, %default = 8. What to set it to? 64 (openssl chacha.h)
% ctx and iv is setup for each packet
}};
-encrypt_init(#ssh{encrypt = '[email protected]', role = server} = Ssh) ->
- <<K2:32/binary,K1:32/binary>> = hash(Ssh, "D", 512),
- {ok, Ssh#ssh{encrypt_keys = {K1,K2}
- % encrypt_block_size = 16, %default = 8. What to set it to?
- }};
-encrypt_init(#ssh{encrypt = 'AEAD_AES_128_GCM', role = client} = Ssh) ->
- IV = hash(Ssh, "A", 12*8),
- <<K:16/binary>> = hash(Ssh, "C", 128),
- {ok, Ssh#ssh{encrypt_keys = K,
- encrypt_block_size = 16,
- encrypt_ctx = IV}};
-encrypt_init(#ssh{encrypt = 'AEAD_AES_128_GCM', role = server} = Ssh) ->
- IV = hash(Ssh, "B", 12*8),
- <<K:16/binary>> = hash(Ssh, "D", 128),
- {ok, Ssh#ssh{encrypt_keys = K,
- encrypt_block_size = 16,
- encrypt_ctx = IV}};
-encrypt_init(#ssh{encrypt = 'AEAD_AES_256_GCM', role = client} = Ssh) ->
- IV = hash(Ssh, "A", 12*8),
- <<K:32/binary>> = hash(Ssh, "C", 256),
- {ok, Ssh#ssh{encrypt_keys = K,
- encrypt_block_size = 16,
- encrypt_ctx = IV}};
-encrypt_init(#ssh{encrypt = 'AEAD_AES_256_GCM', role = server} = Ssh) ->
- IV = hash(Ssh, "B", 12*8),
- <<K:32/binary>> = hash(Ssh, "D", 256),
- {ok, Ssh#ssh{encrypt_keys = K,
- encrypt_block_size = 16,
- encrypt_ctx = IV}};
-encrypt_init(#ssh{encrypt = '3des-cbc', role = client} = Ssh) ->
- IV = hash(Ssh, "A", 64),
- <<K1:8/binary, K2:8/binary, K3:8/binary>> = hash(Ssh, "C", 192),
- {ok, Ssh#ssh{encrypt_keys = {K1,K2,K3},
- encrypt_block_size = 8,
- encrypt_ctx = IV}};
-encrypt_init(#ssh{encrypt = '3des-cbc', role = server} = Ssh) ->
- IV = hash(Ssh, "B", 64),
- <<K1:8/binary, K2:8/binary, K3:8/binary>> = hash(Ssh, "D", 192),
- {ok, Ssh#ssh{encrypt_keys = {K1,K2,K3},
- encrypt_block_size = 8,
- encrypt_ctx = IV}};
-encrypt_init(#ssh{encrypt = 'aes128-cbc', role = client} = Ssh) ->
- IV = hash(Ssh, "A", 128),
- <<K:16/binary>> = hash(Ssh, "C", 128),
- {ok, Ssh#ssh{encrypt_keys = K,
- encrypt_block_size = 16,
+
+encrypt_init(#ssh{encrypt = SshCipher, role = Role} = Ssh) when SshCipher == 'AEAD_AES_128_GCM';
+ SshCipher == 'AEAD_AES_256_GCM' ->
+ {IvMagic, KeyMagic} = encrypt_magic(Role),
+ #cipher{impl = CryptoCipher,
+ key_bytes = KeyBytes,
+ iv_bytes = IvBytes,
+ block_bytes = BlockBytes} = cipher(SshCipher),
+ IV = hash(Ssh, IvMagic, 8*IvBytes),
+ K = hash(Ssh, KeyMagic, 8*KeyBytes),
+ {ok, Ssh#ssh{encrypt_cipher = CryptoCipher,
+ encrypt_keys = K,
+ encrypt_block_size = BlockBytes,
encrypt_ctx = IV}};
-encrypt_init(#ssh{encrypt = 'aes128-cbc', role = server} = Ssh) ->
- IV = hash(Ssh, "B", 128),
- <<K:16/binary>> = hash(Ssh, "D", 128),
- {ok, Ssh#ssh{encrypt_keys = K,
- encrypt_block_size = 16,
- encrypt_ctx = IV}};
-encrypt_init(#ssh{encrypt = 'aes128-ctr', role = client} = Ssh) ->
- IV = hash(Ssh, "A", 128),
- <<K:16/binary>> = hash(Ssh, "C", 128),
- State = crypto:stream_init(aes_ctr, K, IV),
- {ok, Ssh#ssh{encrypt_keys = K,
- encrypt_block_size = 16,
- encrypt_ctx = State}};
-encrypt_init(#ssh{encrypt = 'aes192-ctr', role = client} = Ssh) ->
- IV = hash(Ssh, "A", 128),
- <<K:24/binary>> = hash(Ssh, "C", 192),
- State = crypto:stream_init(aes_ctr, K, IV),
- {ok, Ssh#ssh{encrypt_keys = K,
- encrypt_block_size = 16,
- encrypt_ctx = State}};
-encrypt_init(#ssh{encrypt = 'aes256-ctr', role = client} = Ssh) ->
- IV = hash(Ssh, "A", 128),
- <<K:32/binary>> = hash(Ssh, "C", 256),
- State = crypto:stream_init(aes_ctr, K, IV),
- {ok, Ssh#ssh{encrypt_keys = K,
- encrypt_block_size = 16,
- encrypt_ctx = State}};
-encrypt_init(#ssh{encrypt = 'aes128-ctr', role = server} = Ssh) ->
- IV = hash(Ssh, "B", 128),
- <<K:16/binary>> = hash(Ssh, "D", 128),
- State = crypto:stream_init(aes_ctr, K, IV),
- {ok, Ssh#ssh{encrypt_keys = K,
- encrypt_block_size = 16,
- encrypt_ctx = State}};
-encrypt_init(#ssh{encrypt = 'aes192-ctr', role = server} = Ssh) ->
- IV = hash(Ssh, "B", 128),
- <<K:24/binary>> = hash(Ssh, "D", 192),
- State = crypto:stream_init(aes_ctr, K, IV),
- {ok, Ssh#ssh{encrypt_keys = K,
- encrypt_block_size = 16,
- encrypt_ctx = State}};
-encrypt_init(#ssh{encrypt = 'aes256-ctr', role = server} = Ssh) ->
- IV = hash(Ssh, "B", 128),
- <<K:32/binary>> = hash(Ssh, "D", 256),
- State = crypto:stream_init(aes_ctr, K, IV),
- {ok, Ssh#ssh{encrypt_keys = K,
- encrypt_block_size = 16,
- encrypt_ctx = State}}.
+
+encrypt_init(#ssh{encrypt = SshCipher, role = Role} = Ssh) ->
+ {IvMagic, KeyMagic} = encrypt_magic(Role),
+ #cipher{impl = CryptoCipher,
+ key_bytes = KeyBytes,
+ iv_bytes = IvBytes,
+ block_bytes = BlockBytes} = cipher(SshCipher),
+ IV = hash(Ssh, IvMagic, 8*IvBytes),
+ K = hash(Ssh, KeyMagic, 8*KeyBytes),
+ Ctx0 = crypto:crypto_init(CryptoCipher, K, IV, true),
+ {ok, Ssh#ssh{encrypt_cipher = CryptoCipher,
+ encrypt_block_size = BlockBytes,
+ encrypt_ctx = Ctx0}}.
encrypt_final(Ssh) ->
- {ok, Ssh#ssh{encrypt = none,
+ {ok, Ssh#ssh{encrypt = none,
encrypt_keys = undefined,
encrypt_block_size = 8,
encrypt_ctx = undefined
}}.
+
encrypt(#ssh{encrypt = none} = Ssh, Data) ->
{Ssh, Data};
+
encrypt(#ssh{encrypt = '[email protected]',
encrypt_keys = {K1,K2},
send_sequence = Seq} = Ssh,
<<LenData:4/binary, PayloadData/binary>>) ->
%% Encrypt length
IV1 = <<0:8/unit:8, Seq:8/unit:8>>,
- {_,EncLen} = crypto:stream_encrypt(crypto:stream_init(chacha20, K1, IV1),
- LenData),
+ EncLen = crypto:crypto_one_time(chacha20, K1, IV1, LenData, true),
%% Encrypt payload
IV2 = <<1:8/little-unit:8, Seq:8/unit:8>>,
- {_,EncPayloadData} = crypto:stream_encrypt(crypto:stream_init(chacha20, K2, IV2),
- PayloadData),
-
+ EncPayloadData = crypto:crypto_one_time(chacha20, K2, IV2, PayloadData, true),
%% MAC tag
- {_,PolyKey} = crypto:stream_encrypt(crypto:stream_init(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>),
- <<0:32/unit:8>>),
+ PolyKey = crypto:crypto_one_time(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>, <<0:32/unit:8>>, true),
EncBytes = <<EncLen/binary,EncPayloadData/binary>>,
Ctag = crypto:poly1305(PolyKey, EncBytes),
%% Result
{Ssh, {EncBytes,Ctag}};
-encrypt(#ssh{encrypt = 'AEAD_AES_128_GCM',
- encrypt_keys = K,
+
+encrypt(#ssh{encrypt = SshCipher,
+ encrypt_cipher = CryptoCipher,
+ encrypt_keys = K,
encrypt_ctx = IV0} = Ssh,
- <<LenData:4/binary, PayloadData/binary>>) ->
- {Ctext,Ctag} = crypto:block_encrypt(aes_gcm, K, IV0, {LenData,PayloadData}),
- IV = next_gcm_iv(IV0),
- {Ssh#ssh{encrypt_ctx = IV}, {<<LenData/binary,Ctext/binary>>,Ctag}};
-encrypt(#ssh{encrypt = 'AEAD_AES_256_GCM',
- encrypt_keys = K,
- encrypt_ctx = IV0} = Ssh,
- <<LenData:4/binary, PayloadData/binary>>) ->
- {Ctext,Ctag} = crypto:block_encrypt(aes_gcm, K, IV0, {LenData,PayloadData}),
+ <<LenData:4/binary, PayloadData/binary>>) when SshCipher == 'AEAD_AES_128_GCM' ;
+ SshCipher == 'AEAD_AES_256_GCM' ->
+ {Ctext,Ctag} = crypto:crypto_one_time_aead(CryptoCipher, K, IV0, PayloadData, LenData, true),
IV = next_gcm_iv(IV0),
{Ssh#ssh{encrypt_ctx = IV}, {<<LenData/binary,Ctext/binary>>,Ctag}};
-encrypt(#ssh{encrypt = '3des-cbc',
- encrypt_keys = {K1,K2,K3},
- encrypt_ctx = IV0} = Ssh, Data) ->
- Enc = crypto:block_encrypt(des3_cbc, [K1,K2,K3], IV0, Data),
- IV = crypto:next_iv(des3_cbc, Enc),
- {Ssh#ssh{encrypt_ctx = IV}, Enc};
-encrypt(#ssh{encrypt = 'aes128-cbc',
- encrypt_keys = K,
- encrypt_ctx = IV0} = Ssh, Data) ->
- Enc = crypto:block_encrypt(aes_cbc128, K,IV0,Data),
- IV = crypto:next_iv(aes_cbc, Enc),
- {Ssh#ssh{encrypt_ctx = IV}, Enc};
-encrypt(#ssh{encrypt = 'aes128-ctr',
- encrypt_ctx = State0} = Ssh, Data) ->
- {State, Enc} = crypto:stream_encrypt(State0,Data),
- {Ssh#ssh{encrypt_ctx = State}, Enc};
-encrypt(#ssh{encrypt = 'aes192-ctr',
- encrypt_ctx = State0} = Ssh, Data) ->
- {State, Enc} = crypto:stream_encrypt(State0,Data),
- {Ssh#ssh{encrypt_ctx = State}, Enc};
-encrypt(#ssh{encrypt = 'aes256-ctr',
- encrypt_ctx = State0} = Ssh, Data) ->
- {State, Enc} = crypto:stream_encrypt(State0,Data),
- {Ssh#ssh{encrypt_ctx = State}, Enc}.
-
+
+encrypt(#ssh{encrypt_ctx = Ctx0} = Ssh, Data) ->
+ Enc = crypto:crypto_update(Ctx0, Data),
+ {Ssh, Enc}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Decryption
@@ -1555,181 +1474,92 @@ encrypt(#ssh{encrypt = 'aes256-ctr',
decrypt_init(#ssh{decrypt = none} = Ssh) ->
{ok, Ssh};
-decrypt_init(#ssh{decrypt = '[email protected]', role = client} = Ssh) ->
- <<K2:32/binary,K1:32/binary>> = hash(Ssh, "D", 512),
- {ok, Ssh#ssh{decrypt_keys = {K1,K2}
- }};
-decrypt_init(#ssh{decrypt = '[email protected]', role = server} = Ssh) ->
- <<K2:32/binary,K1:32/binary>> = hash(Ssh, "C", 512),
+
+decrypt_init(#ssh{decrypt = '[email protected]', role = Role} = Ssh) ->
+ {_, KeyMagic} = decrypt_magic(Role),
+ <<K2:32/binary,K1:32/binary>> = hash(Ssh, KeyMagic, 8*64),
{ok, Ssh#ssh{decrypt_keys = {K1,K2}
}};
-decrypt_init(#ssh{decrypt = 'AEAD_AES_128_GCM', role = client} = Ssh) ->
- IV = hash(Ssh, "B", 12*8),
- <<K:16/binary>> = hash(Ssh, "D", 128),
- {ok, Ssh#ssh{decrypt_keys = K,
- decrypt_block_size = 16,
- decrypt_ctx = IV}};
-decrypt_init(#ssh{decrypt = 'AEAD_AES_128_GCM', role = server} = Ssh) ->
- IV = hash(Ssh, "A", 12*8),
- <<K:16/binary>> = hash(Ssh, "C", 128),
- {ok, Ssh#ssh{decrypt_keys = K,
- decrypt_block_size = 16,
- decrypt_ctx = IV}};
-decrypt_init(#ssh{decrypt = 'AEAD_AES_256_GCM', role = client} = Ssh) ->
- IV = hash(Ssh, "B", 12*8),
- <<K:32/binary>> = hash(Ssh, "D", 256),
- {ok, Ssh#ssh{decrypt_keys = K,
- decrypt_block_size = 16,
- decrypt_ctx = IV}};
-decrypt_init(#ssh{decrypt = 'AEAD_AES_256_GCM', role = server} = Ssh) ->
- IV = hash(Ssh, "A", 12*8),
- <<K:32/binary>> = hash(Ssh, "C", 256),
- {ok, Ssh#ssh{decrypt_keys = K,
- decrypt_block_size = 16,
+
+decrypt_init(#ssh{decrypt = SshCipher, role = Role} = Ssh) when SshCipher == 'AEAD_AES_128_GCM';
+ SshCipher == 'AEAD_AES_256_GCM' ->
+ {IvMagic, KeyMagic} = decrypt_magic(Role),
+ #cipher{impl = CryptoCipher,
+ key_bytes = KeyBytes,
+ iv_bytes = IvBytes,
+ block_bytes = BlockBytes} = cipher(SshCipher),
+ IV = hash(Ssh, IvMagic, 8*IvBytes),
+ K = hash(Ssh, KeyMagic, 8*KeyBytes),
+ {ok, Ssh#ssh{decrypt_cipher = CryptoCipher,
+ decrypt_keys = K,
+ decrypt_block_size = BlockBytes,
decrypt_ctx = IV}};
-decrypt_init(#ssh{decrypt = '3des-cbc', role = client} = Ssh) ->
- {IV, KD} = {hash(Ssh, "B", 64),
- hash(Ssh, "D", 192)},
- <<K1:8/binary, K2:8/binary, K3:8/binary>> = KD,
- {ok, Ssh#ssh{decrypt_keys = {K1,K2,K3}, decrypt_ctx = IV,
- decrypt_block_size = 8}};
-decrypt_init(#ssh{decrypt = '3des-cbc', role = server} = Ssh) ->
- {IV, KD} = {hash(Ssh, "A", 64),
- hash(Ssh, "C", 192)},
- <<K1:8/binary, K2:8/binary, K3:8/binary>> = KD,
- {ok, Ssh#ssh{decrypt_keys = {K1, K2, K3}, decrypt_ctx = IV,
- decrypt_block_size = 8}};
-decrypt_init(#ssh{decrypt = 'aes128-cbc', role = client} = Ssh) ->
- {IV, KD} = {hash(Ssh, "B", 128),
- hash(Ssh, "D", 128)},
- <<K:16/binary>> = KD,
- {ok, Ssh#ssh{decrypt_keys = K, decrypt_ctx = IV,
- decrypt_block_size = 16}};
-decrypt_init(#ssh{decrypt = 'aes128-cbc', role = server} = Ssh) ->
- {IV, KD} = {hash(Ssh, "A", 128),
- hash(Ssh, "C", 128)},
- <<K:16/binary>> = KD,
- {ok, Ssh#ssh{decrypt_keys = K, decrypt_ctx = IV,
- decrypt_block_size = 16}};
-decrypt_init(#ssh{decrypt = 'aes128-ctr', role = client} = Ssh) ->
- IV = hash(Ssh, "B", 128),
- <<K:16/binary>> = hash(Ssh, "D", 128),
- State = crypto:stream_init(aes_ctr, K, IV),
- {ok, Ssh#ssh{decrypt_keys = K,
- decrypt_block_size = 16,
- decrypt_ctx = State}};
-decrypt_init(#ssh{decrypt = 'aes192-ctr', role = client} = Ssh) ->
- IV = hash(Ssh, "B", 128),
- <<K:24/binary>> = hash(Ssh, "D", 192),
- State = crypto:stream_init(aes_ctr, K, IV),
- {ok, Ssh#ssh{decrypt_keys = K,
- decrypt_block_size = 16,
- decrypt_ctx = State}};
-decrypt_init(#ssh{decrypt = 'aes256-ctr', role = client} = Ssh) ->
- IV = hash(Ssh, "B", 128),
- <<K:32/binary>> = hash(Ssh, "D", 256),
- State = crypto:stream_init(aes_ctr, K, IV),
- {ok, Ssh#ssh{decrypt_keys = K,
- decrypt_block_size = 16,
- decrypt_ctx = State}};
-decrypt_init(#ssh{decrypt = 'aes128-ctr', role = server} = Ssh) ->
- IV = hash(Ssh, "A", 128),
- <<K:16/binary>> = hash(Ssh, "C", 128),
- State = crypto:stream_init(aes_ctr, K, IV),
- {ok, Ssh#ssh{decrypt_keys = K,
- decrypt_block_size = 16,
- decrypt_ctx = State}};
-decrypt_init(#ssh{decrypt = 'aes192-ctr', role = server} = Ssh) ->
- IV = hash(Ssh, "A", 128),
- <<K:24/binary>> = hash(Ssh, "C", 192),
- State = crypto:stream_init(aes_ctr, K, IV),
- {ok, Ssh#ssh{decrypt_keys = K,
- decrypt_block_size = 16,
- decrypt_ctx = State}};
-decrypt_init(#ssh{decrypt = 'aes256-ctr', role = server} = Ssh) ->
- IV = hash(Ssh, "A", 128),
- <<K:32/binary>> = hash(Ssh, "C", 256),
- State = crypto:stream_init(aes_ctr, K, IV),
- {ok, Ssh#ssh{decrypt_keys = K,
- decrypt_block_size = 16,
- decrypt_ctx = State}}.
-
-
+
+decrypt_init(#ssh{decrypt = SshCipher, role = Role} = Ssh) ->
+ {IvMagic, KeyMagic} = decrypt_magic(Role),
+ #cipher{impl = CryptoCipher,
+ key_bytes = KeyBytes,
+ iv_bytes = IvBytes,
+ block_bytes = BlockBytes} = cipher(SshCipher),
+ IV = hash(Ssh, IvMagic, 8*IvBytes),
+ K = hash(Ssh, KeyMagic, 8*KeyBytes),
+ Ctx0 = crypto:crypto_init(CryptoCipher, K, IV, false),
+ {ok, Ssh#ssh{decrypt_cipher = CryptoCipher,
+ decrypt_block_size = BlockBytes,
+ decrypt_ctx = Ctx0}}.
+
+
decrypt_final(Ssh) ->
{ok, Ssh#ssh {decrypt = none,
decrypt_keys = undefined,
decrypt_ctx = undefined,
decrypt_block_size = 8}}.
+
decrypt(Ssh, <<>>) ->
{Ssh, <<>>};
+
decrypt(#ssh{decrypt = '[email protected]',
- decrypt_keys = {K1,_K2},
- recv_sequence = Seq} = Ssh, {length,EncryptedLen}) ->
- {_State,PacketLenBin} =
- crypto:stream_decrypt(crypto:stream_init(chacha20, K1, <<0:8/unit:8, Seq:8/unit:8>>),
- EncryptedLen),
- {Ssh, PacketLenBin};
-decrypt(#ssh{decrypt = '[email protected]',
- decrypt_keys = {_K1,K2},
- recv_sequence = Seq} = Ssh, {AAD,Ctext,Ctag}) ->
- %% The length is already decoded and used to divide the input
- %% Check the mac (important that it is timing-safe):
- {_,PolyKey} =
- crypto:stream_encrypt(crypto:stream_init(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>),
- <<0:32/unit:8>>),
- case equal_const_time(Ctag, crypto:poly1305(PolyKey, <<AAD/binary,Ctext/binary>>)) of
- true ->
- %% MAC is ok, decode
- IV2 = <<1:8/little-unit:8, Seq:8/unit:8>>,
- {_,PlainText} =
- crypto:stream_decrypt(crypto:stream_init(chacha20,K2,IV2), Ctext),
- {Ssh, PlainText};
- false ->
- {Ssh,error}
+ decrypt_keys = {K1,K2},
+ recv_sequence = Seq} = Ssh, Data) ->
+ case Data of
+ {length,EncryptedLen} ->
+ %% The length is decrypted separately in a first step
+ PacketLenBin = crypto:crypto_one_time(chacha20, K1, <<0:8/unit:8, Seq:8/unit:8>>, EncryptedLen, false),
+ {Ssh, PacketLenBin};
+ {AAD,Ctext,Ctag} ->
+ %% The length is already decrypted and used to divide the input
+ %% Check the mac (important that it is timing-safe):
+ PolyKey = crypto:crypto_one_time(chacha20, K2, <<0:8/unit:8,Seq:8/unit:8>>, <<0:32/unit:8>>, false),
+ case equal_const_time(Ctag, crypto:poly1305(PolyKey, <<AAD/binary,Ctext/binary>>)) of
+ true ->
+ %% MAC is ok, decode
+ IV2 = <<1:8/little-unit:8, Seq:8/unit:8>>,
+ PlainText = crypto:crypto_one_time(chacha20, K2, IV2, Ctext, false),
+ {Ssh, PlainText};
+ false ->
+ {Ssh,error}
+ end
end;
+
decrypt(#ssh{decrypt = none} = Ssh, Data) ->
{Ssh, Data};
-decrypt(#ssh{decrypt = 'AEAD_AES_128_GCM',
- decrypt_keys = K,
- decrypt_ctx = IV0} = Ssh, Data = {_AAD,_Ctext,_Ctag}) ->
- Dec = crypto:block_decrypt(aes_gcm, K, IV0, Data), % Dec = PlainText | error
- IV = next_gcm_iv(IV0),
- {Ssh#ssh{decrypt_ctx = IV}, Dec};
-decrypt(#ssh{decrypt = 'AEAD_AES_256_GCM',
+
+decrypt(#ssh{decrypt = SshCipher,
+ decrypt_cipher = CryptoCipher,
decrypt_keys = K,
- decrypt_ctx = IV0} = Ssh, Data = {_AAD,_Ctext,_Ctag}) ->
- Dec = crypto:block_decrypt(aes_gcm, K, IV0, Data), % Dec = PlainText | error
+ decrypt_ctx = IV0} = Ssh, {AAD,Ctext,Ctag}) when SshCipher == 'AEAD_AES_128_GCM' ;
+ SshCipher == 'AEAD_AES_256_GCM' ->
+ Dec = crypto:crypto_one_time_aead(CryptoCipher, K, IV0, Ctext, AAD, Ctag, false),
IV = next_gcm_iv(IV0),
{Ssh#ssh{decrypt_ctx = IV}, Dec};
-decrypt(#ssh{decrypt = '3des-cbc', decrypt_keys = Keys,
- decrypt_ctx = IV0} = Ssh, Data) ->
- {K1, K2, K3} = Keys,
- Dec = crypto:block_decrypt(des3_cbc, [K1,K2,K3], IV0, Data),
- IV = crypto:next_iv(des3_cbc, Data),
- {Ssh#ssh{decrypt_ctx = IV}, Dec};
-decrypt(#ssh{decrypt = 'aes128-cbc', decrypt_keys = Key,
- decrypt_ctx = IV0} = Ssh, Data) ->
- Dec = crypto:block_decrypt(aes_cbc128, Key,IV0,Data),
- IV = crypto:next_iv(aes_cbc, Data),
- {Ssh#ssh{decrypt_ctx = IV}, Dec};
-decrypt(#ssh{decrypt = 'aes128-ctr',
- decrypt_ctx = State0} = Ssh, Data) ->
- {State, Enc} = crypto:stream_decrypt(State0,Data),
- {Ssh#ssh{decrypt_ctx = State}, Enc};
-decrypt(#ssh{decrypt = 'aes192-ctr',
- decrypt_ctx = State0} = Ssh, Data) ->
- {State, Enc} = crypto:stream_decrypt(State0,Data),
- {Ssh#ssh{decrypt_ctx = State}, Enc};
-decrypt(#ssh{decrypt = 'aes256-ctr',
- decrypt_ctx = State0} = Ssh, Data) ->
- {State, Enc} = crypto:stream_decrypt(State0,Data),
- {Ssh#ssh{decrypt_ctx = State}, Enc}.
+decrypt(#ssh{decrypt_ctx = Ctx0} = Ssh, Data) ->
+ Dec = crypto:crypto_update(Ctx0, Data),
+ {Ssh, Dec}.
next_gcm_iv(<<Fixed:32, InvCtr:64>>) -> <<Fixed:32, (InvCtr+1):64>>.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Compression
%%
@@ -2058,9 +1888,9 @@ compute_key(Algorithm, OthersPublic, MyPrivate, Args) ->
dh_bits(#alg{encrypt = Encrypt,
send_mac = SendMac}) ->
C = cipher(Encrypt),
- 8 * lists:max([C#cipher_data.key_bytes,
- C#cipher_data.block_bytes,
- C#cipher_data.iv_bytes,
+ 8 * lists:max([C#cipher.key_bytes,
+ C#cipher.block_bytes,
+ C#cipher.iv_bytes,
mac_key_bytes(SendMac)
]).
@@ -2091,40 +1921,13 @@ select_crypto_supported(L) ->
crypto_supported(Conditions, Supported) ->
lists:all( fun({Tag,CryptoName}) when is_atom(CryptoName) ->
- crypto_name_supported(Tag,CryptoName,Supported);
- ({Tag,{Name,Len}}) when is_integer(Len) ->
- crypto_name_supported(Tag,Name,Supported) andalso
- len_supported(Name,Len)
+ crypto_name_supported(Tag,CryptoName,Supported)
end, Conditions).
crypto_name_supported(Tag, CryptoName, Supported) ->
- Vs = case proplists:get_value(Tag,Supported,[]) of
- [] when Tag == curves -> crypto:ec_curves();
- L -> L
- end,
+ Vs = proplists:get_value(Tag,Supported,[]),
lists:member(CryptoName, Vs).
-len_supported(Name, Len) ->
- try
- case Name of
- aes_ctr ->
- {_, <<_/binary>>} =
- %% Test encryption
- crypto:stream_encrypt(crypto:stream_init(Name, <<0:Len>>, <<0:128>>), <<"">>);
- aes_gcm ->
- {<<_/binary>>, <<_/binary>>} =
- crypto:block_encrypt(Name,
- _Key = <<0:Len>>,
- _IV = <<0:12/unsigned-unit:8>>,
- {<<"AAD">>,"PT"})
- end
- of
- _ -> true
- catch
- _:_ -> false
- end.
-
-
same(Algs) -> [{client2server,Algs}, {server2client,Algs}].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/ssh/src/ssh_userauth.hrl b/lib/ssh/src/ssh_userauth.hrl
deleted file mode 100644
index 2cfc1f0f83..0000000000
--- a/lib/ssh/src/ssh_userauth.hrl
+++ /dev/null
@@ -1,78 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
-%%
-%% 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.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-
-%%% Description: user authentication protocol
-
--define(SSH_MSG_USERAUTH_REQUEST, 50).
--define(SSH_MSG_USERAUTH_FAILURE, 51).
--define(SSH_MSG_USERAUTH_SUCCESS, 52).
--define(SSH_MSG_USERAUTH_BANNER, 53).
--define(SSH_MSG_USERAUTH_PK_OK, 60).
--define(SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 60).
--define(SSH_MSG_USERAUTH_INFO_REQUEST, 60).
--define(SSH_MSG_USERAUTH_INFO_RESPONSE, 61).
-
--record(ssh_msg_userauth_request,
- {
- user, %% string
- service, %% string
- method, %% string "publickey", "password"
- data %% opaque
- }).
-
--record(ssh_msg_userauth_failure,
- {
- authentications, %% string
- partial_success %% boolean
- }).
-
--record(ssh_msg_userauth_success,
- {
- }).
-
--record(ssh_msg_userauth_banner,
- {
- message, %% string
- language %% string
- }).
-
--record(ssh_msg_userauth_passwd_changereq,
- {
- prompt, %% string
- languge %% string
- }).
-
--record(ssh_msg_userauth_pk_ok,
- {
- algorithm_name, % string
- key_blob % string
- }).
-
--record(ssh_msg_userauth_info_request,
- {name,
- instruction,
- language_tag,
- num_prompts,
- data}).
--record(ssh_msg_userauth_info_response,
- {num_responses,
- data}).
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 5de6d52092..9b987dea5a 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -1399,7 +1399,7 @@ rekey_chk(Config, RLdaemon, RLclient) ->
Kex1 = ssh_test_lib:get_kex_init(ConnectionRef),
%% Make both sides send something:
- {ok, SftpPid} = ssh_sftp:start_channel(ConnectionRef),
+ {ok, _SftpPid} = ssh_sftp:start_channel(ConnectionRef),
%% Check rekeying
timer:sleep(?REKEY_DATA_TMO),
diff --git a/lib/ssh/test/ssh_bench_SUITE.erl b/lib/ssh/test/ssh_bench_SUITE.erl
index 2ac4e5636a..5ff7a71c45 100644
--- a/lib/ssh/test/ssh_bench_SUITE.erl
+++ b/lib/ssh/test/ssh_bench_SUITE.erl
@@ -26,7 +26,7 @@
-include_lib("ssh/src/ssh.hrl").
-include_lib("ssh/src/ssh_transport.hrl").
-include_lib("ssh/src/ssh_connect.hrl").
--include_lib("ssh/src/ssh_userauth.hrl").
+-include_lib("ssh/src/ssh_auth.hrl").
%%%================================================================
%%%
@@ -178,7 +178,7 @@ gen_data(DataSz) ->
connect_measure(Port, Cipher, Mac, Data, Options) ->
- AES_GCM = {cipher,
+ _AES_GCM = {cipher,
[]},
@@ -187,22 +187,22 @@ connect_measure(Port, Cipher, Mac, Data, Options) ->
{none,none} ->
[{modify_algorithms,[{prepend, [{cipher,[Cipher]},
{mac,[Mac]}]}
-%%% ,{rm,[AES_GCM]}
+%%% ,{rm,[_AES_GCM]}
]}];
{none,_} ->
[{modify_algorithms,[{prepend, [{cipher,[Cipher]}]}
-%%% ,{rm,[AES_GCM]}
+%%% ,{rm,[_AES_GCM]}
]},
{preferred_algorithms, [{mac,[Mac]}]}];
{_,none} ->
[{modify_algorithms,[{prepend, [{mac,[Mac]}]}
-%%% ,{rm,[AES_GCM]}
+%%% ,{rm,[_AES_GCM]}
]},
{preferred_algorithms, [{cipher,[Cipher]}]}];
_ ->
[{preferred_algorithms, [{cipher,[Cipher]},
{mac,[Mac]}]}
-%%% ,{modify_algorithms, [{rm,[AES_GCM]}]}
+%%% ,{modify_algorithms, [{rm,[_AES_GCM]}]}
]
end,
Times =
diff --git a/lib/ssh/test/ssh_chan_behaviours_SUITE.erl b/lib/ssh/test/ssh_chan_behaviours_SUITE.erl
index 16ed152bcd..103d7253fd 100644
--- a/lib/ssh/test/ssh_chan_behaviours_SUITE.erl
+++ b/lib/ssh/test/ssh_chan_behaviours_SUITE.erl
@@ -128,8 +128,8 @@ subsystem_client(Config) ->
C = proplists:get_value(connref, Config),
{ok,ChRef} = ssh_chan_behaviours_client:start_link(C),
- IDclt = ?EXPECT({{C,Ch1clt}, {ssh_channel_up,Ch1clt,C}}, {C,Ch1clt}),
- IDsrv = ?EXPECT({{_Csrv,Ch1srv}, {ssh_channel_up,Ch1srv,_Csrv}}, {_Csrv,Ch1srv}),
+ IDclt = ?EXPECT({{C,_Ch1clt}, {ssh_channel_up,_Ch1clt,C}}, {C,_Ch1clt}),
+ IDsrv = ?EXPECT({{_Csrv,_Ch1srv}, {ssh_channel_up,_Ch1srv,_Csrv}}, {_Csrv,_Ch1srv}),
ok = ssh_chan_behaviours_client:stop(ChRef),
?EXPECT({IDclt, {terminate,normal}}, []), % From the proper channel handler
diff --git a/lib/ssh/test/ssh_chan_behaviours_client.erl b/lib/ssh/test/ssh_chan_behaviours_client.erl
index 15f17733d6..8dd18973ad 100644
--- a/lib/ssh/test/ssh_chan_behaviours_client.erl
+++ b/lib/ssh/test/ssh_chan_behaviours_client.erl
@@ -94,7 +94,7 @@ handle_ssh_msg({ssh_cm, C, {eof, Ch}}=M, #state{ch=Ch,cm=C} = State) ->
?DBG(State, "eof",[]),
{ok, State};
-handle_ssh_msg({ssh_cm, C, {signal, _Ch, _SigNameStr}=Sig} = M, #state{ch=Ch,cm=C} = State) ->
+handle_ssh_msg({ssh_cm, C, {signal, Ch, _SigNameStr}=Sig} = M, #state{ch=Ch,cm=C} = State) ->
%% Ignore signals according to RFC 4254 section 6.9.
tell_parent(M, State),
?DBG(State, "~p",[Sig]),
diff --git a/lib/ssh/test/ssh_chan_behaviours_server.erl b/lib/ssh/test/ssh_chan_behaviours_server.erl
index 1408675a6e..1d504b1bc6 100644
--- a/lib/ssh/test/ssh_chan_behaviours_server.erl
+++ b/lib/ssh/test/ssh_chan_behaviours_server.erl
@@ -65,7 +65,7 @@ handle_ssh_msg({ssh_cm, C, {eof, Ch}}=M, #state{ch=Ch,cm=C} = State) ->
?DBG(State, "eof",[]),
{ok, State};
-handle_ssh_msg({ssh_cm, C, {signal, _Ch, _SigNameStr}=Sig} = M, #state{ch=Ch,cm=C} = State) ->
+handle_ssh_msg({ssh_cm, C, {signal, Ch, _SigNameStr}=Sig} = M, #state{ch=Ch,cm=C} = State) ->
%% Ignore signals according to RFC 4254 section 6.9.
tell_parent(M, State),
?DBG(State, "~p",[Sig]),
diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl
index 8e82527c6e..06ed9082cf 100644
--- a/lib/ssh/test/ssh_compat_SUITE.erl
+++ b/lib/ssh/test/ssh_compat_SUITE.erl
@@ -150,8 +150,7 @@ init_per_group(G, Config0) ->
stop_docker(ID),
{fail, "Can't contact docker sshd"}
catch
- Class:Exc ->
- ST = erlang:get_stacktrace(),
+ Class:Exc:ST ->
ct:log("common_algs: ~p:~p~n~p",[Class,Exc,ST]),
stop_docker(ID),
{fail, "Failed during setup"}
@@ -160,8 +159,7 @@ init_per_group(G, Config0) ->
cant_start_docker ->
{skip, "Can't start docker"};
- C:E ->
- ST = erlang:get_stacktrace(),
+ C:E:ST ->
ct:log("No ~p~n~p:~p~n~p",[G,C,E,ST]),
{skip, "Can't start docker"}
end;
@@ -1026,8 +1024,7 @@ receive_hello(S) ->
Result ->
Result
catch
- Class:Error ->
- ST = erlang:get_stacktrace(),
+ Class:Error:ST ->
{error, {Class,Error,ST}}
end.
@@ -1104,8 +1101,7 @@ sftp_tests_erl_server(Config, ServerIP, ServerPort, ServerRootDir, UserDir) ->
call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir),
check_local_directory(ServerRootDir)
catch
- Class:Error ->
- ST = erlang:get_stacktrace(),
+ Class:Error:ST ->
{error, {Class,Error,ST}}
end.
@@ -1133,7 +1129,7 @@ check_local_directory(ServerRootDir) ->
check_local_directory(ServerRootDir, SleepTime, N) ->
case do_check_local_directory(ServerRootDir) of
- {error,Error} when N>0 ->
+ {error,_Error} when N>0 ->
%% Could be that the erlang side is faster and the docker's operations
%% are not yet finalized.
%% Sleep for a while and retry a few times:
@@ -1347,8 +1343,7 @@ one_test_erl_client(SFTP, Id, C) when SFTP==sftp ; SFTP==sftp_async ->
catch ssh_sftp:stop_channel(Ch),
R
catch
- Class:Error ->
- ST = erlang:get_stacktrace(),
+ Class:Error:ST ->
{error, {SFTP,Id,Class,Error,ST}}
end.
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index 778e4a5fc8..6aa587dc7f 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -1124,12 +1124,12 @@ start_our_shell(_User, _Peer) ->
ssh_exec_echo(Cmd) ->
spawn(fun() ->
- io:format("echo "++Cmd ++ "\n")
+ io:format("echo ~s\n", [Cmd])
end).
ssh_exec_echo(Cmd, User) ->
spawn(fun() ->
- io:format(io_lib:format("echo ~s ~s\n",[User,Cmd]))
+ io:format("echo ~s ~s\n",[User,Cmd])
end).
ssh_exec_echo(Cmd, User, _PeerAddr) ->
ssh_exec_echo(Cmd,User).
diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl
index 60d0da2a39..bf90f74324 100644
--- a/lib/ssh/test/ssh_options_SUITE.erl
+++ b/lib/ssh/test/ssh_options_SUITE.erl
@@ -214,7 +214,7 @@ init_per_testcase(_TestCase, Config) ->
file:make_dir(UserDir),
[{user_dir,UserDir}|Config].
-end_per_testcase(_TestCase, Config) ->
+end_per_testcase(_TestCase, _Config) ->
ssh:stop(),
ok.
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index 3e3e151781..b12ddfeef6 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -38,7 +38,11 @@
-define(EXTRA_KEX, 'diffie-hellman-group1-sha1').
-define(CIPHERS, ['aes256-ctr','aes192-ctr','aes128-ctr','aes128-cbc','3des-cbc']).
--define(DEFAULT_CIPHERS, [{client2server,?CIPHERS}, {server2client,?CIPHERS}]).
+-define(DEFAULT_CIPHERS, (fun() -> Ciphs = filter_supported(cipher, ?CIPHERS),
+ [{client2server,Ciphs}, {server2client,Ciphs}]
+ end)()
+ ).
+
-define(v(Key, Config), proplists:get_value(Key, Config)).
-define(v(Key, Config, Default), proplists:get_value(Key, Config, Default)).
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index a1a7eebcde..1129303414 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -409,7 +409,7 @@ ct:log("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file
setup_ecdsa_auth_keys(Size, DataDir, UserDir).
setup_eddsa(Alg, DataDir, UserDir) ->
- {IdPriv, IdPub, HostPriv, HostPub} =
+ {IdPriv, _IdPub, HostPriv, HostPub} =
case Alg of
ed25519 -> {"id_ed25519", "id_ed25519.pub", "ssh_host_ed25519_key", "ssh_host_ed25519_key.pub"};
ed448 -> {"id_ed448", "id_ed448.pub", "ssh_host_ed448_key", "ssh_host_ed448_key.pub"}
@@ -970,7 +970,7 @@ expected_state(_) -> false.
%%%----------------------------------------------------------------
%%% Return a string with N random characters
%%%
-random_chars(N) -> [crypto:rand_uniform($a,$z) || _<-lists:duplicate(N,x)].
+random_chars(N) -> [($a-1)+rand:uniform($z-$a) || _<-lists:duplicate(N,x)].
create_random_dir(Config) ->
diff --git a/lib/ssh/test/ssh_trpt_test_lib.erl b/lib/ssh/test/ssh_trpt_test_lib.erl
index f2c9892f95..3f4df2c986 100644
--- a/lib/ssh/test/ssh_trpt_test_lib.erl
+++ b/lib/ssh/test/ssh_trpt_test_lib.erl
@@ -570,75 +570,6 @@ receive_binary_msg(S0=#s{}) ->
-old_receive_binary_msg(S0=#s{ssh=C0=#ssh{decrypt_block_size = BlockSize,
- recv_mac_size = MacSize
- }
- }) ->
- case size(S0#s.encrypted_data_buffer) >= max(8,BlockSize) of
- false ->
- %% Need more bytes to decode the packet_length field
- Remaining = max(8,BlockSize) - size(S0#s.encrypted_data_buffer),
- receive_binary_msg( receive_wait(Remaining, S0) );
- true ->
- %% Has enough bytes to decode the packet_length field
- {_, <<?UINT32(PacketLen), _/binary>>, _} =
- ssh_transport:decrypt_blocks(S0#s.encrypted_data_buffer, BlockSize, C0), % FIXME: BlockSize should be at least 4
-
- %% FIXME: Check that ((4+PacketLen) rem BlockSize) == 0 ?
-
- S1 = if
- PacketLen > ?SSH_MAX_PACKET_SIZE ->
- fail({too_large_message,PacketLen},S0); % FIXME: disconnect
-
- ((4+PacketLen) rem BlockSize) =/= 0 ->
- fail(bad_packet_length_modulo, S0); % FIXME: disconnect
-
- size(S0#s.encrypted_data_buffer) >= (4 + PacketLen + MacSize) ->
- %% has the whole packet
- S0;
-
- true ->
- %% need more bytes to get have the whole packet
- Remaining = (4 + PacketLen + MacSize) - size(S0#s.encrypted_data_buffer),
- receive_wait(Remaining, S0)
- end,
-
- %% Decrypt all, including the packet_length part (re-use the initial #ssh{})
- {C1, SshPacket = <<?UINT32(_),?BYTE(PadLen),Tail/binary>>, EncRest} =
- ssh_transport:decrypt_blocks(S1#s.encrypted_data_buffer, PacketLen+4, C0),
-
- PayloadLen = PacketLen - 1 - PadLen,
- <<CompressedPayload:PayloadLen/binary, _Padding:PadLen/binary>> = Tail,
-
- {C2, Payload} = ssh_transport:decompress(C1, CompressedPayload),
-
- <<Mac:MacSize/binary, Rest/binary>> = EncRest,
-
- case {ssh_transport:is_valid_mac(Mac, SshPacket, C2),
- catch ssh_message:decode(set_prefix_if_trouble(Payload,S1))}
- of
- {false, _} -> fail(bad_mac,S1);
- {_, {'EXIT',_}} -> fail(decode_failed,S1);
-
- {true, Msg} ->
- C3 = case Msg of
- #ssh_msg_kexinit{} ->
- ssh_transport:key_init(opposite_role(C2), C2, Payload);
- _ ->
- C2
- end,
- S2 = opt(print_messages, S1,
- fun(X) when X==true;X==detail -> {"Recv~n~s~n",[format_msg(Msg)]} end),
- S3 = opt(print_messages, S2,
- fun(detail) -> {"decrypted bytes ~p~n",[SshPacket]} end),
- S3#s{ssh = inc_recv_seq_num(C3),
- encrypted_data_buffer = Rest,
- return_value = Msg
- }
- end
- end.
-
-
set_prefix_if_trouble(Msg = <<?BYTE(Op),_/binary>>, #s{alg=#alg{kex=Kex}})
when Op == 30;
Op == 31
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 837da27ab0..bb87dd388c 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,4 +1,4 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.7.6
+SSH_VSN = 4.7.7
APP_VSN = "ssh-$(SSH_VSN)"