    View Javadoc
    1   package org.apache.commons.net.tftp;
    2
    3   /* ====================================================================
    4    * The Apache Software License, Version 1.1
    5    *
    6    * Copyright (c) 2001 The Apache Software Foundation.  All rights
    7    * reserved.
    8    *
    9    * Redistribution and use in source and binary forms, with or without
    10   * modification, are permitted provided that the following conditions
    11   * are met:
    12   *
    13   * 1. Redistributions of source code must retain the above copyright
    14   *    notice, this list of conditions and the following disclaimer.
    15   *
    16   * 2. Redistributions in binary form must reproduce the above copyright
    17   *    notice, this list of conditions and the following disclaimer in
    18   *    the documentation and/or other materials provided with the
    19   *    distribution.
    20   *
    21   * 3. The end-user documentation included with the redistribution,
    22   *    if any, must include the following acknowledgment:
    23   *       "This product includes software developed by the
    24   *        Apache Software Foundation (http://www.apache.org/)."
    25   *    Alternately, this acknowledgment may appear in the software itself,
    26   *    if and wherever such third-party acknowledgments normally appear.
    27   *
    28   * 4. The names "Apache" and "Apache Software Foundation" and
    29   *    "Apache Commons" must not be used to endorse or promote products
    30   *    derived from this software without prior written permission. For
    31   *    written permission, please contact apache@apache.org.
    32   *
    33   * 5. Products derived from this software may not be called "Apache",
    34   *    nor may "Apache" appear in their name, without
    35   *    prior written permission of the Apache Software Foundation.
    36   *
    37   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
    38   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    39   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    40   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
    41   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    42   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    43   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    44   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    45   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    46   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    47   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    48   * SUCH DAMAGE.
    49   * ====================================================================
    50   *
    51   * This software consists of voluntary contributions made by many
    52   * individuals on behalf of the Apache Software Foundation.  For more
    53   * information on the Apache Software Foundation, please see
    54   * <http://www.apache.org/>;.
    55   */
    56
    57  import java.io.IOException;
    58  import java.io.InputStream;
    59  import java.io.InterruptedIOException;
    60  import java.io.OutputStream;
    61  import java.net.InetAddress;
    62  import java.net.SocketException;
    63  import java.net.UnknownHostException;
    64  import org.apache.commons.net.io.FromNetASCIIOutputStream;
    65  import org.apache.commons.net.io.ToNetASCIIInputStream;
    66
    67  /****
    68   * The TFTPClient class encapsulates all the aspects of the TFTP protocol
    69   * necessary to receive and send files through TFTP.  It is derived from
    70   * the <a href="org.apache.commons.net.tftp.TFTP.html"> TFTP class </a> because
    71   * it is more convenient than using aggregation, and as a result exposes
    72   * the same set of methods to allow you to deal with the TFTP protocol
    73   * directly.  However, almost every user should only be concerend with the
    74   * the <a href="org.apache.commons.net.DatagramSocketClient.html#open"> open() </a>,
    75   * <a href="org.apache.commons.net.DatagramSocketClient.html#close"> close() </a>,
    76   * <a href="#sendFile"> sendFile() </a>, and
    77   * <a href="#receiveFile"> receiveFile() </a> methods.  Additionally, the
    78   * <a href="#setMaxTimeouts"> setMaxTimeouts() </a> and
    79   * <a href="org.apache.commons.net.DatagramSocketClient.html#setDefaultTimeout">
    80   * setDefaultTimeout() </a> methods may be of importance for performance
    81   * tuning.
    82   * <p>
    83   * Details regarding the TFTP protocol and the format of TFTP packets can
    84   * be found in RFC 783.  But the point of these classes is to keep you
    85   * from having to worry about the internals.
    86   * <p>
    87   * <p>
    88   * @author Daniel F. Savarese
    89   * @see TFTP
    90   * @see TFTPPacket
    91   * @see TFTPPacketException
    92   ***/
    93
    94  public class TFTPClient extends TFTP
    95  {
    96      /****
    97       * The default number of times a receive attempt is allowed to timeout
    98       * before ending attempts to retry the receive and failing.  The default
    99       * is 5 timeouts.
    100      ***/
    101     public static final int DEFAULT_MAX_TIMEOUTS = 5;
    102
    103     /**** The maximum number of timeouts allowed before failing. ***/
    104     private int __maxTimeouts;
    105
    106     /****
    107      * Creates a TFTPClient instance with a default timeout of DEFAULT_TIMEOUT,
    108      * maximum timeouts value of DEFAULT_MAX_TIMEOUTS, a null socket,
    109      * and buffered operations disabled.
    110      ***/
    111     public TFTPClient()
    112     {
    113         __maxTimeouts = DEFAULT_MAX_TIMEOUTS;
    114     }
    115
    116     /****
    117      * Sets the maximum number of times a receive attempt is allowed to
    118      * timeout during a receiveFile() or sendFile() operation before ending
    119      * attempts to retry the receive and failing.
    120      * The default is DEFAULT_MAX_TIMEOUTS.
    121      * <p>
    122      * @param numTimeouts  The maximum number of timeouts to allow.  Values
    123      *        less than 1 should not be used, but if they are, they are
    124      *        treated as 1.
    125      ***/
    126     public void setMaxTimeouts(int numTimeouts)
    127     {
    128         if (__maxTimeouts < 1)
    129             __maxTimeouts = 1;
    130         else
    131             __maxTimeouts = numTimeouts;
    132     }
    133
    134     /****
    135      * Returns the maximum number of times a receive attempt is allowed to
    136      * timeout before ending attempts to retry the receive and failing.
    137      * <p>
    138      * @return The maximum number of timeouts allowed.
    139      ***/
    140     public int getMaxTimeouts()
    141     {
    142         return __maxTimeouts;
    143     }
    144
    145
    146     /****
    147      * Requests a named file from a remote host, writes the
    148      * file to an OutputStream, closes the connection, and returns the number
    149      * of bytes read.  A local UDP socket must first be created by
    150      * <a href="org.apache.commons.net.DatagramSocketClient.html#open">open()</a> before
    151      * invoking this method.  This method will not close the OutputStream
    152      * containing the file; you must close it after the method invocation.
    153      * <p>
    154      * @param filename  The name of the file to receive.
    155      * @param mode   The TFTP mode of the transfer (one of the MODE constants).
    156      * @param output The OutputStream to which the file should be written.
    157      * @param host   The remote host serving the file.
    158      * @param port   The port number of the remote TFTP server.
    159      * @exception IOException If an I/O error occurs.  The nature of the
    160      *            error will be reported in the message.
    161      ***/
    162     public int receiveFile(String filename, int mode, OutputStream output,
    163                            InetAddress host, int port) throws IOException
    164     {
    165         int bytesRead, timeouts, lastBlock, block, hostPort, dataLength;
    166         TFTPPacket sent, received = null;
    167         TFTPErrorPacket error;
    168         TFTPDataPacket data;
    169         TFTPAckPacket ack = new TFTPAckPacket(host, port, 0);
    170
    171         beginBufferedOps();
    172
    173         dataLength = lastBlock = hostPort = bytesRead = 0;
    174         block = 1;
    175
    176         if (mode == TFTP.ASCII_MODE)
    177             output = new FromNetASCIIOutputStream(output);
    178
    179         sent =
    180             new TFTPReadRequestPacket(host, port, filename, mode);
    181
    182 _sendPacket:
    183         do
    184         {
    185             bufferedSend(sent);
    186
    187 _receivePacket:
    188             while (true)
    189             {
    190                 timeouts = 0;
    191                 while (timeouts < __maxTimeouts)
    192                 {
    193                     try
    194                     {
    195                         received = bufferedReceive();
    196                         break;
    197                     }
    198                     catch (SocketException e)
    199                     {
    200                         if (++timeouts >= __maxTimeouts)
    201                         {
    202                             endBufferedOps();
    203                             throw new IOException("Connection timed out.");
    204                         }
    205                         continue;
    206                     }
    207                     catch (InterruptedIOException e)
    208                     {
    209                         if (++timeouts >= __maxTimeouts)
    210                         {
    211                             endBufferedOps();
    212                             throw new IOException("Connection timed out.");
    213                         }
    214                         continue;
    215                     }
    216                     catch (TFTPPacketException e)
    217                     {
    218                         endBufferedOps();
    219                         throw new IOException("Bad packet: " + e.getMessage());
    220                     }
    221                 }
    222
    223                 // The first time we receive we get the port number and
    224 		// answering host address (for hosts with multiple IPs)
    225                 if (lastBlock == 0)
    226                 {
    227                     hostPort = received.getPort();
    228                     ack.setPort(hostPort);
    229                     if(!host.equals(received.getAddress()))
    230                     {
    231                         host = received.getAddress();
    232                         ack.setAddress(host);
    233                         sent.setAddress(host);
    234                     }
    235                 }
    236
    237                 // Comply with RFC 783 indication that an error acknowledgement
    238                 // should be sent to originator if unexpected TID or host.
    239                 if (host.equals(received.getAddress()) &&
    240                         received.getPort() == hostPort)
    241                 {
    242
    243                     switch (received.getType())
    244                     {
    245                     case TFTPPacket.ERROR:
    246                         error = (TFTPErrorPacket)received;
    247                         endBufferedOps();
    248                         throw new IOException("Error code " + error.getError() +
    249                                               " received: " + error.getMessage());
    250                     case TFTPPacket.DATA:
    251                         data = (TFTPDataPacket)received;
    252                         dataLength = data.getDataLength();
    253
    254                         lastBlock = data.getBlockNumber();
    255
    256                         if (lastBlock == block)
    257                         {
    258                             try
    259                             {
    260                                 output.write(data.getData(), data.getDataOffset(),
    261                                              dataLength);
    262                             }
    263                             catch (IOException e)
    264                             {
    265                                 error = new TFTPErrorPacket(host, hostPort,
    266                                                             TFTPErrorPacket.OUT_OF_SPACE,
    267                                                             "File write failed.");
    268                                 bufferedSend(error);
    269                                 endBufferedOps();
    270                                 throw e;
    271                             }
    272                             ++block;
    273                             break _receivePacket;
    274                         }
    275                         else
    276                         {
    277                             discardPackets();
    278
    279                             if (lastBlock == (block - 1))
    280                                 continue _sendPacket;  // Resend last acknowledgement.
    281
    282                             continue _receivePacket; // Start fetching packets again.
    283                         }
    284                         //break;
    285
    286                     default:
    287                         endBufferedOps();
    288                         throw new IOException("Received unexpected packet type.");
    289                     }
    290                 }
    291                 else
    292                 {
    293                     error = new TFTPErrorPacket(received.getAddress(),
    294                                                 received.getPort(),
    295                                                 TFTPErrorPacket.UNKNOWN_TID,
    296                                                 "Unexpected host or port.");
    297                     bufferedSend(error);
    298                     continue _sendPacket;
    299                 }
    300
    301                 // We should never get here, but this is a safety to avoid
    302                 // infinite loop.  If only Java had the goto statement.
    303                 //break;
    304             }
    305
    306             ack.setBlockNumber(lastBlock);
    307             sent = ack;
    308             bytesRead += dataLength;
    309         } // First data packet less than 512 bytes signals end of stream.
    310
    311         while (dataLength == TFTPPacket.SEGMENT_SIZE);
    312
    313         bufferedSend(sent);
    314         endBufferedOps();
    315
    316         return bytesRead;
    317     }
    318
    319
    320     /****
    321      * Requests a named file from a remote host, writes the
    322      * file to an OutputStream, closes the connection, and returns the number
    323      * of bytes read.  A local UDP socket must first be created by
    324      * <a href="org.apache.commons.net.DatagramSocketClient.html#open">open()</a> before
    325      * invoking this method.  This method will not close the OutputStream
    326      * containing the file; you must close it after the method invocation.
    327      * <p>
    328      * @param filename The name of the file to receive.
    329      * @param mode     The TFTP mode of the transfer (one of the MODE constants).
    330      * @param output   The OutputStream to which the file should be written.
    331      * @param hostname The name of the remote host serving the file.
    332      * @param port     The port number of the remote TFTP server.
    333      * @exception IOException If an I/O error occurs.  The nature of the
    334      *            error will be reported in the message.
    335      * @exception UnknownHostException  If the hostname cannot be resolved.
    336      ***/
    337     public int receiveFile(String filename, int mode, OutputStream output,
    338                            String hostname, int port)
    339     throws UnknownHostException, IOException
    340     {
    341         return receiveFile(filename, mode, output, InetAddress.getByName(hostname),
    342                            port);
    343     }
    344
    345
    346     /****
    347      * Same as calling receiveFile(filename, mode, output, host, TFTP.DEFAULT_PORT).
    348      *
    349      * @param filename The name of the file to receive.
    350      * @param mode     The TFTP mode of the transfer (one of the MODE constants).
    351      * @param output   The OutputStream to which the file should be written.
    352      * @param host     The remote host serving the file.
    353      * @exception IOException If an I/O error occurs.  The nature of the
    354      *            error will be reported in the message.
    355      ***/
    356     public int receiveFile(String filename, int mode, OutputStream output,
    357                            InetAddress host)
    358     throws IOException
    359     {
    360         return receiveFile(filename, mode, output, host, DEFAULT_PORT);
    361     }
    362
    363     /****
    364      * Same as calling receiveFile(filename, mode, output, hostname, TFTP.DEFAULT_PORT).
    365      *
    366      * @param filename The name of the file to receive.
    367      * @param mode     The TFTP mode of the transfer (one of the MODE constants).
    368      * @param output   The OutputStream to which the file should be written.
    369      * @param hostname The name of the remote host serving the file.
    370      * @exception IOException If an I/O error occurs.  The nature of the
    371      *            error will be reported in the message.
    372      * @exception UnknownHostException  If the hostname cannot be resolved.
    373      ***/
    374     public int receiveFile(String filename, int mode, OutputStream output,
    375                            String hostname)
    376     throws UnknownHostException, IOException
    377     {
    378         return receiveFile(filename, mode, output, InetAddress.getByName(hostname),
    379                            DEFAULT_PORT);
    380     }
    381
    382
    383     /****
    384      * Requests to send a file to a remote host, reads the file from an
    385      * InputStream, sends the file to the remote host, and closes the
    386      * connection.  A local UDP socket must first be created by
    387      * <a href="org.apache.commons.net.DatagramSocketClient.html#open">open()</a> before
    388      * invoking this method.  This method will not close the InputStream
    389      * containing the file; you must close it after the method invocation.
    390      * <p>
    391      * @param filename The name the remote server should use when creating
    392      *        the file on its file system.
    393      * @param mode     The TFTP mode of the transfer (one of the MODE constants).
    394      * @param output   The InputStream containing the file.
    395      * @param host     The remote host receiving the file.
    396      * @param port     The port number of the remote TFTP server.
    397      * @exception IOException If an I/O error occurs.  The nature of the
    398      *            error will be reported in the message.
    399      ***/
    400     public void sendFile(String filename, int mode, InputStream input,
    401                          InetAddress host, int port) throws IOException
    402     {
    403         int bytesRead, timeouts, lastBlock, block, hostPort, dataLength, offset;
    404         TFTPPacket sent, received = null;
    405         TFTPErrorPacket error;
    406         TFTPDataPacket data =
    407             new TFTPDataPacket(host, port, 0, _sendBuffer, 4, 0);
    408         ;
    409         TFTPAckPacket ack;
    410
    411         beginBufferedOps();
    412
    413         dataLength = lastBlock = hostPort = bytesRead = 0;
    414         block = 0;
    415
    416         if (mode == TFTP.ASCII_MODE)
    417             input = new ToNetASCIIInputStream(input);
    418
    419         sent =
    420             new TFTPWriteRequestPacket(host, port, filename, mode);
    421
    422 _sendPacket:
    423         do
    424         {
    425             bufferedSend(sent);
    426
    427 _receivePacket:
    428             while (true)
    429             {
    430                 timeouts = 0;
    431                 while (timeouts < __maxTimeouts)
    432                 {
    433                     try
    434                     {
    435                         received = bufferedReceive();
    436                         break;
    437                     }
    438                     catch (SocketException e)
    439                     {
    440                         if (++timeouts >= __maxTimeouts)
    441                         {
    442                             endBufferedOps();
    443                             throw new IOException("Connection timed out.");
    444                         }
    445                         continue;
    446                     }
    447                     catch (InterruptedIOException e)
    448                     {
    449                         if (++timeouts >= __maxTimeouts)
    450                         {
    451                             endBufferedOps();
    452                             throw new IOException("Connection timed out.");
    453                         }
    454                         continue;
    455                     }
    456                     catch (TFTPPacketException e)
    457                     {
    458                         endBufferedOps();
    459                         throw new IOException("Bad packet: " + e.getMessage());
    460                     }
    461                 }
    462
    463                 // The first time we receive we get the port number and
    464 		// answering host address (for hosts with multiple IPs)
    465                 if (lastBlock == 0)
    466                 {
    467                     hostPort = received.getPort();
    468                     data.setPort(hostPort);
    469                     if(!host.equals(received.getAddress()))
    470                     {
    471                         host = received.getAddress();
    472                         data.setAddress(host);
    473                         sent.setAddress(host);
    474                     }
    475                 }
    476
    477                 // Comply with RFC 783 indication that an error acknowledgement
    478                 // should be sent to originator if unexpected TID or host.
    479                 if (host.equals(received.getAddress()) &&
    480                         received.getPort() == hostPort)
    481                 {
    482
    483                     switch (received.getType())
    484                     {
    485                     case TFTPPacket.ERROR:
    486                         error = (TFTPErrorPacket)received;
    487                         endBufferedOps();
    488                         throw new IOException("Error code " + error.getError() +
    489                                               " received: " + error.getMessage());
    490                     case TFTPPacket.ACKNOWLEDGEMENT:
    491                         ack = (TFTPAckPacket)received;
    492
    493                         lastBlock = ack.getBlockNumber();
    494
    495                         if (lastBlock == block)
    496                         {
    497                             ++block;
    498                             break _receivePacket;
    499                         }
    500                         else
    501                         {
    502                             discardPackets();
    503
    504                             if (lastBlock == (block - 1))
    505                                 continue _sendPacket;  // Resend last acknowledgement.
    506
    507                             continue _receivePacket; // Start fetching packets again.
    508                         }
    509                         //break;
    510
    511                     default:
    512                         endBufferedOps();
    513                         throw new IOException("Received unexpected packet type.");
    514                     }
    515                 }
    516                 else
    517                 {
    518                     error = new TFTPErrorPacket(received.getAddress(),
    519                                                 received.getPort(),
    520                                                 TFTPErrorPacket.UNKNOWN_TID,
    521                                                 "Unexpected host or port.");
    522                     bufferedSend(error);
    523                     continue _sendPacket;
    524                 }
    525
    526                 // We should never get here, but this is a safety to avoid
    527                 // infinite loop.  If only Java had the goto statement.
    528                 //break;
    529             }
    530
    531             dataLength = TFTPPacket.SEGMENT_SIZE;
    532             offset = 4;
    533             while (dataLength > 0 &&
    534                     (bytesRead = input.read(_sendBuffer, offset, dataLength)) > 0)
    535             {
    536                 offset += bytesRead;
    537                 dataLength -= bytesRead;
    538             }
    539
    540             data.setBlockNumber(block);
    541             data.setData(_sendBuffer, 4, offset - 4);
    542             sent = data;
    543         }
    544         while (dataLength == 0);
    545
    546         bufferedSend(sent);
    547         endBufferedOps();
    548     }
    549
    550
    551     /****
    552      * Requests to send a file to a remote host, reads the file from an
    553      * InputStream, sends the file to the remote host, and closes the
    554      * connection.  A local UDP socket must first be created by
    555      * <a href="org.apache.commons.net.DatagramSocketClient.html#open">open()</a> before
    556      * invoking this method.  This method will not close the InputStream
    557      * containing the file; you must close it after the method invocation.
    558      * <p>
    559      * @param filename The name the remote server should use when creating
    560      *        the file on its file system.
    561      * @param mode     The TFTP mode of the transfer (one of the MODE constants).
    562      * @param output   The InputStream containing the file.
    563      * @param hostname The name of the remote host receiving the file.
    564      * @param port     The port number of the remote TFTP server.
    565      * @exception IOException If an I/O error occurs.  The nature of the
    566      *            error will be reported in the message.
    567      * @exception UnknownHostException  If the hostname cannot be resolved.
    568      ***/
    569     public void sendFile(String filename, int mode, InputStream input,
    570                          String hostname, int port)
    571     throws UnknownHostException, IOException
    572     {
    573         sendFile(filename, mode, input, InetAddress.getByName(hostname), port);
    574     }
    575
    576
    577     /****
    578      * Same as calling sendFile(filename, mode, input, host, TFTP.DEFAULT_PORT).
    579      *
    580      * @param filename The name the remote server should use when creating
    581      *        the file on its file system.
    582      * @param mode     The TFTP mode of the transfer (one of the MODE constants).
    583      * @param output   The InputStream containing the file.
    584      * @param hostname The name of the remote host receiving the file.
    585      * @param port     The port number of the remote TFTP server.
    586      * @exception IOException If an I/O error occurs.  The nature of the
    587      *            error will be reported in the message.
    588      * @exception UnknownHostException  If the hostname cannot be resolved.
    589      ***/
    590     public void sendFile(String filename, int mode, InputStream input,
    591                          InetAddress host)
    592     throws IOException
    593     {
    594         sendFile(filename, mode, input, host, DEFAULT_PORT);
    595     }
    596
    597     /****
    598      * Same as calling sendFile(filename, mode, input, hostname, TFTP.DEFAULT_PORT).
    599      *
    600      * @param filename The name the remote server should use when creating
    601      *        the file on its file system.
    602      * @param mode     The TFTP mode of the transfer (one of the MODE constants).
    603      * @param output   The InputStream containing the file.
    604      * @param hostname The name of the remote host receiving the file.
    605      * @param port     The port number of the remote TFTP server.
    606      * @exception IOException If an I/O error occurs.  The nature of the
    607      *            error will be reported in the message.
    608      * @exception UnknownHostException  If the hostname cannot be resolved.
    609      ***/
    610     public void sendFile(String filename, int mode, InputStream input,
    611                          String hostname)
    612     throws UnknownHostException, IOException
    613     {
    614         sendFile(filename, mode, input, InetAddress.getByName(hostname),
    615                  DEFAULT_PORT);
    616     }
    617 }

This page was automatically generated by Maven