Browse Source

Don't choke on duplicate OACK packets.

Make await_tftp() static and create tftp_get() for fetching the next TFTP
packet instead.
tags/v0.9.3
Michael Brown 19 years ago
parent
commit
f2198e8a65
3 changed files with 116 additions and 63 deletions
  1. 11
    3
      src/include/tftpcore.h
  2. 31
    24
      src/proto/tftp.c
  3. 74
    36
      src/proto/tftpcore.c

+ 11
- 3
src/include/tftpcore.h View File

1
 #ifndef TFTPCORE_H
1
 #ifndef TFTPCORE_H
2
 #define TFTPCORE_H
2
 #define TFTPCORE_H
3
 
3
 
4
+/** @file
5
+ *
6
+ * TFTP core functions
7
+ *
8
+ * This file provides functions that are common to the TFTP (rfc1350),
9
+ * TFTM (rfc2090) and MTFTP (PXE) protocols.
10
+ *
11
+ */
12
+
4
 #include "tftp.h"
13
 #include "tftp.h"
5
 
14
 
6
-extern int await_tftp ( int ival, void *ptr, unsigned short ptype,
7
-			struct iphdr *ip, struct udphdr *udp,
8
-			struct tcphdr *tcp );
15
+extern int tftp_get ( struct tftp_state *state, long timeout,
16
+		      union tftp_any **reply );
9
 
17
 
10
 extern int tftp_open ( struct tftp_state *state, const char *filename,
18
 extern int tftp_open ( struct tftp_state *state, const char *filename,
11
 		       union tftp_any **reply );
19
 		       union tftp_any **reply );

+ 31
- 24
src/proto/tftp.c View File

103
 		return 0;
103
 		return 0;
104
 	}
104
 	}
105
 	
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 */
106
 	/* Fetch file, a block at a time */
116
 	do {
107
 	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" );
108
+		twiddle();
109
+		switch ( ntohs ( reply->common.opcode ) ) {
110
+		case TFTP_DATA:
111
+			if ( ! process_tftp_data ( &state, &reply->data,
112
+						   buffer, &eof ) ) {
113
+				tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
114
+					     NULL );
124
 				return 0;
115
 				return 0;
125
 			}
116
 			}
126
-		}
127
-		twiddle();
128
-		/* Check it's a DATA block */
129
-		if ( ntohs ( reply->common.opcode ) != TFTP_DATA ) {
117
+			break;
118
+		case TFTP_OACK:
119
+			if ( state.block ) {
120
+				/* OACK must be first block, if present */
121
+				DBG ( "TFTP: OACK after block %d\n",
122
+				      state.block );
123
+				errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
124
+				tftp_error ( &state, TFTP_ERR_ILLEGAL_OP,
125
+					     NULL ); 
126
+				return 0;
127
+			}
128
+			if ( ! tftp_process_opts ( &state, &reply->oack ) ) {
129
+				DBG ( "TFTP: option processing failed: %m\n" );
130
+				tftp_error ( &state, TFTP_ERR_BAD_OPTS, NULL );
131
+				return 0;
132
+			}
133
+			break;
134
+		default:
130
 			DBG ( "TFTP: unexpected opcode %d\n",
135
 			DBG ( "TFTP: unexpected opcode %d\n",
131
 			      ntohs ( reply->common.opcode ) );
136
 			      ntohs ( reply->common.opcode ) );
132
 			errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
137
 			errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
138
+			tftp_error ( &state, TFTP_ERR_ILLEGAL_OP, NULL );
133
 			return 0;
139
 			return 0;
134
 		}
140
 		}
135
-		/* Process the DATA block */
136
-		if ( ! process_tftp_data ( &state, &reply->data, buffer,
137
-					   &eof ) )
141
+		/* Fetch the next data block */
142
+		if ( ! tftp_ack ( &state, &reply ) ) {
143
+			DBG ( "TFTP: could not get next block: %m\n" );
144
+			tftp_error ( &state, TFTP_ERR_ILLEGAL_OP, NULL );
138
 			return 0;
145
 			return 0;
139
-		reply = NULL;
146
+		}
140
 	} while ( ! eof );
147
 	} while ( ! eof );
141
 
148
 
142
 	/* ACK the final packet, as a courtesy to the server */
149
 	/* ACK the final packet, as a courtesy to the server */

+ 74
- 36
src/proto/tftpcore.c View File

4
 #include "etherboot.h"
4
 #include "etherboot.h"
5
 #include "tftpcore.h"
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
  * @v ptr			Pointer to a struct tftp_state
12
  * @v ptr			Pointer to a struct tftp_state
20
  * @v tftp_state::server::sin_addr TFTP server IP address
13
  * @v tftp_state::server::sin_addr TFTP server IP address
30
  * and is addressed either to our IP address or to our multicast
23
  * and is addressed either to our IP address or to our multicast
31
  * listening address).
24
  * listening address).
32
  *
25
  *
33
- * Invoke await_tftp() using code such as
26
+ * Use await_tftp() in code such as
34
  *
27
  *
35
  * @code
28
  * @code
36
  *
29
  *
40
  *
33
  *
41
  * @endcode
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
 	struct tftp_state *state = ptr;
39
 	struct tftp_state *state = ptr;
47
 
40
 
48
 	/* Must have valid UDP (and, therefore, also IP) headers */
41
 	/* Must have valid UDP (and, therefore, also IP) headers */
76
 	return 1;
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
  * Issue a TFTP open request (RRQ)
119
  * Issue a TFTP open request (RRQ)
210
 			return 0;
248
 			return 0;
211
 		
249
 		
212
 		/* Wait for response */
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
 			      state->server.sin_addr.s_addr,
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
 			return 1;
258
 			return 1;
226
 		}
259
 		}
260
+		if ( reply ) {
261
+			/* We got an error response; abort */
262
+			return 0;
263
+		}
227
 	}
264
 	}
228
 
265
 
229
 	DBG ( "TFTPCORE: open request timed out\n" );
266
 	DBG ( "TFTPCORE: open request timed out\n" );
421
 		if ( ! tftp_ack_nowait ( state ) ) {
458
 		if ( ! tftp_ack_nowait ( state ) ) {
422
 			DBG ( "TFTP: could not send ACK: %m\n" );
459
 			DBG ( "TFTP: could not send ACK: %m\n" );
423
 			return 0;
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
 			return 1;
464
 			return 1;
435
 		}
465
 		}
466
+		if ( reply ) {
467
+			/* We got an error response */
468
+			return 0;
469
+		}
436
 	}
470
 	}
437
 	DBG ( "TFTP: timed out during read\n" );
471
 	DBG ( "TFTP: timed out during read\n" );
438
 	errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
472
 	errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
447
  * @v tftp_state::server::sin_port	TFTP server UDP port
481
  * @v tftp_state::server::sin_port	TFTP server UDP port
448
  * @v tftp_state::client::sin_port	Client UDP port
482
  * @v tftp_state::client::sin_port	Client UDP port
449
  * @v errcode				TFTP error code
483
  * @v errcode				TFTP error code
450
- * @v errmsg				Descriptive error string
484
+ * @v errmsg				Descriptive error string, or NULL
451
  * @ret True				Error packet was sent
485
  * @ret True				Error packet was sent
452
  * @ret False				Error packet was not sent
486
  * @ret False				Error packet was not sent
453
  *
487
  *
454
  * Send a TFTP ERROR packet back to the server to terminate the
488
  * Send a TFTP ERROR packet back to the server to terminate the
455
  * transfer.
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
 int tftp_error ( struct tftp_state *state, int errcode, const char *errmsg ) {
494
 int tftp_error ( struct tftp_state *state, int errcode, const char *errmsg ) {
458
 	struct tftp_error error;
495
 	struct tftp_error error;
460
 	DBG ( "TFTPCORE: aborting with error %d (%s)\n", errcode, errmsg );
497
 	DBG ( "TFTPCORE: aborting with error %d (%s)\n", errcode, errmsg );
461
 	error.opcode = htons ( TFTP_ERROR );
498
 	error.opcode = htons ( TFTP_ERROR );
462
 	error.errcode = htons ( errcode );
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
 	return udp_transmit ( state->server.sin_addr.s_addr,
502
 	return udp_transmit ( state->server.sin_addr.s_addr,
465
 			      state->client.sin_port, state->server.sin_port,
503
 			      state->client.sin_port, state->server.sin_port,
466
 			      sizeof ( error ), &error );
504
 			      sizeof ( error ), &error );

Loading…
Cancel
Save