Ver código fonte

Now have enough functions to implement a standard TFTP client in around 50

lines of code.
tags/v0.9.3
Michael Brown 19 anos atrás
pai
commit
0b048e9cfb
2 arquivos alterados com 132 adições e 11 exclusões
  1. 25
    0
      src/include/tftpcore.h
  2. 107
    11
      src/proto/tftpcore.c

+ 25
- 0
src/include/tftpcore.h Ver arquivo

@@ -0,0 +1,25 @@
1
+#ifndef TFTPCORE_H
2
+#define TFTPCORE_H
3
+
4
+#include "tftp.h"
5
+
6
+extern int await_tftp ( int ival, void *ptr, unsigned short ptype,
7
+			struct iphdr *ip, struct udphdr *udp,
8
+			struct tcphdr *tcp );
9
+
10
+extern int tftp_open ( struct tftp_state *state, const char *filename,
11
+		       union tftp_any **reply );
12
+
13
+extern int tftp_process_opts ( struct tftp_state *state,
14
+			       struct tftp_oack *oack );
15
+
16
+extern int tftp_ack_nowait ( struct tftp_state *state );
17
+
18
+extern int tftp_ack ( struct tftp_state *state, union tftp_any **reply );
19
+
20
+extern int tftp_error ( struct tftp_state *state, int errcode,
21
+			const char *errmsg );
22
+
23
+extern void tftp_set_errno ( struct tftp_error *error );
24
+
25
+#endif /* TFTPCORE_H */

+ 107
- 11
src/proto/tftpcore.c Ver arquivo

@@ -2,6 +2,7 @@
2 2
 #include "tcp.h" /* for struct tcphdr */
3 3
 #include "errno.h"
4 4
 #include "etherboot.h"
5
+#include "tftpcore.h"
5 6
 
6 7
 /** @file
7 8
  *
@@ -16,6 +17,9 @@
16 17
  * Wait for a TFTP packet
17 18
  *
18 19
  * @v ptr			Pointer to a struct tftp_state
20
+ * @v tftp_state::server::sin_addr TFTP server IP address
21
+ * @v tftp_state::client::sin_addr Client multicast IP address, or 0.0.0.0
22
+ * @v tftp_state::client::sin_port Client UDP port
19 23
  * @v ip			IP header
20 24
  * @v udp			UDP header
21 25
  * @ret True			This is our TFTP packet
@@ -74,16 +78,20 @@ int await_tftp ( int ival __unused, void *ptr, unsigned short ptype __unused,
74 78
  * @v filename				File name
75 79
  * @ret True				Received a non-error response
76 80
  * @ret False				Received error response / no response
81
+ * @ret tftp_state::server::sin_port	TFTP server UDP port
77 82
  * @ret tftp_state::client::sin_port	Client UDP port
78
- * @ret tftp_state::client::blksize	Always #TFTP_DEFAULT_BLKSIZE
79
- * @ret *tftp				The server's response, if any
83
+ * @ret tftp_state::blksize		Always #TFTP_DEFAULT_BLKSIZE
84
+ * @ret *reply				The server's response, if any
85
+ * @err #PXENV_STATUS_TFTP_OPEN_TIMEOUT	TFTP open timed out
86
+ * @err other				As returned by udp_transmit()
87
+ * @err other				As set by tftp_set_errno()
80 88
  *
81 89
  * Send a TFTP/TFTM/MTFTP RRQ (read request) to a TFTP server, and
82 90
  * return the server's reply (which may be an OACK, DATA or ERROR
83 91
  * packet).  The server's reply will not be acknowledged, or processed
84 92
  * in any way.
85 93
  *
86
- * If tftp_state::server::sin_port is 0, the standard tftp server port
94
+ * If tftp_state::server::sin_port is 0, the standard TFTP server port
87 95
  * (#TFTP_PORT) will be used.
88 96
  *
89 97
  * If tftp_state::client::sin_addr is not 0.0.0.0, it will be used as
@@ -121,6 +129,10 @@ int await_tftp ( int ival __unused, void *ptr, unsigned short ptype __unused,
121 129
  * be assumed until the OACK packet is processed (by a subsequent call
122 130
  * to tftp_process_opts()).
123 131
  *
132
+ * tftp_state::server::sin_port will be set to the UDP port from which
133
+ * the server's response originated.  This may or may not be the port
134
+ * to which the open request was sent.
135
+ *
124 136
  * The options "blksize", "tsize" and "multicast" will always be
125 137
  * appended to a TFTP open request.  Servers that do not understand
126 138
  * any of these options should simply ignore them.
@@ -129,9 +141,11 @@ int await_tftp ( int ival __unused, void *ptr, unsigned short ptype __unused,
129 141
  * the caller is responsible for calling join_group() and
130 142
  * leave_group() at appropriate times.
131 143
  *
144
+ * If the response from the server is a TFTP ERROR packet, tftp_open()
145
+ * will return False and #errno will be set accordingly.
132 146
  */
133 147
 int tftp_open ( struct tftp_state *state, const char *filename,
134
-		union tftp_any **tftp ) {
148
+		union tftp_any **reply ) {
135 149
 	static unsigned short lport = 2000; /* local port */
136 150
 	int fixed_lport;
137 151
 	struct tftp_rrq rrq;
@@ -164,7 +178,7 @@ int tftp_open ( struct tftp_state *state, const char *filename,
164 178
 	state->blksize = TFTP_DEFAULT_BLKSIZE;
165 179
 	
166 180
 	/* Nullify received packet pointer */
167
-	*tftp = NULL;
181
+	*reply = NULL;
168 182
 
169 183
 	/* Transmit RRQ until we get a response */
170 184
 	for ( retry = 0 ; retry < MAX_TFTP_RETRIES ; retry++ ) {
@@ -186,7 +200,13 @@ int tftp_open ( struct tftp_state *state, const char *filename,
186 200
 		
187 201
 		/* Wait for response */
188 202
 		if ( await_reply ( await_tftp, 0, state, timeout ) ) {
189
-			*tftp = ( union tftp_any * ) &nic.packet[ETH_HLEN];
203
+			*reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
204
+			state->server.sin_port =
205
+				ntohs ( (*reply)->common.udp.dest );
206
+			if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
207
+				tftp_set_errno ( &(*reply)->error );
208
+				return 0;
209
+			}
190 210
 			return 1;
191 211
 		}
192 212
 	}
@@ -321,13 +341,14 @@ int tftp_process_opts ( struct tftp_state *state, struct tftp_oack *oack ) {
321 341
  * @v tftp_state::block			Most recently received block number
322 342
  * @ret True				Acknowledgement packet was sent
323 343
  * @ret False				Acknowledgement packet was not sent
344
+ * @err other				As returned by udp_transmit()
324 345
  * 
325 346
  * Send a TFTP ACK packet for the most recently received block.
326 347
  *
327 348
  * This sends only a single ACK packet; it does not wait for the
328 349
  * server's response.
329 350
  */
330
-int tftp_ack ( struct tftp_state *state ) {
351
+int tftp_ack_nowait ( struct tftp_state *state ) {
331 352
 	struct tftp_ack ack;
332 353
 
333 354
 	ack.opcode = htons ( TFTP_ACK );
@@ -337,6 +358,59 @@ int tftp_ack ( struct tftp_state *state ) {
337 358
 			      sizeof ( ack ), &ack );
338 359
 }
339 360
 
361
+/**
362
+ * Acknowledge a TFTP packet and wait for a response
363
+ *
364
+ * @v state				TFTP transfer state
365
+ * @v tftp_state::server::sin_addr	TFTP server IP address
366
+ * @v tftp_state::server::sin_port	TFTP server UDP port
367
+ * @v tftp_state::client::sin_port	Client UDP port
368
+ * @v tftp_state::block			Most recently received block number
369
+ * @ret True				Received a non-error response
370
+ * @ret False				Received error response / no response
371
+ * @ret *reply				The server's response, if any
372
+ * @err #PXENV_STATUS_TFTP_READ_TIMEOUT	Timed out waiting for a response
373
+ * @err other				As returned by tftp_ack_nowait()
374
+ * @err other				As set by tftp_set_errno()
375
+ *
376
+ * Send a TFTP ACK packet for the most recently received data block,
377
+ * and keep transmitting this ACK until we get a response from the
378
+ * server (e.g. a new data block).
379
+ *
380
+ * If the response is a TFTP DATA packet, no processing is done.
381
+ * Specifically, the block number is not checked to ensure that this
382
+ * is indeed the next data block in the sequence, nor is
383
+ * tftp_state::block updated with the new block number.
384
+ *
385
+ * If the response from the server is a TFTP ERROR packet, tftp_open()
386
+ * will return False and #errno will be set accordingly.
387
+ */
388
+int tftp_ack ( struct tftp_state *state, union tftp_any **reply ) {
389
+	int retry;
390
+
391
+	*reply = NULL;
392
+	for ( retry = 0 ; retry < MAX_TFTP_RETRIES ; retry++ ) {
393
+		long timeout = rfc2131_sleep_interval ( TFTP_REXMT, retry );
394
+		/* ACK the last data block */
395
+		if ( ! tftp_ack_nowait ( state ) ) {
396
+			DBG ( "TFTP: could not send ACK: %m\n" );
397
+			return 0;
398
+		}	
399
+		if ( await_reply ( await_tftp, 0, &state, timeout ) ) {
400
+			/* We received a reply */
401
+			*reply = ( union tftp_any * ) &nic.packet[ETH_HLEN];
402
+			if ( ntohs ( (*reply)->common.opcode ) == TFTP_ERROR ){
403
+				tftp_set_errno ( &(*reply)->error );
404
+				return 0;
405
+			}
406
+			return 1;
407
+		}
408
+	}
409
+	DBG ( "TFTP: ACK retries exceeded\n" );
410
+	errno = PXENV_STATUS_TFTP_READ_TIMEOUT;
411
+	return 0;
412
+}
413
+
340 414
 /**
341 415
  * Send a TFTP error
342 416
  *
@@ -344,7 +418,7 @@ int tftp_ack ( struct tftp_state *state ) {
344 418
  * @v tftp_state::server::sin_addr	TFTP server IP address
345 419
  * @v tftp_state::server::sin_port	TFTP server UDP port
346 420
  * @v tftp_state::client::sin_port	Client UDP port
347
- * @v err				TFTP error code
421
+ * @v errcode				TFTP error code
348 422
  * @v errmsg				Descriptive error string
349 423
  * @ret True				Error packet was sent
350 424
  * @ret False				Error packet was not sent
@@ -352,14 +426,36 @@ int tftp_ack ( struct tftp_state *state ) {
352 426
  * Send a TFTP ERROR packet back to the server to terminate the
353 427
  * transfer.
354 428
  */
355
-int tftp_error ( struct tftp_state *state, int err, const char *errmsg ) {
429
+int tftp_error ( struct tftp_state *state, int errcode, const char *errmsg ) {
356 430
 	struct tftp_error error;
357 431
 
358
-	DBG ( "TFTPCORE: aborting with error %d (%s)\n", err, errmsg );
432
+	DBG ( "TFTPCORE: aborting with error %d (%s)\n", errcode, errmsg );
359 433
 	error.opcode = htons ( TFTP_ERROR );
360
-	error.errcode = htons ( err );
434
+	error.errcode = htons ( errcode );
361 435
 	strncpy ( error.errmsg, errmsg, sizeof ( error.errmsg ) );
362 436
 	return udp_transmit ( state->server.sin_addr.s_addr,
363 437
 			      state->client.sin_port, state->server.sin_port,
364 438
 			      sizeof ( error ), &error );
365 439
 }
440
+
441
+/**
442
+ * Interpret a TFTP error
443
+ *
444
+ * @v error				Pointer to a struct tftp_error
445
+ *
446
+ * Sets #errno based on the error code in a TFTP ERROR packet.
447
+ */
448
+void tftp_set_errno ( struct tftp_error *error ) {
449
+	static int errmap[] = {
450
+		[TFTP_ERR_FILE_NOT_FOUND] = PXENV_STATUS_TFTP_FILE_NOT_FOUND,
451
+		[TFTP_ERR_ACCESS_DENIED] = PXENV_STATUS_TFTP_ACCESS_VIOLATION,
452
+		[TFTP_ERR_ILLEGAL_OP] = PXENV_STATUS_TFTP_UNKNOWN_OPCODE,
453
+	};
454
+	unsigned int errcode = ntohs ( error->errcode );
455
+	
456
+	errno = 0;
457
+	if ( errcode < ( sizeof(errmap) / sizeof(errmap[0]) ) )
458
+		errno = errmap[errcode];
459
+	if ( ! errno )
460
+		errno = PXENV_STATUS_TFTP_ERROR_OPCODE;
461
+}

Carregando…
Cancelar
Salvar