Warnings purge
[people/xl0/gpxe.git] / src / proto / tftm.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  * TFTM protocol
10  *
11  * TFTM is a protocol defined in RFC2090 as a multicast extension to
12  * TFTP.
13  */
14
15 static inline int tftm_process_opts ( struct tftp_state *state,
16                                       struct tftp_oack *oack ) {
17         struct in_addr old_mcast_addr = state->multicast.sin_addr;
18
19         if ( ! tftp_process_opts ( state, oack ) )
20                 return 0;
21
22         if ( old_mcast_addr.s_addr != state->multicast.sin_addr.s_addr ) {
23                 if ( old_mcast_addr.s_addr ) {
24                         DBG ( "TFTM: Leaving multicast group %@\n",
25                               old_mcast_addr.s_addr );
26                         leave_group ( IGMP_SERVER );
27                 }
28                 DBG ( "TFTM: Joining multicast group %@\n",
29                       state->multicast.sin_addr.s_addr );
30                 join_group ( IGMP_SERVER, state->multicast.sin_addr.s_addr );
31         }
32
33         DBG ( "TFTM: I am a %s client\n",
34               ( state->master ? "master" : "slave" ) );
35
36         return 1;
37 }
38
39
40 static inline int tftm_process_data ( struct tftp_state *state,
41                                       struct tftp_data *data,
42                                       struct buffer *buffer ) {
43         unsigned int blksize;
44         off_t offset;
45
46         /* Calculate block size and offset within file */
47         blksize = ( ntohs ( data->udp.len )
48                     + offsetof ( typeof ( *data ), udp )
49                     - offsetof ( typeof ( *data ), data ) );
50         offset = ( ntohs ( data->block ) - 1 ) * state->blksize;
51
52         /* Check for oversized block */
53         if ( blksize > state->blksize ) {
54                 DBG ( "TFTM: oversized block size %d (max %d)\n",
55                       blksize, state->blksize );
56                 errno = PXENV_STATUS_TFTP_INVALID_PACKET_SIZE;
57                 return 0;
58         }
59
60         /* Place block in the buffer */
61         if ( ! fill_buffer ( buffer, data->data, offset, blksize ) ) {
62                 DBG ( "TFTM: could not place data in buffer: %m\n" );
63                 return 0;
64         }
65
66         /* If this is the last block, record the filesize (in case the
67          * server didn't supply a tsize option.
68          */
69         if ( blksize < state->blksize ) {
70                 state->tsize = offset + blksize;
71         }
72
73         /* Record the last received block */
74         state->block = ntohs ( data->block );
75
76         return 1;
77 }
78
79
80 static inline int tftm_next ( struct tftp_state *state,
81                               union tftp_any **reply,
82                               struct buffer *buffer ) {
83         long listen_timeout;
84
85         listen_timeout = rfc2131_sleep_interval ( TIMEOUT, MAX_TFTP_RETRIES );
86
87         /* If we are not the master client, just listen for the next
88          * packet
89          */
90         if ( ! state->master ) {
91                 if ( tftp_get ( state, listen_timeout, reply ) ) {
92                         /* Heard a non-error packet */
93                         return 1;
94                 }
95                 if ( *reply ) {
96                         /* Received an error packet */
97                         return 0;
98                 }
99                 /* Didn't hear anything; try prodding the server */
100                 state->master = 1;
101         }
102         /* We are the master client; trigger the next packet
103          * that we want
104          */
105         state->block = buffer->fill / state->blksize;
106         return tftp_ack ( state, reply );
107 }
108
109 /**
110  * Download a file via TFTM
111  *
112  * @v server                            TFTP server
113  * @v file                              File name
114  * @v buffer                            Buffer into which to load file
115  * @ret True                            File was downloaded successfully
116  * @ret False                           File was not downloaded successfully
117  * @err #PXENV_STATUS_TFTP_UNKNOWN_OPCODE Unknown type of TFTP block received
118  * @err other                           As returned by tftp_open()
119  * @err other                           As returned by tftp_process_opts()
120  * @err other                           As returned by tftp_ack()
121  * @err other                           As returned by tftp_process_data()
122  *
123  * Download a file from a TFTP server into the specified buffer using
124  * the TFTM protocol.
125  */
126 static int tftm ( char *url __unused, struct sockaddr_in *server, char *file,
127                   struct buffer *buffer ) {
128         struct tftp_state state;
129         union tftp_any *reply;
130         int rc = 0;
131
132         /* Initialise TFTP state */
133         memset ( &state, 0, sizeof ( state ) );
134         state.server = *server;
135
136         /* Start as the master.  This means that if the TFTP server
137          * doesn't actually support multicast, we'll still ACK the
138          * packets and it should all proceed as for a normal TFTP
139          * connection.
140          */
141         state.master = 1;
142         
143         /* Open the file */
144         if ( ! tftp_open ( &state, file, &reply, 1 ) ) {
145                 DBG ( "TFTM: could not open %@:%d/%s : %m\n",
146                       server->sin_addr.s_addr, server->sin_port, file );
147                 return 0;
148         }
149
150         /* Fetch file, a block at a time */
151         while ( 1 ) {
152                 twiddle();
153                 /* Process the current packet */
154                 switch ( ntohs ( reply->common.opcode ) ) {
155                 case TFTP_OACK:
156                         /* Options can be received at any time */
157                         if ( ! tftm_process_opts ( &state, &reply->oack ) ) {
158                                 DBG ( "TFTM: failed to process OACK: %m\n" );
159                                 tftp_error ( &state, TFTP_ERR_BAD_OPTS, NULL );
160                                 goto out;
161                         }
162                         break;
163                 case TFTP_DATA:
164                         if ( ! tftm_process_data ( &state, &reply->data,
165                                                    buffer ) ) {
166                                 DBG ( "TFTM: failed to process DATA: %m\n" );
167                                 tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
168                                              NULL );
169                                 goto out;
170                         }
171                         break;
172                 default:
173                         DBG ( "TFTM: unexpected packet type %d\n",
174                               ntohs ( reply->common.opcode ) );
175                         errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
176                         tftp_error ( &state, TFTP_ERR_ILLEGAL_OP, NULL );
177                         goto out;
178                 }
179                 /* If we know the filesize, and we have all the data, stop */
180                 if ( state.tsize && ( buffer->fill == state.tsize ) )
181                         break;
182                 /* Fetch the next packet */
183                 if ( ! tftm_next ( &state, &reply, buffer ) ) {
184                         DBG ( "TFTM: could not get next block: %m\n" );
185                         if ( ! reply ) {
186                                 tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
187                                              NULL );
188                         }
189                         goto out;
190                 }
191         }
192
193         /* ACK the final packet, as a courtesy to the server */
194         tftp_ack_nowait ( &state );
195
196         rc = 1;
197  out:
198         if ( state.multicast.sin_addr.s_addr ) {
199                 leave_group ( IGMP_SERVER );
200         }
201         return rc;
202 }
203
204 static struct protocol tftm_protocol __protocol = {
205         .name = "x-tftm",
206         .default_port = TFTP_PORT,
207         .load = tftm,
208 };