aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/src')
-rw-r--r--lib/ssh/src/Makefile2
-rw-r--r--lib/ssh/src/ssh.appup.src12
-rw-r--r--lib/ssh/src/ssh.erl1
-rw-r--r--lib/ssh/src/ssh_connect.hrl4
-rw-r--r--lib/ssh/src/ssh_connection.erl82
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl52
-rw-r--r--lib/ssh/src/ssh_sftp.erl316
-rw-r--r--lib/ssh/src/ssh_xfer.erl9
8 files changed, 416 insertions, 62 deletions
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index 04ce750371..90d71107ad 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -116,7 +116,7 @@ $(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES)
debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
- rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(BEHAVIOUR_TARGET_FILES)
rm -f errs core *~
$(APP_TARGET): $(APP_SRC) ../vsn.mk
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 600c01454c..70f1cc487c 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -19,6 +19,12 @@
{"%VSN%",
[
+ {"3.0.8", [{load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_sftp, soft_purge, soft_purge, [erl_tar,ssh_xfer]},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_xfer, soft_purge, soft_purge, []}
+ ]},
{"3.0.7", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
{load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
{load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
@@ -42,6 +48,12 @@
{<<".*">>, [{restart_application, ssh}]}
],
[
+ {"3.0.8", [{load_module, ssh_connection, soft_purge, soft_purge, [ssh_connection_handler]},
+ {load_module, ssh_sftp, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_handler, soft_purge, soft_purge, []},
+ {load_module, ssh, soft_purge, soft_purge, []},
+ {load_module, ssh_xfer, soft_purge, soft_purge, []}
+ ]},
{"3.0.7", [{load_module, ssh_auth, soft_purge, soft_purge, [ssh_connection_handler]},
{load_module, ssh_acceptor, soft_purge, soft_purge, [ssh_connection_handler]},
{load_module, ssh_channel, soft_purge, soft_purge, [ssh_connection_handler]},
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index de047d3c83..eae33e3683 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -194,6 +194,7 @@ shell(Host, Port, Options) ->
{ok, ConnectionRef} ->
case ssh_connection:session_channel(ConnectionRef, infinity) of
{ok,ChannelId} ->
+ success = ssh_connection:ptty_alloc(ConnectionRef, ChannelId, []),
Args = [{channel_cb, ssh_shell},
{init_args,[ConnectionRef, ChannelId]},
{cm, ConnectionRef}, {channel_id, ChannelId}],
diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl
index 9307dbbad0..d14f7ce27d 100644
--- a/lib/ssh/src/ssh_connect.hrl
+++ b/lib/ssh/src/ssh_connect.hrl
@@ -165,6 +165,10 @@
recipient_channel
}).
+-define(TERMINAL_WIDTH, 80).
+-define(TERMINAL_HEIGHT, 24).
+-define(DEFAULT_TERMINAL, "vt100").
+
-define(TTY_OP_END,0). %% Indicates end of options.
-define(VINTR,1). %% Interrupt character; 255 if none. Similarly for the
%% other characters. Not all of these characters are
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index 83bfad7900..593443e11c 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -32,11 +32,11 @@
%% API
-export([session_channel/2, session_channel/4,
exec/4, shell/2, subsystem/4, send/3, send/4, send/5,
- send_eof/2, adjust_window/3, setenv/5, close/2, reply_request/4]).
+ send_eof/2, adjust_window/3, setenv/5, close/2, reply_request/4,
+ ptty_alloc/3, ptty_alloc/4]).
%% Potential API currently unsupported and not tested
--export([open_pty/3, open_pty/7,
- open_pty/9, window_change/4, window_change/6,
+-export([window_change/4, window_change/6,
direct_tcpip/6, direct_tcpip/8, tcpip_forward/3,
cancel_tcpip_forward/3, signal/3, exit_status/3]).
@@ -107,9 +107,15 @@ shell(ConnectionHandler, ChannelId) ->
%% Description: Executes a predefined subsystem.
%%--------------------------------------------------------------------
subsystem(ConnectionHandler, ChannelId, SubSystem, TimeOut) ->
- ssh_connection_handler:request(ConnectionHandler, self(),
- ChannelId, "subsystem",
- true, [?string(SubSystem)], TimeOut).
+ case ssh_connection_handler:request(ConnectionHandler, self(),
+ ChannelId, "subsystem",
+ true, [?string(SubSystem)], TimeOut) of
+ success -> success;
+ failure -> failure;
+ {error,timeout} -> {error,timeout};
+ _ -> failure
+ end.
+
%%--------------------------------------------------------------------
-spec send(pid(), channel_id(), iodata()) ->
ok | {error, closed}.
@@ -183,6 +189,25 @@ reply_request(_,false, _, _) ->
ok.
%%--------------------------------------------------------------------
+-spec ptty_alloc(pid(), channel_id(), proplists:proplist()) -> success | failiure.
+%%
+%%
+%% Description: Sends a ssh connection protocol pty_req.
+%%--------------------------------------------------------------------
+ptty_alloc(ConnectionHandler, Channel, Options) ->
+ ptty_alloc(ConnectionHandler, Channel, Options, infinity).
+ptty_alloc(ConnectionHandler, Channel, Options, TimeOut) ->
+ {Width, PixWidth} = pty_default_dimensions(width, Options),
+ {Hight, PixHight} = pty_default_dimensions(hight, Options),
+ pty_req(ConnectionHandler, Channel,
+ proplists:get_value(term, Options, default_term()),
+ proplists:get_value(width, Options, Width),
+ proplists:get_value(hight, Options, Hight),
+ proplists:get_value(pixel_widh, Options, PixWidth),
+ proplists:get_value(pixel_hight, Options, PixHight),
+ proplists:get_value(pty_opts, Options, []), TimeOut
+ ).
+%%--------------------------------------------------------------------
%% Not yet officialy supported! The following functions are part of the
%% initial contributed ssh application. They are untested. Do we want them?
%% Should they be documented and tested?
@@ -205,23 +230,6 @@ exit_status(ConnectionHandler, Channel, Status) ->
ssh_connection_handler:request(ConnectionHandler, Channel,
"exit-status", false, [?uint32(Status)], 0).
-open_pty(ConnectionHandler, Channel, TimeOut) ->
- open_pty(ConnectionHandler, Channel,
- os:getenv("TERM"), 80, 24, [], TimeOut).
-
-open_pty(ConnectionHandler, Channel, Term, Width, Height, PtyOpts, TimeOut) ->
- open_pty(ConnectionHandler, Channel, Term, Width,
- Height, 0, 0, PtyOpts, TimeOut).
-
-open_pty(ConnectionHandler, Channel, Term, Width, Height,
- PixWidth, PixHeight, PtyOpts, TimeOut) ->
- ssh_connection_handler:request(ConnectionHandler,
- Channel, "pty-req", true,
- [?string(Term),
- ?uint32(Width), ?uint32(Height),
- ?uint32(PixWidth),?uint32(PixHeight),
- encode_pty_opts(PtyOpts)], TimeOut).
-
direct_tcpip(ConnectionHandler, RemoteHost,
RemotePort, OrigIP, OrigPort, Timeout) ->
direct_tcpip(ConnectionHandler, RemoteHost, RemotePort, OrigIP, OrigPort,
@@ -1074,6 +1082,27 @@ flow_control([_|_], #channel{flow_control = From,
flow_control(_,_,_) ->
[].
+pty_req(ConnectionHandler, Channel, Term, Width, Height,
+ PixWidth, PixHeight, PtyOpts, TimeOut) ->
+ ssh_connection_handler:request(ConnectionHandler,
+ Channel, "pty-req", true,
+ [?string(Term),
+ ?uint32(Width), ?uint32(Height),
+ ?uint32(PixWidth),?uint32(PixHeight),
+ encode_pty_opts(PtyOpts)], TimeOut).
+
+pty_default_dimensions(Dimension, Options) ->
+ case proplists:get_value(Dimension, Options, 0) of
+ N when is_integer(N), N > 0 ->
+ {N, 0};
+ _ ->
+ case proplists:get_value(list_to_atom("pixel_" ++ atom_to_list(Dimension)), Options, 0) of
+ N when is_integer(N), N > 0 ->
+ {0, N};
+ _ ->
+ {?TERMINAL_WIDTH, 0}
+ end
+ end.
encode_pty_opts(Opts) ->
Bin = list_to_binary(encode_pty_opts2(Opts)),
@@ -1271,3 +1300,10 @@ decode_ip(Addr) when is_binary(Addr) ->
{ok,A} -> A
end.
+default_term() ->
+ case os:getenv("TERM") of
+ false ->
+ ?DEFAULT_TERMINAL;
+ Str when is_list(Str)->
+ Str
+ end.
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index fa107be1b1..fdb9d3b3e6 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -624,7 +624,7 @@ handle_event(renegotiate, connected, #state{ssh_params = Ssh0}
renegotiate = true})};
handle_event(renegotiate, StateName, State) ->
- timer:apply_after(?REKEY_TIMOUT, gen_fsm, send_all_state_event, [self(), renegotiatie]),
+ timer:apply_after(?REKEY_TIMOUT, gen_fsm, send_all_state_event, [self(), renegotiate]),
%% Allready in keyexcahange so ignore
{next_state, StateName, State};
@@ -1299,9 +1299,9 @@ generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName,
end;
generate_event(Msg, StateName, State0, EncData) ->
- Event = ssh_message:decode(Msg),
- State = generate_event_new_state(State0, EncData),
try
+ Event = ssh_message:decode(Msg),
+ State = generate_event_new_state(State0, EncData),
case Event of
#ssh_msg_kexinit{} ->
%% We need payload for verification later.
@@ -1315,7 +1315,7 @@ generate_event(Msg, StateName, State0, EncData) ->
#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
description = "Encountered unexpected input",
language = "en"},
- handle_disconnect(DisconnectMsg, State)
+ handle_disconnect(DisconnectMsg, State0)
end.
@@ -1475,25 +1475,35 @@ handle_ssh_packet(Length, StateName, #state{decoded_data_buffer = DecData0,
ssh_params = Ssh0,
transport_protocol = _Protocol,
socket = _Socket} = State0) ->
- {Ssh1, DecData, EncData, Mac} =
- ssh_transport:unpack(EncData0, Length, Ssh0),
- SshPacket = <<DecData0/binary, DecData/binary>>,
- case ssh_transport:is_valid_mac(Mac, SshPacket, Ssh1) of
- true ->
- PacketData = ssh_transport:msg_data(SshPacket),
- {Ssh1, Msg} = ssh_transport:decompress(Ssh1, PacketData),
- generate_event(Msg, StateName,
- State0#state{ssh_params = Ssh1,
- %% Important to be set for
- %% next_packet
- decoded_data_buffer = <<>>}, EncData);
- false ->
- DisconnectMsg =
+ try
+ {Ssh1, DecData, EncData, Mac} =
+ ssh_transport:unpack(EncData0, Length, Ssh0),
+ SshPacket = <<DecData0/binary, DecData/binary>>,
+ case ssh_transport:is_valid_mac(Mac, SshPacket, Ssh1) of
+ true ->
+ PacketData = ssh_transport:msg_data(SshPacket),
+ {Ssh1, Msg} = ssh_transport:decompress(Ssh1, PacketData),
+ generate_event(Msg, StateName,
+ State0#state{ssh_params = Ssh1,
+ %% Important to be set for
+ %% next_packet
+ decoded_data_buffer = <<>>},
+ EncData);
+ false ->
+ DisconnectMsg =
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
+ description = "Bad mac",
+ language = "en"},
+ handle_disconnect(DisconnectMsg, State0)
+ end
+ catch _:_ ->
+ Disconnect =
#ssh_msg_disconnect{code = ?SSH_DISCONNECT_PROTOCOL_ERROR,
- description = "Bad mac",
+ description = "Bad input",
language = "en"},
- handle_disconnect(DisconnectMsg, State0)
- end.
+ handle_disconnect(Disconnect, State0)
+ end.
+
handle_disconnect(DisconnectMsg, State) ->
handle_disconnect(own, DisconnectMsg, State).
diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl
index 721146c509..613f8f25b2 100644
--- a/lib/ssh/src/ssh_sftp.erl
+++ b/lib/ssh/src/ssh_sftp.erl
@@ -33,8 +33,8 @@
-export([start_channel/1, start_channel/2, start_channel/3, stop_channel/1]).
--export([open/3, opendir/2, close/2, readdir/2, pread/4, read/3,
- open/4, opendir/3, close/3, readdir/3, pread/5, read/4,
+-export([open/3, open_tar/3, opendir/2, close/2, readdir/2, pread/4, read/3,
+ open/4, open_tar/4, opendir/3, close/3, readdir/3, pread/5, read/4,
apread/4, aread/3, pwrite/4, write/3, apwrite/4, awrite/3,
pwrite/5, write/4,
position/3, real_path/2, read_file_info/2, get_file_info/2,
@@ -69,6 +69,18 @@
mode
}).
+-record(bufinf,
+ {
+ mode, % read | write (=from or to buffer by user)
+ crypto_state,
+ crypto_fun, % For encode or decode depending on the mode field
+ size = 0, % # bytes "before" the current buffer for the postion call
+
+ chunksize, % The size of the chunks to be sent or received
+ enc_text_buf = <<>>, % Encrypted text
+ plain_text_buf = <<>> % Decrypted text
+ }).
+
-define(FILEOP_TIMEOUT, infinity).
-define(NEXT_REQID(S),
@@ -112,7 +124,7 @@ start_channel(Host, Opts) ->
start_channel(Host, Port, Opts) ->
{SshOpts, SftpOpts} = handle_options(Opts, [], []),
Timeout = proplists:get_value(timeout, SftpOpts, infinity),
- case ssh_xfer:connect(Host, Port, SshOpts) of
+ case ssh_xfer:connect(Host, Port, SshOpts, Timeout) of
{ok, ChannelId, Cm} ->
case ssh_channel:start(Cm, ChannelId, ?MODULE, [Cm,
ChannelId, SftpOpts]) of
@@ -162,6 +174,77 @@ open(Pid, File, Mode) ->
open(Pid, File, Mode, FileOpTimeout) ->
call(Pid, {open, false, File, Mode}, FileOpTimeout).
+open_tar(Pid, File, Mode) ->
+ open_tar(Pid, File, Mode, ?FILEOP_TIMEOUT).
+open_tar(Pid, File, Mode, FileOpTimeout) ->
+ case {lists:member(write,Mode),
+ lists:member(read,Mode),
+ Mode -- [read,write]} of
+ {true,false,[]} ->
+ {ok,Handle} = open(Pid, File, [write], FileOpTimeout),
+ erl_tar:init(Pid, write,
+ fun(write, {_,Data}) ->
+ write_to_remote_tar(Pid, Handle, to_bin(Data), FileOpTimeout);
+ (position, {_,Pos}) ->
+ position(Pid, Handle, Pos, FileOpTimeout);
+ (close, _) ->
+ close(Pid, Handle, FileOpTimeout)
+ end);
+ {true,false,[{crypto,{CryptoInitFun,CryptoEncryptFun,CryptoEndFun}}]} ->
+ {ok,SftpHandle} = open(Pid, File, [write], FileOpTimeout),
+ BI = #bufinf{mode = write,
+ crypto_fun = CryptoEncryptFun},
+ {ok,BufHandle} = open_buf(Pid, CryptoInitFun, BI, FileOpTimeout),
+ erl_tar:init(Pid, write,
+ fun(write, {_,Data}) ->
+ write_buf(Pid, SftpHandle, BufHandle, to_bin(Data), FileOpTimeout);
+ (position, {_,Pos}) ->
+ position_buf(Pid, SftpHandle, BufHandle, Pos, FileOpTimeout);
+ (close, _) ->
+ {ok,#bufinf{
+ plain_text_buf = PlainBuf0,
+ enc_text_buf = EncBuf0,
+ crypto_state = CState0
+ }} = call(Pid, {get_bufinf,BufHandle}, FileOpTimeout),
+ {ok,EncTextTail} = CryptoEndFun(PlainBuf0, CState0),
+ EncTextBuf = <<EncBuf0/binary, EncTextTail/binary>>,
+ case write(Pid, SftpHandle, EncTextBuf, FileOpTimeout) of
+ ok ->
+ call(Pid, {erase_bufinf,BufHandle}, FileOpTimeout),
+ close(Pid, SftpHandle, FileOpTimeout);
+ Other ->
+ Other
+ end
+ end);
+ {false,true,[]} ->
+ {ok,Handle} = open(Pid, File, [read,binary], FileOpTimeout),
+ erl_tar:init(Pid, read,
+ fun(read2, {_,Len}) ->
+ read_repeat(Pid, Handle, Len, FileOpTimeout);
+ (position, {_,Pos}) ->
+ position(Pid, Handle, Pos, FileOpTimeout);
+ (close, _) ->
+ close(Pid, Handle, FileOpTimeout)
+ end);
+ {false,true,[{crypto,{CryptoInitFun,CryptoDecryptFun}}]} ->
+ {ok,SftpHandle} = open(Pid, File, [read,binary], FileOpTimeout),
+ BI = #bufinf{mode = read,
+ crypto_fun = CryptoDecryptFun},
+ {ok,BufHandle} = open_buf(Pid, CryptoInitFun, BI, FileOpTimeout),
+ erl_tar:init(Pid, read,
+ fun(read2, {_,Len}) ->
+ read_buf(Pid, SftpHandle, BufHandle, Len, FileOpTimeout);
+ (position, {_,Pos}) ->
+ position_buf(Pid, SftpHandle, BufHandle, Pos, FileOpTimeout);
+ (close, _) ->
+ call(Pid, {erase_bufinf,BufHandle}, FileOpTimeout),
+ close(Pid, SftpHandle, FileOpTimeout)
+ end);
+ _ ->
+ {error,{illegal_mode,Mode}}
+ end.
+
+
opendir(Pid, Path) ->
opendir(Pid, Path, ?FILEOP_TIMEOUT).
opendir(Pid, Path, FileOpTimeout) ->
@@ -447,6 +530,15 @@ handle_cast(_,State) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
+do_handle_call({get_bufinf,BufHandle}, _From, S=#state{inf=I0}) ->
+ {reply, dict:find(BufHandle,I0), S};
+
+do_handle_call({put_bufinf,BufHandle,B}, _From, S=#state{inf=I0}) ->
+ {reply, ok, S#state{inf=dict:store(BufHandle,B,I0)}};
+
+do_handle_call({erase_bufinf,BufHandle}, _From, S=#state{inf=I0}) ->
+ {reply, ok, S#state{inf=dict:erase(BufHandle,I0)}};
+
do_handle_call({open, Async,FileName,Mode}, From, #state{xf = XF} = State) ->
{Access,Flags,Attrs} = open_mode(XF#ssh_xfer.vsn, Mode),
ReqID = State#state.req_id,
@@ -551,12 +643,7 @@ do_handle_call({read,Async,Handle,Length}, From, State) ->
do_handle_call({pwrite,Async,Handle,At,Data0}, From, State) ->
case lseek_position(Handle, At, State) of
{ok,Offset} ->
- Data = if
- is_binary(Data0) ->
- Data0;
- is_list(Data0) ->
- list_to_binary(Data0)
- end,
+ Data = to_bin(Data0),
ReqID = State#state.req_id,
Size = size(Data),
ssh_xfer:write(?XF(State),ReqID,Handle,Offset,Data),
@@ -569,12 +656,7 @@ do_handle_call({pwrite,Async,Handle,At,Data0}, From, State) ->
do_handle_call({write,Async,Handle,Data0}, From, State) ->
case lseek_position(Handle, cur, State) of
{ok,Offset} ->
- Data = if
- is_binary(Data0) ->
- Data0;
- is_list(Data0) ->
- list_to_binary(Data0)
- end,
+ Data = to_bin(Data0),
ReqID = State#state.req_id,
Size = size(Data),
ssh_xfer:write(?XF(State),ReqID,Handle,Offset,Data),
@@ -1126,5 +1208,207 @@ lseek_pos({eof, Offset}, _CurOffset, CurSize)
end;
lseek_pos(_, _, _) ->
{error, einval}.
-
+%%%================================================================
+%%%
+to_bin(Data) when is_list(Data) -> list_to_binary(Data);
+to_bin(Data) when is_binary(Data) -> Data.
+
+
+read_repeat(Pid, Handle, Len, FileOpTimeout) ->
+ {ok,{_WindowSz,PacketSz}} = recv_window(Pid, FileOpTimeout),
+ read_rpt(Pid, Handle, Len, PacketSz, FileOpTimeout, <<>>).
+
+read_rpt(Pid, Handle, WantedLen, PacketSz, FileOpTimeout, Acc) when WantedLen > 0 ->
+ case read(Pid, Handle, min(WantedLen,PacketSz), FileOpTimeout) of
+ {ok, Data} ->
+ read_rpt(Pid, Handle, WantedLen-size(Data), PacketSz, FileOpTimeout, <<Acc/binary, Data/binary>>);
+ eof ->
+ {ok, Acc};
+ Error ->
+ Error
+ end;
+read_rpt(_Pid, _Handle, WantedLen, _PacketSz, _FileOpTimeout, Acc) when WantedLen >= 0 ->
+ {ok,Acc}.
+
+
+write_to_remote_tar(_Pid, _SftpHandle, <<>>, _FileOpTimeout) ->
+ ok;
+write_to_remote_tar(Pid, SftpHandle, Bin, FileOpTimeout) ->
+ {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
+ write_file_loop(Pid, SftpHandle, 0, Bin, size(Bin), Packet, FileOpTimeout).
+
+position_buf(Pid, SftpHandle, BufHandle, Pos, FileOpTimeout) ->
+ {ok,#bufinf{mode = Mode,
+ plain_text_buf = Buf0,
+ size = Size}} = call(Pid, {get_bufinf,BufHandle}, FileOpTimeout),
+ case Pos of
+ {cur,0} when Mode==write ->
+ {ok,Size+size(Buf0)};
+
+ {cur,0} when Mode==read ->
+ {ok,Size};
+
+ _ when Mode==read, is_integer(Pos) ->
+ Skip = Pos-Size,
+ if
+ Skip < 0 ->
+ {error, cannot_rewind};
+ Skip == 0 ->
+ %% Optimization
+ {ok,Pos};
+ Skip > 0 ->
+ case read_buf(Pid, SftpHandle, BufHandle, Skip, FileOpTimeout) of
+ %% A bit innefficient to fetch the bufinf again, but there are lots of
+ %% other more important optimizations waiting....
+ {ok,_} ->
+ {ok,Pos};
+ Other ->
+ Other
+ end
+ end;
+
+ _ ->
+ {error,{not_yet_implemented,{pos,Pos}}}
+ end.
+
+read_buf(Pid, SftpHandle, BufHandle, WantedLen, FileOpTimeout) ->
+ {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
+ {ok,B0} = call(Pid, {get_bufinf,BufHandle}, FileOpTimeout),
+ case do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout, B0) of
+ {ok,ResultBin,B} ->
+ call(Pid, {put_bufinf,BufHandle,B}, FileOpTimeout),
+ {ok,ResultBin};
+ {error,Error} ->
+ {error,Error};
+ {eof,B} ->
+ call(Pid, {put_bufinf,BufHandle,B}, FileOpTimeout),
+ eof
+ end.
+
+do_the_read_buf(_Pid, _SftpHandle, WantedLen, _Packet, _FileOpTimeout,
+ B=#bufinf{plain_text_buf=PlainBuf0,
+ size = Size})
+ when size(PlainBuf0) >= WantedLen ->
+ %% We already have the wanted number of bytes decoded and ready!
+ <<ResultBin:WantedLen/binary, PlainBuf/binary>> = PlainBuf0,
+ {ok,ResultBin,B#bufinf{plain_text_buf=PlainBuf,
+ size = Size + WantedLen}};
+
+do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout,
+ B0=#bufinf{plain_text_buf = PlainBuf0,
+ enc_text_buf = EncBuf0,
+ chunksize = undefined
+ })
+ when size(EncBuf0) > 0 ->
+ %% We have (at least) one decodable byte waiting for decodeing.
+ {ok,DecodedBin,B} = apply_crypto(EncBuf0, B0),
+ do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout,
+ B#bufinf{plain_text_buf = <<PlainBuf0/binary, DecodedBin/binary>>,
+ enc_text_buf = <<>>
+ });
+
+do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout,
+ B0=#bufinf{plain_text_buf = PlainBuf0,
+ enc_text_buf = EncBuf0,
+ chunksize = ChunkSize0
+ })
+ when size(EncBuf0) >= ChunkSize0 ->
+ %% We have (at least) one chunk of decodable bytes waiting for decodeing.
+ <<ToDecode:ChunkSize0/binary, EncBuf/binary>> = EncBuf0,
+ {ok,DecodedBin,B} = apply_crypto(ToDecode, B0),
+ do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout,
+ B#bufinf{plain_text_buf = <<PlainBuf0/binary, DecodedBin/binary>>,
+ enc_text_buf = EncBuf
+ });
+
+do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout, B=#bufinf{enc_text_buf = EncBuf0}) ->
+ %% We must read more bytes and append to the buffer of encoded bytes.
+ case read(Pid, SftpHandle, Packet, FileOpTimeout) of
+ {ok,EncryptedBin} ->
+ do_the_read_buf(Pid, SftpHandle, WantedLen, Packet, FileOpTimeout,
+ B#bufinf{enc_text_buf = <<EncBuf0/binary, EncryptedBin/binary>>});
+ eof ->
+ {eof,B};
+ Other ->
+ Other
+ end.
+
+
+write_buf(Pid, SftpHandle, BufHandle, PlainBin, FileOpTimeout) ->
+ {ok,{_Window,Packet}} = send_window(Pid, FileOpTimeout),
+ {ok,B0=#bufinf{plain_text_buf=PTB}} = call(Pid, {get_bufinf,BufHandle}, FileOpTimeout),
+ case do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
+ B0#bufinf{plain_text_buf = <<PTB/binary,PlainBin/binary>>}) of
+ {ok, B} ->
+ call(Pid, {put_bufinf,BufHandle,B}, FileOpTimeout),
+ ok;
+ {error,Error} ->
+ {error,Error}
+ end.
+
+do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
+ B=#bufinf{enc_text_buf = EncBuf0,
+ size = Size})
+ when size(EncBuf0) >= Packet ->
+ <<BinToWrite:Packet/binary, EncBuf/binary>> = EncBuf0,
+ case write(Pid, SftpHandle, BinToWrite, FileOpTimeout) of
+ ok ->
+ do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
+ B#bufinf{enc_text_buf = EncBuf,
+ size = Size + Packet});
+ Other ->
+ Other
+ end;
+
+do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
+ B0=#bufinf{plain_text_buf = PlainBuf0,
+ enc_text_buf = EncBuf0,
+ chunksize = undefined})
+ when size(PlainBuf0) > 0 ->
+ {ok,EncodedBin,B} = apply_crypto(PlainBuf0, B0),
+ do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
+ B#bufinf{plain_text_buf = <<>>,
+ enc_text_buf = <<EncBuf0/binary, EncodedBin/binary>>});
+
+do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
+ B0=#bufinf{plain_text_buf = PlainBuf0,
+ enc_text_buf = EncBuf0,
+ chunksize = ChunkSize0
+ })
+ when size(PlainBuf0) >= ChunkSize0 ->
+ <<ToEncode:ChunkSize0/binary, PlainBuf/binary>> = PlainBuf0,
+ {ok,EncodedBin,B} = apply_crypto(ToEncode, B0),
+ do_the_write_buf(Pid, SftpHandle, Packet, FileOpTimeout,
+ B#bufinf{plain_text_buf = PlainBuf,
+ enc_text_buf = <<EncBuf0/binary, EncodedBin/binary>>});
+
+do_the_write_buf(_Pid, _SftpHandle, _Packet, _FileOpTimeout, B) ->
+ {ok,B}.
+
+apply_crypto(In, B=#bufinf{crypto_state = CState0,
+ crypto_fun = F}) ->
+ case F(In,CState0) of
+ {ok,EncodedBin,CState} ->
+ {ok, EncodedBin, B#bufinf{crypto_state=CState}};
+ {ok,EncodedBin,CState,ChunkSize} ->
+ {ok, EncodedBin, B#bufinf{crypto_state=CState,
+ chunksize=ChunkSize}}
+ end.
+
+open_buf(Pid, CryptoInitFun, BufInfo0, FileOpTimeout) ->
+ case CryptoInitFun() of
+ {ok,CryptoState} ->
+ open_buf1(Pid, BufInfo0, FileOpTimeout, CryptoState, undefined);
+ {ok,CryptoState,ChunkSize} ->
+ open_buf1(Pid, BufInfo0, FileOpTimeout, CryptoState, ChunkSize);
+ Other ->
+ Other
+ end.
+
+open_buf1(Pid, BufInfo0, FileOpTimeout, CryptoState, ChunkSize) ->
+ BufInfo = BufInfo0#bufinf{crypto_state = CryptoState,
+ chunksize = ChunkSize},
+ BufHandle = make_ref(),
+ call(Pid, {put_bufinf,BufHandle,BufInfo}, FileOpTimeout),
+ {ok,BufHandle}.
diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl
index 1881392db8..2743b704f1 100644
--- a/lib/ssh/src/ssh_xfer.erl
+++ b/lib/ssh/src/ssh_xfer.erl
@@ -23,7 +23,7 @@
-module(ssh_xfer).
--export([attach/2, connect/3]).
+-export([attach/2, connect/3, connect/4]).
-export([open/6, opendir/3, readdir/3, close/3, read/5, write/5,
rename/5, remove/3, mkdir/4, rmdir/3, realpath/3, extended/4,
stat/4, fstat/4, lstat/4, setstat/4,
@@ -58,6 +58,13 @@ connect(Host, Port, Opts) ->
Error -> Error
end.
+connect(Host, Port, Opts, Timeout) ->
+ case ssh:connect(Host, Port, Opts, Timeout) of
+ {ok, CM} -> open_xfer(CM, [{timeout, Timeout}|Opts]);
+ {error, Timeout} -> {error, timeout};
+ Error -> Error
+ end.
+
open_xfer(CM, Opts) ->
TMO = proplists:get_value(timeout, Opts, infinity),
case ssh_connection:session_channel(CM, ?XFER_WINDOW_SIZE, ?XFER_PACKET_SIZE, TMO) of