|
@@ -4,17 +4,10 @@
|
4
|
4
|
#include "etherboot.h"
|
5
|
5
|
#include "tftpcore.h"
|
6
|
6
|
|
7
|
|
-/** @file
|
8
|
|
- *
|
9
|
|
- * TFTP core functions
|
10
|
|
- *
|
11
|
|
- * This file provides functions that are common to the TFTP (rfc1350),
|
12
|
|
- * TFTM (rfc2090) and MTFTP (PXE) protocols.
|
13
|
|
- *
|
14
|
|
- */
|
|
7
|
+/** @file */
|
15
|
8
|
|
16
|
9
|
/**
|
17
|
|
- * Wait for a TFTP packet
|
|
10
|
+ * await_reply() filter for TFTP packets
|
18
|
11
|
*
|
19
|
12
|
* @v ptr Pointer to a struct tftp_state
|
20
|
13
|
* @v tftp_state::server::sin_addr TFTP server IP address
|
|
@@ -30,7 +23,7 @@
|
30
|
23
|
* and is addressed either to our IP address or to our multicast
|
31
|
24
|
* listening address).
|
32
|
25
|
*
|
33
|
|
- * Invoke await_tftp() using code such as
|
|
26
|
+ * Use await_tftp() in code such as
|
34
|
27
|
*
|
35
|
28
|
* @code
|
36
|
29
|
*
|
|
@@ -40,9 +33,9 @@
|
40
|
33
|
*
|
41
|
34
|
* @endcode
|
42
|
35
|
*/
|
43
|
|
-int await_tftp ( int ival __unused, void *ptr, unsigned short ptype __unused,
|
44
|
|
- struct iphdr *ip, struct udphdr *udp,
|
45
|
|
- struct tcphdr *tcp __unused ) {
|
|
36
|
+static int await_tftp ( int ival __unused, void *ptr,
|
|
37
|
+ unsigned short ptype __unused, struct iphdr *ip,
|
|
38
|
+ struct udphdr *udp, struct tcphdr *tcp __unused ) {
|
46
|
39
|
struct tftp_state *state = ptr;
|
47
|
40
|
|
48
|
41
|
/* Must have valid UDP (and, therefore, also IP) headers */
|
|
@@ -76,6 +69,51 @@ int await_tftp ( int ival __unused, void *ptr, unsigned short ptype __unused,
|
76
|
69
|
return 1;
|
77
|
70
|
}
|
78
|
71
|
|
|
72
|
+/**
|
|
73
|
+ * Retrieve a TFTP packet
|
|
74
|
+ *
|
|
75
|
+ * @v state TFTP transfer state
|
|
76
|
+ * @v tftp_state::server::sin_addr TFTP server IP address
|
|
77
|
+ * @v tftp_state::client::sin_addr Client multicast IP address, or 0.0.0.0
|
|
78
|
+ * @v tftp_state::client::sin_port Client UDP port
|
|
79
|
+ * @v timeout Time to wait for a response
|
|
80
|
+ * @ret True Received a non-error response
|
|
81
|
+ * @ret False Received error response / no response
|
|
82
|
+ * @ret *reply The server's response, if any
|
|
83
|
+ * @err #PXENV_STATUS_TFTP_READ_TIMEOUT No response received in time
|
|
84
|
+ * @err other As set by tftp_set_errno()
|
|
85
|
+ *
|
|
86
|
+ * Retrieve the next packet sent by the TFTP server, if any is sent
|
|
87
|
+ * within the specified timeout period. The packet is returned via
|
|
88
|
+ * #reply. If no packet is received within the timeout period, a NULL
|
|
89
|
+ * value will be stored in #reply.
|
|
90
|
+ *
|
|
91
|
+ * If the response from the server is a TFTP ERROR packet, tftp_get()
|
|
92
|
+ * will return False and #errno will be set accordingly.
|
|
93
|
+ *
|
|
94
|
+ * You can differentiate between "received no response" and "received
|
|
95
|
+ * an error response" by checking #reply; if #reply is NULL then no
|
|
96
|
+ * response was received.
|
|
97
|
+ */
|
|
98
|
+int tftp_get ( struct tftp_state *state, long timeout,
|
|
99
|
+ union tftp_any **reply ) {
|
|
100
|
+
|
|
101
|
+ *reply = NULL;
|
|
102
|
+
|
|
103
|
+ if ( ! await_reply ( await_tftp, 0, state, timeout ) ) {
|
|
104
|
+ errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
|
|
105
|
+ return 0;
|
|
106
|
+ }
|
|
107
|
+
|
|
108
|
+ *reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
|
|
109
|
+ DBG ( "TFTPCORE: got reply (type %d)\n",
|
|
110
|
+ ntohs ( (*reply)->common.opcode ) );
|
|
111
|
+ if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
|
|
112
|
+ tftp_set_errno ( &(*reply)->error );
|
|
113
|
+ return 0;
|
|
114
|
+ }
|
|
115
|
+ return 1;
|
|
116
|
+}
|
79
|
117
|
|
80
|
118
|
/**
|
81
|
119
|
* Issue a TFTP open request (RRQ)
|
|
@@ -210,20 +248,19 @@ int tftp_open ( struct tftp_state *state, const char *filename,
|
210
|
248
|
return 0;
|
211
|
249
|
|
212
|
250
|
/* Wait for response */
|
213
|
|
- if ( await_reply ( await_tftp, 0, state, timeout ) ) {
|
214
|
|
- *reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
|
215
|
|
- state->server.sin_port =
|
216
|
|
- ntohs ( (*reply)->common.udp.src );
|
217
|
|
- DBG ( "TFTPCORE: got reply from %@:%d (type %d)\n",
|
|
251
|
+ if ( tftp_get ( state, timeout, reply ) ) {
|
|
252
|
+ /* We got a non-error response */
|
|
253
|
+ state->server.sin_port
|
|
254
|
+ = ntohs ( (*reply)->common.udp.src );
|
|
255
|
+ DBG ( "TFTP server is at %@:%d\n",
|
218
|
256
|
state->server.sin_addr.s_addr,
|
219
|
|
- state->server.sin_port,
|
220
|
|
- ntohs ( (*reply)->common.opcode ) );
|
221
|
|
- if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
|
222
|
|
- tftp_set_errno ( &(*reply)->error );
|
223
|
|
- return 0;
|
224
|
|
- }
|
|
257
|
+ state->server.sin_port );
|
225
|
258
|
return 1;
|
226
|
259
|
}
|
|
260
|
+ if ( reply ) {
|
|
261
|
+ /* We got an error response; abort */
|
|
262
|
+ return 0;
|
|
263
|
+ }
|
227
|
264
|
}
|
228
|
265
|
|
229
|
266
|
DBG ( "TFTPCORE: open request timed out\n" );
|
|
@@ -421,18 +458,15 @@ int tftp_ack ( struct tftp_state *state, union tftp_any **reply ) {
|
421
|
458
|
if ( ! tftp_ack_nowait ( state ) ) {
|
422
|
459
|
DBG ( "TFTP: could not send ACK: %m\n" );
|
423
|
460
|
return 0;
|
424
|
|
- }
|
425
|
|
- if ( await_reply ( await_tftp, 0, state, timeout ) ) {
|
426
|
|
- /* We received a reply */
|
427
|
|
- *reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
|
428
|
|
- DBG ( "TFTPCORE: got reply (type %d)\n",
|
429
|
|
- ntohs ( (*reply)->common.opcode ) );
|
430
|
|
- if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
|
431
|
|
- tftp_set_errno ( &(*reply)->error );
|
432
|
|
- return 0;
|
433
|
|
- }
|
|
461
|
+ }
|
|
462
|
+ if ( tftp_get ( state, timeout, reply ) ) {
|
|
463
|
+ /* We got a non-error response */
|
434
|
464
|
return 1;
|
435
|
465
|
}
|
|
466
|
+ if ( reply ) {
|
|
467
|
+ /* We got an error response */
|
|
468
|
+ return 0;
|
|
469
|
+ }
|
436
|
470
|
}
|
437
|
471
|
DBG ( "TFTP: timed out during read\n" );
|
438
|
472
|
errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
|
|
@@ -447,12 +481,15 @@ int tftp_ack ( struct tftp_state *state, union tftp_any **reply ) {
|
447
|
481
|
* @v tftp_state::server::sin_port TFTP server UDP port
|
448
|
482
|
* @v tftp_state::client::sin_port Client UDP port
|
449
|
483
|
* @v errcode TFTP error code
|
450
|
|
- * @v errmsg Descriptive error string
|
|
484
|
+ * @v errmsg Descriptive error string, or NULL
|
451
|
485
|
* @ret True Error packet was sent
|
452
|
486
|
* @ret False Error packet was not sent
|
453
|
487
|
*
|
454
|
488
|
* Send a TFTP ERROR packet back to the server to terminate the
|
455
|
489
|
* transfer.
|
|
490
|
+ *
|
|
491
|
+ * If #errmsg is NULL, the current error message string as returned by
|
|
492
|
+ * strerror(errno) will be used as the error text.
|
456
|
493
|
*/
|
457
|
494
|
int tftp_error ( struct tftp_state *state, int errcode, const char *errmsg ) {
|
458
|
495
|
struct tftp_error error;
|
|
@@ -460,7 +497,8 @@ int tftp_error ( struct tftp_state *state, int errcode, const char *errmsg ) {
|
460
|
497
|
DBG ( "TFTPCORE: aborting with error %d (%s)\n", errcode, errmsg );
|
461
|
498
|
error.opcode = htons ( TFTP_ERROR );
|
462
|
499
|
error.errcode = htons ( errcode );
|
463
|
|
- strncpy ( error.errmsg, errmsg, sizeof ( error.errmsg ) );
|
|
500
|
+ strncpy ( error.errmsg, errmsg ? errmsg : strerror ( errno ),
|
|
501
|
+ sizeof ( error.errmsg ) );
|
464
|
502
|
return udp_transmit ( state->server.sin_addr.s_addr,
|
465
|
503
|
state->client.sin_port, state->server.sin_port,
|
466
|
504
|
sizeof ( error ), &error );
|