Coverage report

  %line %branch
org.apache.commons.net.tftp.TFTPClient
0% 
0% 

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

This report is generated by jcoverage, Maven and Maven JCoverage Plugin.