Просмотр исходного кода

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 лет назад
Родитель
Сommit
f2198e8a65
3 измененных файлов: 116 добавлений и 63 удалений
  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 Просмотреть файл

@@ -1,11 +1,19 @@
1 1
 #ifndef TFTPCORE_H
2 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 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 18
 extern int tftp_open ( struct tftp_state *state, const char *filename,
11 19
 		       union tftp_any **reply );

+ 31
- 24
src/proto/tftp.c Просмотреть файл

@@ -103,40 +103,47 @@ static int tftp ( char *url __unused, struct sockaddr_in *server, char *file,
103 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 106
 	/* Fetch file, a block at a time */
116 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 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 135
 			DBG ( "TFTP: unexpected opcode %d\n",
131 136
 			      ntohs ( reply->common.opcode ) );
132 137
 			errno = PXENV_STATUS_TFTP_UNKNOWN_OPCODE;
138
+			tftp_error ( &state, TFTP_ERR_ILLEGAL_OP, NULL );
133 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 145
 			return 0;
139
-		reply = NULL;
146
+		}
140 147
 	} while ( ! eof );
141 148
 
142 149
 	/* ACK the final packet, as a courtesy to the server */

+ 74
- 36
src/proto/tftpcore.c Просмотреть файл

@@ -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 );

Загрузка…
Отмена
Сохранить