53706448dd53469f2d4826304dcf4759a17497a8
[people/xl0/gpxe.git] / src / proto / tftp.c
1 #include "etherboot.h"
2 #include "proto.h"
3 #include "errno.h"
4 #include "tftp.h"
5 #include "tftpcore.h"
6
7 /** @file
8  *
9  * TFTP protocol
10  */
11
12 /**
13  * Process a TFTP block
14  *
15  * @v state                     TFTP transfer state
16  * @v tftp_state::block         Last received data block
17  * @v tftp_state::blksize       Transfer block size
18  * @v data                      The data block to process
19  * @v buffer                    The buffer to fill with the data
20  * @ret True                    Block processed successfully
21  * @ret False                   Block not processed successfully
22  * @ret tftp_state::block       Incremented if applicable
23  * @ret *eof                    End-of-file marker
24  * @err #PXENV_STATUS_TFTP_INVALID_PACKET_SIZE Packet is too large
25  * @err other                   As returned by fill_buffer()
26  *
27  * Process a TFTP DATA packet that has been received.  If the data
28  * packet is the next data packet in the stream, its contents will be
29  * placed in the #buffer and tftp_state::block will be incremented.
30  * If the packet is the final packet, end-of-file will be indicated
31  * via #eof.
32  *
33  * If the data packet is a duplicate, then process_tftp_data() will
34  * still return True, though nothing will be done with the packet.  A
35  * False return value always indicates an error that should abort the
36  * transfer.
37  */
38 static inline int process_tftp_data ( struct tftp_state *state,
39                                       struct tftp_data *data,
40                                       struct buffer *buffer,
41                                       int *eof ) {
42         unsigned int blksize;
43
44         /* Check it's the correct DATA block */
45         if ( ntohs ( data->block ) != ( state->block + 1 ) ) {
46                 DBG ( "TFTP: got block %d, wanted block %d\n",
47                       ntohs ( data->block ), state->block + 1 );
48                 return 1;
49         }
50         /* Check it's an acceptable size */
51         blksize = ( ntohs ( data->udp.len )
52                     + offsetof ( typeof ( *data ), udp )
53                     - offsetof ( typeof ( *data ), data ) );
54         if ( blksize > state->blksize ) {
55                 DBG ( "TFTP: oversized block size %d (max %d)\n",
56                       blksize, state->blksize );
57                 errno = PXENV_STATUS_TFTP_INVALID_PACKET_SIZE;
58                 return 0;
59         }
60         /* Place block in the buffer */
61         if ( ! fill_buffer ( buffer, data->data, state->block * state->blksize,
62                              blksize ) ) {
63                 DBG ( "TFTP: could not place data in buffer: %m\n" );
64                 return 0;
65         }
66         /* Increment block counter */
67         state->block++;
68         /* Set EOF marker */
69         *eof = ( blksize < state->blksize );
70         return 1;
71 }
72
73 /**
74  * Download a file via TFTP
75  *
76  * @v server                            TFTP server
77  * @v file                              File name
78  * @v buffer                            Buffer into which to load file
79  * @ret True                            File was downloaded successfully
80  * @ret False                           File was not downloaded successfully
81  * @err #PXENV_STATUS_TFTP_UNKNOWN_OPCODE Unknown type of TFTP block received
82  * @err other                           As returned by tftp_open()
83  * @err other                           As returned by tftp_process_opts()
84  * @err other                           As returned by tftp_ack()
85  * @err other                           As returned by tftp_process_data()
86  *
87  * Download a file from a TFTP server into the specified buffer.
88  */
89 static int tftp ( char *url __unused, struct sockaddr_in *server, char *file,
90                   struct buffer *buffer ) {
91         struct tftp_state state;
92         union tftp_any *reply;
93         int eof = 0;
94
95         /* Initialise TFTP state */
96         memset ( &state, 0, sizeof ( state ) );
97         state.server = *server;
98         
99         /* Open the file */
100         if ( ! tftp_open ( &state, file, &reply ) ) {
101                 DBG ( "TFTP: could not open %@:%d/%s : %m\n",
102                       server->sin_addr.s_addr, server->sin_port, file );
103                 return 0;
104         }
105         
106         /* Process OACK, if any */
107         if ( ntohs ( reply->common.opcode ) == TFTP_OACK ) {
108                 if ( ! tftp_process_opts ( &state, &reply->oack ) ) {
109                         DBG ( "TFTP: option processing failed : %m\n" );
110                         return 0;
111                 }
112                 reply = NULL;
113         }
114
115         /* Fetch file, a block at a time */
116         do {
117                 /* Get next block to process.  (On the first time
118                  * through, we may already have a block from
119                  * tftp_open()).
120                  */
121                 if ( ! reply ) {
122                         if ( ! tftp_ack ( &state, &reply ) ) {
123                                 DBG ( "TFTP: could not get next block: %m\n" );
124                                 return 0;
125                         }
126                 }
127                 twiddle();
128                 /* Check it's a DATA block */
129                 if ( ntohs ( reply->common.opcode ) != TFTP_DATA ) {
130                         DBG ( "TFTP: unexpected opcode %d\n",
131                               ntohs ( reply->common.opcode ) );
132                         errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
133                         return 0;
134                 }
135                 /* Process the DATA block */
136                 if ( ! process_tftp_data ( &state, &reply->data, buffer,
137                                            &eof ) )
138                         return 0;
139                 reply = NULL;
140         } while ( ! eof );
141
142         /* ACK the final packet, as a courtesy to the server */
143         tftp_ack_nowait ( &state );
144
145         return 1;
146 }
147
148 struct protocol tftp_protocol __default_protocol = {
149         .name = "tftp",
150         .default_port = TFTP_PORT,
151         .load = tftp,
152 };