123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- #include "etherboot.h"
- #include "proto.h"
- #include "errno.h"
- #include "tftp.h"
- #include "tftpcore.h"
-
- /** @file
- *
- * TFTP protocol
- */
-
- /**
- * Process a TFTP block
- *
- * @v state TFTP transfer state
- * @v tftp_state::block Last received data block
- * @v tftp_state::blksize Transfer block size
- * @v data The data block to process
- * @v buffer The buffer to fill with the data
- * @ret True Block processed successfully
- * @ret False Block not processed successfully
- * @ret tftp_state::block Incremented if applicable
- * @ret *eof End-of-file marker
- * @err #PXENV_STATUS_TFTP_INVALID_PACKET_SIZE Packet is too large
- * @err other As returned by fill_buffer()
- *
- * Process a TFTP DATA packet that has been received. If the data
- * packet is the next data packet in the stream, its contents will be
- * placed in the #buffer and tftp_state::block will be incremented.
- * If the packet is the final packet, end-of-file will be indicated
- * via #eof.
- *
- * If the data packet is a duplicate, then process_tftp_data() will
- * still return True, though nothing will be done with the packet. A
- * False return value always indicates an error that should abort the
- * transfer.
- */
- static inline int tftp_process_data ( struct tftp_state *state,
- struct tftp_data *data,
- struct buffer *buffer,
- int *eof ) {
- unsigned int blksize;
-
- /* Check it's the correct DATA block */
- if ( ntohs ( data->block ) != ( state->block + 1 ) ) {
- DBG ( "TFTP: got block %d, wanted block %d\n",
- ntohs ( data->block ), state->block + 1 );
- return 1;
- }
- /* Check it's an acceptable size */
- blksize = ( ntohs ( data->udp.len )
- + offsetof ( typeof ( *data ), udp )
- - offsetof ( typeof ( *data ), data ) );
- if ( blksize > state->blksize ) {
- DBG ( "TFTP: oversized block size %d (max %d)\n",
- blksize, state->blksize );
- errno = PXENV_STATUS_TFTP_INVALID_PACKET_SIZE;
- return 0;
- }
- /* Place block in the buffer */
- if ( ! fill_buffer ( buffer, data->data, state->block * state->blksize,
- blksize ) ) {
- DBG ( "TFTP: could not place data in buffer: %m\n" );
- return 0;
- }
- /* Increment block counter */
- state->block++;
- /* Set EOF marker */
- *eof = ( blksize < state->blksize );
- return 1;
- }
-
- /**
- * Download a file via TFTP
- *
- * @v server TFTP server
- * @v file File name
- * @v buffer Buffer into which to load file
- * @ret True File was downloaded successfully
- * @ret False File was not downloaded successfully
- * @err #PXENV_STATUS_TFTP_UNKNOWN_OPCODE Unknown type of TFTP block received
- * @err other As returned by tftp_open()
- * @err other As returned by tftp_process_opts()
- * @err other As returned by tftp_ack()
- * @err other As returned by tftp_process_data()
- *
- * Download a file from a TFTP server into the specified buffer.
- */
- static int tftp ( char *url __unused, struct sockaddr_in *server, char *file,
- struct buffer *buffer ) {
- struct tftp_state state;
- union tftp_any *reply;
- int eof = 0;
-
- /* Initialise TFTP state */
- memset ( &state, 0, sizeof ( state ) );
- state.server = *server;
-
- /* Open the file */
- if ( ! tftp_open ( &state, file, &reply, 0 ) ) {
- DBG ( "TFTP: could not open %@:%d/%s : %m\n",
- server->sin_addr.s_addr, server->sin_port, file );
- return 0;
- }
-
- /* Fetch file, a block at a time */
- while ( 1 ) {
- twiddle();
- switch ( ntohs ( reply->common.opcode ) ) {
- case TFTP_DATA:
- if ( ! tftp_process_data ( &state, &reply->data,
- buffer, &eof ) ) {
- tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
- NULL );
- return 0;
- }
- break;
- case TFTP_OACK:
- if ( state.block ) {
- /* OACK must be first block, if present */
- DBG ( "TFTP: OACK after block %d\n",
- state.block );
- errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
- tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
- NULL );
- return 0;
- }
- if ( ! tftp_process_opts ( &state, &reply->oack ) ) {
- DBG ( "TFTP: option processing failed: %m\n" );
- tftp_error ( &state, TFTP_ERR_BAD_OPTS, NULL );
- return 0;
- }
- break;
- default:
- DBG ( "TFTP: unexpected opcode %d\n",
- ntohs ( reply->common.opcode ) );
- errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
- tftp_error ( &state, TFTP_ERR_ILLEGAL_OP, NULL );
- return 0;
- }
- /* If we have reached EOF, stop here */
- if ( eof )
- break;
- /* Fetch the next data block */
- if ( ! tftp_ack ( &state, &reply ) ) {
- DBG ( "TFTP: could not get next block: %m\n" );
- if ( ! reply ) {
- tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
- NULL );
- }
- return 0;
- }
- }
-
- /* ACK the final packet, as a courtesy to the server */
- tftp_ack_nowait ( &state );
-
- return 1;
- }
-
- struct protocol tftp_protocol __default_protocol = {
- .name = "tftp",
- .default_port = TFTP_PORT,
- .load = tftp,
- };
|