|
@@ -1,169 +1,111 @@
|
1
|
1
|
#include "etherboot.h"
|
2
|
|
-#include "in.h"
|
3
|
|
-#include "nic.h"
|
4
|
2
|
#include "proto.h"
|
|
3
|
+#include "errno.h"
|
5
|
4
|
#include "tftp.h"
|
|
5
|
+#include "tftpcore.h"
|
6
|
6
|
|
7
|
|
-/* Utility function for tftp_block() */
|
8
|
|
-static int await_tftp ( int ival, void *ptr __unused,
|
9
|
|
- unsigned short ptype __unused, struct iphdr *ip,
|
10
|
|
- struct udphdr *udp, struct tcphdr *tcp __unused ) {
|
11
|
|
- if ( ! udp ) {
|
12
|
|
- return 0;
|
13
|
|
- }
|
14
|
|
- if ( arptable[ARP_CLIENT].ipaddr.s_addr != ip->dest.s_addr )
|
15
|
|
- return 0;
|
16
|
|
- if ( ntohs ( udp->dest ) != ival )
|
17
|
|
- return 0;
|
18
|
|
- return 1;
|
19
|
|
-}
|
|
7
|
+/** @file
|
|
8
|
+ *
|
|
9
|
+ * TFTP protocol
|
|
10
|
+ */
|
20
|
11
|
|
21
|
|
-/*
|
22
|
|
- * Download a single block via TFTP. This function is non-static so
|
23
|
|
- * that pxe_export.c can call it.
|
|
12
|
+/**
|
|
13
|
+ * Process a TFTP block
|
24
|
14
|
*
|
25
|
15
|
*/
|
26
|
|
-int tftp_block ( struct tftpreq_info_t *request,
|
27
|
|
- struct tftpblk_info_t *block ) {
|
28
|
|
- static struct sockaddr_in server;
|
29
|
|
- static unsigned short lport = 2000; /* local port */
|
30
|
|
- struct tftp_t *rcvd = NULL;
|
31
|
|
- static struct tftpreq_t xmit;
|
32
|
|
- static unsigned short xmitlen = 0;
|
33
|
|
- static unsigned short blockidx = 0; /* Last block received */
|
34
|
|
- static unsigned short retry = 0; /* Retry attempts on last block */
|
35
|
|
- static int blksize = 0;
|
36
|
|
- unsigned short recvlen = 0;
|
|
16
|
+static inline int process_tftp_data ( struct tftp_state *state,
|
|
17
|
+ struct tftp_data *data,
|
|
18
|
+ struct buffer *buffer,
|
|
19
|
+ int *eof ) {
|
|
20
|
+ unsigned int blksize;
|
37
|
21
|
|
38
|
|
- /* If this is a new request (i.e. if name is set), fill in
|
39
|
|
- * transmit block with RRQ and send it.
|
40
|
|
- */
|
41
|
|
- if ( request ) {
|
42
|
|
- rx_qdrain(); /* Flush receive queue */
|
43
|
|
- xmit.opcode = htons(TFTP_RRQ);
|
44
|
|
- xmitlen = (void*)&xmit.u.rrq - (void*)&xmit +
|
45
|
|
- sprintf((char*)xmit.u.rrq, "%s%coctet%cblksize%c%d",
|
46
|
|
- request->name, 0, 0, 0, request->blksize)
|
47
|
|
- + 1; /* null terminator */
|
48
|
|
- blockidx = 0; /* Reset counters */
|
49
|
|
- retry = 0;
|
50
|
|
- blksize = TFTP_DEFAULTSIZE_PACKET;
|
51
|
|
- lport++; /* Use new local port */
|
52
|
|
- server = *(request->server);
|
53
|
|
- if ( !udp_transmit(server.sin_addr.s_addr, lport,
|
54
|
|
- server.sin_port, xmitlen, &xmit) )
|
55
|
|
- return (0);
|
|
22
|
+ /* Check it's the correct DATA block */
|
|
23
|
+ if ( ntohs ( data->block ) != ( state->block + 1 ) ) {
|
|
24
|
+ DBG ( "TFTP: got block %d, wanted block %d\n",
|
|
25
|
+ ntohs ( data->block ), state->block + 1 );
|
|
26
|
+ return 1;
|
56
|
27
|
}
|
57
|
|
- /* Exit if no transfer in progress */
|
58
|
|
- if ( !blksize ) return (0);
|
59
|
|
- /* Loop to wait until we get a packet we're interested in */
|
60
|
|
- block->data = NULL; /* Used as flag */
|
61
|
|
- while ( block->data == NULL ) {
|
62
|
|
- long timeout = rfc2131_sleep_interval ( blockidx ? TFTP_REXMT :
|
63
|
|
- TIMEOUT, retry );
|
64
|
|
- if ( !await_reply(await_tftp, lport, NULL, timeout) ) {
|
65
|
|
- /* No packet received */
|
66
|
|
- if ( retry++ > MAX_TFTP_RETRIES ) break;
|
67
|
|
- /* Retransmit last packet */
|
68
|
|
- if ( !blockidx ) lport++; /* New lport if new RRQ */
|
69
|
|
- if ( !udp_transmit(server.sin_addr.s_addr, lport,
|
70
|
|
- server.sin_port, xmitlen, &xmit) )
|
71
|
|
- return (0);
|
72
|
|
- continue; /* Back to waiting for packet */
|
73
|
|
- }
|
74
|
|
- /* Packet has been received */
|
75
|
|
- rcvd = (struct tftp_t *)&nic.packet[ETH_HLEN];
|
76
|
|
- recvlen = ntohs(rcvd->udp.len) - sizeof(struct udphdr)
|
77
|
|
- - sizeof(rcvd->opcode);
|
78
|
|
- server.sin_port = ntohs(rcvd->udp.src);
|
79
|
|
- retry = 0; /* Reset retry counter */
|
80
|
|
- switch ( htons(rcvd->opcode) ) {
|
81
|
|
- case TFTP_ERROR : {
|
82
|
|
- printf ( "TFTP error %d (%s)\n",
|
83
|
|
- ntohs(rcvd->u.err.errcode),
|
84
|
|
- rcvd->u.err.errmsg );
|
85
|
|
- return (0); /* abort */
|
86
|
|
- }
|
87
|
|
- case TFTP_OACK : {
|
88
|
|
- const char *p = rcvd->u.oack.data;
|
89
|
|
- const char *e = p + recvlen - 10; /* "blksize\0\d\0" */
|
90
|
|
-
|
91
|
|
- *((char*)(p+recvlen-1)) = '\0'; /* Force final 0 */
|
92
|
|
- if ( blockidx || !request ) break; /* Too late */
|
93
|
|
- if ( recvlen <= TFTP_MAX_PACKET ) /* sanity */ {
|
94
|
|
- /* Check for blksize option honoured */
|
95
|
|
- while ( p < e ) {
|
96
|
|
- if ( strcasecmp("blksize",p) == 0 &&
|
97
|
|
- p[7] == '\0' ) {
|
98
|
|
- blksize = strtoul(p+8,&p,10);
|
99
|
|
- p++; /* skip null */
|
100
|
|
- }
|
101
|
|
- while ( *(p++) ) {};
|
102
|
|
- }
|
103
|
|
- }
|
104
|
|
- if ( blksize < TFTP_DEFAULTSIZE_PACKET ||
|
105
|
|
- blksize > request->blksize ) {
|
106
|
|
- /* Incorrect blksize - error and abort */
|
107
|
|
- xmit.opcode = htons(TFTP_ERROR);
|
108
|
|
- xmit.u.err.errcode = 8;
|
109
|
|
- xmitlen = (void*)&xmit.u.err.errmsg
|
110
|
|
- - (void*)&xmit
|
111
|
|
- + sprintf((char*)xmit.u.err.errmsg,
|
112
|
|
- "RFC1782 error")
|
113
|
|
- + 1;
|
114
|
|
- udp_transmit(server.sin_addr.s_addr, lport,
|
115
|
|
- server.sin_port, xmitlen, &xmit);
|
116
|
|
- return (0);
|
117
|
|
- }
|
118
|
|
- } break;
|
119
|
|
- case TFTP_DATA :
|
120
|
|
- if ( ntohs(rcvd->u.data.block) != ( blockidx + 1 ) )
|
121
|
|
- break; /* Re-ACK last block sent */
|
122
|
|
- if ( recvlen > ( blksize+sizeof(rcvd->u.data.block) ) )
|
123
|
|
- break; /* Too large; ignore */
|
124
|
|
- block->data = rcvd->u.data.download;
|
125
|
|
- block->block = ++blockidx;
|
126
|
|
- block->len = recvlen - sizeof(rcvd->u.data.block);
|
127
|
|
- block->eof = ( (unsigned short)block->len < blksize );
|
128
|
|
- /* If EOF, zero blksize to indicate transfer done */
|
129
|
|
- if ( block->eof ) blksize = 0;
|
130
|
|
- break;
|
131
|
|
- default: break; /* Do nothing */
|
132
|
|
- }
|
133
|
|
- /* Send ACK */
|
134
|
|
- xmit.opcode = htons(TFTP_ACK);
|
135
|
|
- xmit.u.ack.block = htons(blockidx);
|
136
|
|
- xmitlen = TFTP_MIN_PACKET;
|
137
|
|
- udp_transmit ( server.sin_addr.s_addr, lport, server.sin_port,
|
138
|
|
- xmitlen, &xmit );
|
|
28
|
+ /* Check it's an acceptable size */
|
|
29
|
+ blksize = ( ntohs ( data->udp.len )
|
|
30
|
+ + offsetof ( typeof ( *data ), udp )
|
|
31
|
+ - offsetof ( typeof ( *data ), data ) );
|
|
32
|
+ if ( blksize > state->blksize ) {
|
|
33
|
+ DBG ( "TFTP: oversized block size %d (max %d)\n",
|
|
34
|
+ blksize, state->blksize );
|
|
35
|
+ return 1;
|
139
|
36
|
}
|
140
|
|
- return ( block->data ? 1 : 0 );
|
|
37
|
+ /* Place block in the buffer */
|
|
38
|
+ if ( ! fill_buffer ( buffer, data->data, state->block * state->blksize,
|
|
39
|
+ blksize ) ) {
|
|
40
|
+ DBG ( "TFTP: could not place data in buffer: %m\n" );
|
|
41
|
+ return 0;
|
|
42
|
+ }
|
|
43
|
+ /* Increment block counter */
|
|
44
|
+ state->block++;
|
|
45
|
+ /* Set EOF marker */
|
|
46
|
+ *eof = ( blksize < state->blksize );
|
|
47
|
+ return 1;
|
141
|
48
|
}
|
142
|
49
|
|
143
|
|
-/*
|
|
50
|
+/**
|
144
|
51
|
* Download a file via TFTP
|
145
|
52
|
*
|
146
|
53
|
*/
|
147
|
54
|
int tftp ( char *url __unused, struct sockaddr_in *server, char *file,
|
148
|
55
|
struct buffer *buffer ) {
|
149
|
|
- struct tftpreq_info_t request_data = {
|
150
|
|
- .server = server,
|
151
|
|
- .name = file,
|
152
|
|
- .blksize = TFTP_MAX_PACKET,
|
153
|
|
- };
|
154
|
|
- struct tftpreq_info_t *request = &request_data;
|
155
|
|
- struct tftpblk_info_t block;
|
156
|
|
- off_t offset = 0;
|
|
56
|
+ struct tftp_state state;
|
|
57
|
+ union tftp_any *reply;
|
|
58
|
+ int eof = 0;
|
|
59
|
+
|
|
60
|
+ /* Initialise TFTP state */
|
|
61
|
+ memset ( &state, 0, sizeof ( state ) );
|
|
62
|
+ state.server = *server;
|
|
63
|
+
|
|
64
|
+ /* Open the file */
|
|
65
|
+ if ( ! tftp_open ( &state, file, &reply ) ) {
|
|
66
|
+ DBG ( "TFTP: could not open %@:%d/%s : %m\n",
|
|
67
|
+ server->sin_addr.s_addr, server->sin_port, file );
|
|
68
|
+ return 0;
|
|
69
|
+ }
|
|
70
|
+
|
|
71
|
+ /* Process OACK, if any */
|
|
72
|
+ if ( ntohs ( reply->common.opcode ) == TFTP_OACK ) {
|
|
73
|
+ if ( ! tftp_process_opts ( &state, &reply->oack ) ) {
|
|
74
|
+ DBG ( "TFTP: option processing failed : %m\n" );
|
|
75
|
+ return 0;
|
|
76
|
+ }
|
|
77
|
+ reply = NULL;
|
|
78
|
+ }
|
157
|
79
|
|
|
80
|
+ /* Fetch file, a block at a time */
|
158
|
81
|
do {
|
159
|
|
- if ( ! tftp_block ( request, &block ) )
|
|
82
|
+ /* Get next block to process. (On the first time
|
|
83
|
+ * through, we may already have a block from
|
|
84
|
+ * tftp_open()).
|
|
85
|
+ */
|
|
86
|
+ if ( ! reply ) {
|
|
87
|
+ if ( ! tftp_ack ( &state, &reply ) ) {
|
|
88
|
+ DBG ( "TFTP: could not get next block: %m\n" );
|
|
89
|
+ return 0;
|
|
90
|
+ }
|
|
91
|
+ }
|
|
92
|
+ twiddle();
|
|
93
|
+ /* Check it's a DATA block */
|
|
94
|
+ if ( ntohs ( reply->common.opcode ) != TFTP_DATA ) {
|
|
95
|
+ DBG ( "TFTP: unexpected opcode %d\n",
|
|
96
|
+ ntohs ( reply->common.opcode ) );
|
|
97
|
+ errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
|
160
|
98
|
return 0;
|
161
|
|
- if ( ! fill_buffer ( buffer, block.data, offset, block.len ) )
|
|
99
|
+ }
|
|
100
|
+ /* Process the DATA block */
|
|
101
|
+ if ( ! process_tftp_data ( &state, &reply->data, buffer,
|
|
102
|
+ &eof ) )
|
162
|
103
|
return 0;
|
163
|
|
- twiddle();
|
164
|
|
- offset += block.len;
|
165
|
|
- request = NULL; /* Send request only once */
|
166
|
|
- } while ( ! block.eof );
|
|
104
|
+ reply = NULL;
|
|
105
|
+ } while ( ! eof );
|
|
106
|
+
|
|
107
|
+ /* ACK the final packet, as a courtesy to the server */
|
|
108
|
+ tftp_ack_nowait ( &state );
|
167
|
109
|
|
168
|
110
|
return 1;
|
169
|
111
|
}
|