aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tftp/src/tftp.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tftp/src/tftp.erl')
-rw-r--r--lib/tftp/src/tftp.erl409
1 files changed, 409 insertions, 0 deletions
diff --git a/lib/tftp/src/tftp.erl b/lib/tftp/src/tftp.erl
new file mode 100644
index 0000000000..31e4c651e8
--- /dev/null
+++ b/lib/tftp/src/tftp.erl
@@ -0,0 +1,409 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2018. 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%
+%%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File : tftp.erl
+%%% Author : Hakan Mattsson <[email protected]>
+%%% Description : Trivial FTP
+%%% Created : 18 May 2004 by Hakan Mattsson <[email protected]>
+%%%-------------------------------------------------------------------
+%%%
+%%% This is a complete implementation of the following IETF standards:
+%%%
+%%% RFC 1350, The TFTP Protocol (revision 2).
+%%% RFC 2347, TFTP Option Extension.
+%%% RFC 2348, TFTP Blocksize Option.
+%%% RFC 2349, TFTP Timeout Interval and Transfer Size Options.
+%%%
+%%% The only feature that not is implemented in this release is
+%%% the "netascii" transfer mode.
+%%%
+%%% The start/1 function starts a daemon process which, listens for
+%%% UDP packets on a port. When it receives a request for read or
+%%% write it spawns a temporary server process which handles the
+%%% actual transfer of the file. On the client side the read_file/3
+%%% and write_file/3 functions spawns a temporary client process which
+%%% establishes contact with a TFTP daemon and performs the actual
+%%% transfer of the file.
+%%%
+%%% Most of the options are common for both the client and the server
+%%% side, but some of them differs a little. Here are the available
+%%% options:
+%%%
+%%% {debug, Level}
+%%%
+%%% Level = none | error | warning brief | normal | verbose | all
+%%%
+%%% Controls the level of debug printouts. The default is none.
+%%%
+%%% {host, Host}
+%%%
+%%% The name or IP address of the host where the TFTP daemon
+%%% resides. This option is only used by the client. See
+%%% 'inet' about valid host names.
+%%%
+%%% {port, Port}
+%%%
+%%% Port = integer()
+%%%
+%%% The TFTP port where the daemon listens. It defaults to the
+%%% standardized number 69. On the server side it may sometimes
+%%% make sense to set it to 0, which means that the daemon just
+%%% will pick a free port (which is returned by the start/1
+%%% function).
+%%%
+%%% If a socket has somehow already has been connected, the
+%%% {udp, [{fd, integer()}]} option can be used to pass the
+%%% open file descriptor to gen_udp. This can be automated
+%%% a bit by using a command line argument stating the
+%%% prebound file descriptor number. For example, if the
+%%% Port is 69 and the file descriptor 22 has been opened by
+%%% setuid_socket_wrap. Then the command line argument
+%%% "-tftpd_69 22" will trigger the prebound file
+%%% descriptor 22 to be used instead of opening port 69.
+%%% The UDP option {udp, [{fd, 22}]} autmatically be added.
+%%% See init:get_argument/ about command line arguments and
+%%% gen_udp:open/2 about UDP options.
+%%%
+%%% {port_policy, Policy}
+%%%
+%%% Policy = random | Port | {range, MinPort, MaxPort}
+%%% Port = MinPort = MaxPort = integer()
+%%%
+%%% Policy for the selection of the temporary port which is used
+%%% by the server/client during the file transfer. It defaults to
+%%% 'random' which is the standardized policy. With this policy a
+%%% randomized free port used. A single port or a range of ports
+%%% can be useful if the protocol should pass thru a firewall.
+%%%
+%%% {prebound_fd, InitArgFlag}
+%%%
+%%% InitArgFlag = atom()
+%%%
+%%% If a socket has somehow already has been connected, the
+%%% {udp, [{fd, integer()}]} option can be used to pass the
+%%% open file descriptor to gen_udp.
+%%%
+%%% The prebound_fd option makes it possible to pass give the
+%%% file descriptor as a command line argument. The typical
+%%% usage is when used in conjunction with setuid_socket_wrap
+%%% to be able to open privileged sockets. For example if the
+%%% file descriptor 22 has been opened by setuid_socket_wrap
+%%% and you have choosen my_tftp_fd as init argument, the
+%%% command line should like this "erl -my_tftp_fd 22" and
+%%% FileDesc should be set to my_tftpd_fd. This would
+%%% automatically imply {fd, 22} to be set as UDP option.
+%%%
+%%% {udp, UdpOptions}
+%%%
+%%% Options to gen_udp:open/2.
+%%%
+%%% {use_tsize, Bool}
+%%%
+%%% Bool = boolean()
+%%%
+%%% Flag for automated usage of the "tsize" option. With this set
+%%% to true, the write_file/3 client will determine the filesize
+%%% and send it to the server as the standardized "tsize" option.
+%%% A read_file/3 client will just acquire filesize from the
+%%% server by sending a zero "tsize".
+%%%
+%%% {max_tsize, MaxTsize}
+%%%
+%%% MaxTsize = integer() | infinity
+%%%
+%%% Threshold for the maximal filesize in bytes. The transfer will
+%%% be aborted if the limit is exceeded. It defaults to
+%%% 'infinity'.
+%%%
+%%% {max_conn, MaxConn}
+%%%
+%%% MaxConn = integer() | infinity
+%%%
+%%% Threshold for the maximal number of active connections. The
+%%% daemon will reject the setup of new connections if the limit
+%%% is exceeded. It defaults to 'infinity'.
+%%%
+%%% {TftpKey, TftpVal}
+%%%
+%%% TftpKey = string()
+%%% TftpVal = string()
+%%%
+%%% The name and value of a TFTP option.
+%%%
+%%% {reject, Feature}
+%%%
+%%% Feature = Mode | TftpKey
+%%% Mode = read | write
+%%% TftpKey = string()
+%%%
+%%% Control which features that should be rejected.
+%%% This is mostly useful for the server as it may restrict
+%%% usage of certain TFTP options or read/write access.
+%%%
+%%% {callback, {RegExp, Module, State}}
+%%%
+%%% RegExp = string()
+%%% Module = atom()
+%%% State = term()
+%%%
+%%% Registration of a callback module. When a file is to be
+%%% transferred, its local filename will be matched to the
+%%% regular expressions of the registered callbacks. The first
+%%% matching callback will be used the during the transfer.The
+%%% callback module must implement the 'tftp' behaviour.
+%%%
+%%% On the server side the callback interaction starts with a
+%%% call to open/5 with the registered initial callback
+%%% state. open/5 is expected to open the (virtual) file. Then
+%%% either the read/1 or write/2 functions are invoked
+%%% repeatedly, once per transfererred block. At each function
+%%% call the state returned from the previous call is
+%%% obtained. When the last block has been encountered the read/1
+%%% or write/2 functions is expected to close the (virtual)
+%%% file.and return its last state. The abort/3 function is only
+%%% used in error situations. prepare/5 is not used on the server
+%%% side.
+%%%
+%%% On the client side the callback interaction is the same, but
+%%% it starts and ends a bit differently. It starts with a call
+%%% to prepare/5 with the same arguments as open/5
+%%% takes. prepare/5 is expected to validate the TFTP options,
+%%% suggested by the user and return the subset of them that it
+%%% accepts. Then the options is sent to the server which will
+%%% perform the same TFTP option negotiation procedure. The
+%%% options that are accepted by the server is forwarded to the
+%%% open/5 function on the client side. On the client side the
+%%% open/5 function must accept all option as is or reject the
+%%% transfer. Then the callback interaction follows the same
+%%% pattern as described above for the server side. When the last
+%%% block is encountered in read/1 or write/2 the returned stated
+%%% is forwarded to the user and returned from read_file/3 or
+%%% write_file/3.
+%%%-------------------------------------------------------------------
+
+-module(tftp).
+
+%%-------------------------------------------------------------------
+%% Interface
+%%-------------------------------------------------------------------
+
+%% Public functions
+-export([
+ read_file/3,
+ write_file/3,
+ start/1,
+ info/1,
+ change_config/2,
+ start/0,
+ stop/0
+ ]).
+
+%% Application local functions
+-export([
+ start_standalone/1,
+ start_service/1,
+ stop_service/1,
+ services/0,
+ service_info/1
+ ]).
+
+
+-type peer() :: {PeerType :: inet | inet6,
+ PeerHost :: inet:ip_address(),
+ PeerPort :: port()}.
+
+-type access() :: read | write.
+
+-type options() :: [{Key :: string(), Value :: string()}].
+
+-type error_code() :: undef | enoent | eacces | enospc |
+ badop | eexist | baduser | badopt |
+ integer().
+
+-callback prepare(Peer :: peer(),
+ Access :: access(),
+ Filename :: file:name(),
+ Mode :: string(),
+ SuggestedOptions :: options(),
+ InitialState :: [] | [{root_dir, string()}]) ->
+ {ok, AcceptedOptions :: options(), NewState :: term()} |
+ {error, {Code :: error_code(), string()}}.
+
+-callback open(Peer :: peer(),
+ Access :: access(),
+ Filename :: file:name(),
+ Mode :: string(),
+ SuggestedOptions :: options(),
+ State :: [] | [{root_dir, string()}] | term()) ->
+ {ok, AcceptedOptions :: options(), NewState :: term()} |
+ {error, {Code :: error_code(), string()}}.
+
+-callback read(State :: term()) -> {more, binary(), NewState :: term()} |
+ {last, binary(), integer()} |
+ {error, {Code :: error_code(), string()}}.
+
+-callback write(binary(), State :: term()) ->
+ {more, NewState :: term()} |
+ {last, FileSize :: integer()} |
+ {error, {Code :: error_code(), string()}}.
+
+-callback abort(Code :: error_code(), string(), State :: term()) -> 'ok'.
+
+-include("tftp.hrl").
+
+
+%%-------------------------------------------------------------------
+%% read_file(RemoteFilename, LocalFilename, Options) ->
+%% {ok, LastCallbackState} | {error, Reason}
+%%
+%% RemoteFilename = string()
+%% LocalFilename = binary | string()
+%% Options = [option()]
+%% LastCallbackState = term()
+%% Reason = term()
+%%
+%% Reads a (virtual) file from a TFTP server
+%%
+%% If LocalFilename is the atom 'binary', tftp_binary will be used as
+%% callback module. It will concatenate all transferred blocks and
+%% return them as one single binary in the CallbackState.
+%%
+%% When LocalFilename is a string, it will be matched to the
+%% registered callback modules and hopefully one of them will be
+%% selected. By default, tftp_file will be used as callback module. It
+%% will write each transferred block to the file named
+%% LocalFilename. The number of transferred bytes will be returned as
+%% LastCallbackState.
+%%-------------------------------------------------------------------
+
+read_file(RemoteFilename, LocalFilename, Options) ->
+ tftp_engine:client_start(read, RemoteFilename, LocalFilename, Options).
+
+%%-------------------------------------------------------------------
+%% write(RemoteFilename, LocalFilename, Options) ->
+%% {ok, LastCallbackState} | {error, Reason}
+%%
+%% RemoteFilename = string()
+%% LocalFilename = binary() | string()
+%% Options = [option()]
+%% LastCallbackState = term()
+%% Reason = term()
+%%
+%% Writes a (virtual) file to a TFTP server
+%%
+%% If LocalFilename is a binary, tftp_binary will be used as callback
+%% module. The binary will be transferred block by block and the number
+%% of transferred bytes will be returned as LastCallbackState.
+%%
+%% When LocalFilename is a string, it will be matched to the
+%% registered callback modules and hopefully one of them will be
+%% selected. By default, tftp_file will be used as callback module. It
+%% will read the file named LocalFilename block by block. The number
+%% of transferred bytes will be returned as LastCallbackState.
+%%-------------------------------------------------------------------
+
+write_file(RemoteFilename, LocalFilename, Options) ->
+ tftp_engine:client_start(write, RemoteFilename, LocalFilename, Options).
+
+%%-------------------------------------------------------------------
+%% start(Options) -> {ok, Pid} | {error, Reason}
+%%
+%% Options = [option()]
+%% Pid = pid()
+%% Reason = term()
+%%
+%% Starts a daemon process which listens for udp packets on a
+%% port. When it receives a request for read or write it spawns
+%% a temporary server process which handles the actual transfer
+%% of the (virtual) file.
+%%-------------------------------------------------------------------
+
+start(Options) ->
+ tftp_engine:daemon_start(Options).
+
+%%-------------------------------------------------------------------
+%% info(Pid) -> {ok, Options} | {error, Reason}
+%%
+%% Options = [option()]
+%% Reason = term()
+%%
+%% Returns info about a tftp daemon, server or client process
+%%-------------------------------------------------------------------
+
+info(Pid) ->
+ tftp_engine:info(Pid).
+
+%%-------------------------------------------------------------------
+%% change_config(Pid, Options) -> ok | {error, Reason}
+%%
+%% Options = [option()]
+%% Reason = term()
+%%
+%% Changes config for a tftp daemon, server or client process
+%% Must be used with care.
+%%-------------------------------------------------------------------
+
+change_config(Pid, Options) ->
+ tftp_engine:change_config(Pid, Options).
+
+%%-------------------------------------------------------------------
+%% start() -> ok | {error, Reason}
+%%
+%% Reason = term()
+%%
+%% Start the application
+%%-------------------------------------------------------------------
+
+start() ->
+ application:start(tftp).
+
+%%-------------------------------------------------------------------
+%% stop() -> ok | {error, Reason}
+%%
+%% Reason = term()
+%%
+%% Stop the application
+%%-------------------------------------------------------------------
+stop() ->
+ application:stop(tftp).
+
+%%-------------------------------------------------------------------
+%% Inets service behavior
+%%-------------------------------------------------------------------
+
+start_standalone(Options) ->
+ start(Options).
+
+start_service(Options) ->
+ tftp_sup:start_child(Options).
+
+stop_service(Pid) ->
+ tftp_sup:stop_child(Pid).
+
+services() ->
+ tftp_sup:which_children().
+
+service_info(Pid) ->
+ info(Pid).
+
+
+