XINU
tftp.c
Go to the documentation of this file.
1 /* tftp.c - trivial file transfer protocol */
2 
3 #include <xinu.h>
4 #include <stdlib.h>
5 #include <string.h>
6 
7 /*------------------------------------------------------------------------
8  *
9  * tftp_send1 - Internal function to send one outgoing request (RRQ or
10  * ACK) message during a read sequence and get a matching
11  * response (ignoring duplicates and nonsense packets)
12  *
13  *------------------------------------------------------------------------
14  */
15 
17  int32 sock, /* UDP socket to use */
18  uint32 remip, /* Remote IP address */
19  uint16 *remport, /* Remote port to use/set */
20  struct tftp_msg *msg, /* Pointer to outgoing message */
21  int32 mlen, /* Size of ougoing message */
22  struct tftp_msg *inmsg, /* Pointer to buffer for an */
23  /* incoming message */
24  uint16 expected /* Block number expected next */
25  )
26 {
27  int32 ret; /* Return value for udp_send */
28  int32 n; /* Number of bytes in response */
29  uint32 tmp; /* Holds IP address on receive */
30 
31  /* TFTP RRQ/WRQ Packet */
32  /* 2 bytes string 1 byte string 1 byte */
33  /* ------------------------------------------------ */
34  /* | Opcode | Filename | 0 | Mode | 0 | */
35  /* ------------------------------------------------ */
36 
37  /* TFTP ACK Packet */
38  /* 2 bytes 2 bytes */
39  /* --------------------- */
40  /* | Opcode | Block # | */
41  /* --------------------- */
42 
43  /* Send the outgoing message */
44 
45  ret = udp_sendto(sock, remip, *remport, (char *) msg, mlen);
46 
47  if (ret == SYSERR) {
48  return SYSERR;
49  }
50 
51  /* Repeatedly read incoming messages, discarding packets that */
52  /* are not valid or do not match the expected block number */
53  /* (i.e., duplicates of previous packets) */
54 
55  while(1) {
56 
57  /* Read next incoming message */
58 
59  n = udp_recvaddr(sock, &tmp, remport, (char *)inmsg,
60  sizeof(struct tftp_msg), TFTP_WAIT);
61  if (n == SYSERR) {
62  return SYSERR;
63  } else if (n == TIMEOUT) {
64  kprintf("\n[tftp_send1] UDP Receive Timeout\n");
65  return TIMEOUT;
66  }
67 
68  if (n < 4) { /* too small to be a valid packet */
69  continue;
70  }
71 
72  /* If Error came back, give up */
73 
74  if (ntohs(inmsg->tf_opcode) == TFTP_ERROR) {
75  kprintf("\n[tftp_send1] TFTP Error %d, %s\n",
76  ntohs(inmsg->tf_ercode),
77  inmsg->tf_ermsg );
78  return SYSERR;
79  }
80 
81  /* If data packet matches expected block, return */
82 
83  if ( (ntohs(inmsg->tf_opcode) == TFTP_DATA) &&
84  (ntohs(inmsg->tf_dblk) == expected)) {
85  return n;
86  }
87 
88  /* As this point, incoming message is either invalid or */
89  /* a duplicate, so ignore and try again */
90  }
91 }
92 
93 
94 /*------------------------------------------------------------------------
95  *
96  * tftpget - Use TFTP to download a specified file from a server
97  *
98  *------------------------------------------------------------------------
99  */
101  uint32 serverip, /* IP address of server */
102  const char* filename, /* Name of the file to download */
103  char* rcv_buf, /* Buffer to hold the file */
104  uint32 rcv_buf_size, /* Size of the buffer */
105  byte verbose /* Verbosity level */
106  )
107 {
108  return tftpget_mb(serverip, filename, &rcv_buf, &rcv_buf_size, 1,
109  verbose);
110 }
111 
112 /*------------------------------------------------------------------------
113  *
114  * tftpget_mb - multibuffer version of TFPT
115  *
116  *------------------------------------------------------------------------
117  */
119  uint32 serverip, /* IP address of server */
120  const char* filename, /* Name of the file to download */
121  char** rcv_bufs, /* Buffer to hold the file */
122  uint32* rcv_buf_sizes, /* Size of each buffer */
123  uint32 rcv_buf_count, /* Number of buffers */
124  byte verbose /* Verbosity level */
125  )
126 {
127  int32 nlen; /* Length of file name */
128  uint16 localport; /* Local UDP port to use */
129  uint16 remport=TFTP_PORT; /* Remote UDP port to use */
130  int32 sock; /* Socket descriptor to use */
131  uint16 expected = 1; /* Next data block we expect */
132  int32 i; /* Loop index */
133  int32 n; /* Num,h. octets in incoming msg*/
134  int32 ret; /* Return value */
135  int32 filesiz; /* Total size of downloaded file*/
136  struct tftp_msg outmsg; /* Outgoing message */
137  int32 mlen; /* Length of outgoing mesage */
138  struct tftp_msg inmsg; /* Buffer for response message */
139  int32 dlen; /* Size of data in a response */
140  char* curr_buf; /* Current buffer being used */
141  uint32 curr_buf_ind; /* Index of current buffer */
142  uint32 curr_used; /* Amount used in buffer */
143 
144  /* Check args */
145 
146  if(filename == NULL || serverip == 0 || rcv_bufs == NULL ||
147  rcv_buf_sizes == NULL || rcv_buf_count == 0) {
148  kprintf("[TFTP GET] ERROR: Invalid argument\n");
149  return SYSERR;
150  }
151  for(i = 0; i < rcv_buf_count; i++) {
152  if(rcv_bufs[i] == NULL || rcv_buf_sizes[i] == 0) {
153  kprintf("[TFTP GET] ERROR: Invalid argument\n");
154  return SYSERR;
155  }
156  }
157 
158  nlen = strnlen(filename, TFTP_MAXNAM+1);
159  if ( (nlen <= 0) || (nlen > TFTP_MAXNAM) ) {
160  return SYSERR;
161  }
162 
163  if(verbose & TFTP_VERBOSE) {
164  kprintf("[TFTP Get] Server: %08X File: %s\n",
165  serverip, filename);
166  }
167 
168  /* Generate a local port */
169 
170  localport = getport();
171 
172  if (verbose & TFTP_VERBOSE) {
173  kprintf("[TFTP Get] Using local port %u\n",
174  0xffff & localport);
175  }
176 
177  /* Register a UDP socket */
178 
179  sock = udp_register(serverip, 0, localport);
180  if (sock == SYSERR) {
181  kprintf("[TFTP Get] ERROR: udp_register failed\n");
182  return SYSERR;
183  }
184 
185  /* Clear the message buffer */
186 
187  memset((char*)&outmsg, NULLCH, sizeof(outmsg));
188 
189  /* Initialize the total file size to zero */
190 
191  filesiz = 0;
192  curr_buf_ind = 0;
193  curr_buf = (char*)rcv_bufs[curr_buf_ind];
194  curr_used = 0;
195 
196  /* Form the first message and compute length (a Read Request) */
197 
198  outmsg.tf_opcode = htons(TFTP_RRQ);
199  strncpy(outmsg.tf_filemode, filename, nlen+1);
200  /* Set mode to 'octet' */
201  strncpy(outmsg.tf_filemode+nlen+1, "octet", sizeof("octet")+1);
202 
203  /* Length is 2 opcode octets, name length, "octet" length, */
204  /* and two NULLs */
205 
206  mlen = nlen + strnlen("octet", 6) + 4;
207 
208  /* Repeatedly send the next request and get a response, */
209  /* retransmitting a request up to TFTP_MAXRETRIES times */
210 
211  while(1) {
212  for (i=0; i < TFTP_MAXRETRIES; i++) {
213  n = tftp_send1(sock, serverip, &remport, &outmsg, mlen,
214  &inmsg, expected);
215  if (n > 0) {
216  break;
217  } else if (n == SYSERR) {
218  kprintf("\n[TFTP Get] ERROR: TFTP Send fails\n");
219  udp_release(sock);
220  return SYSERR;
221  } else if (n == TIMEOUT) {
222  continue;
223 
224  }
225  }
226  if (i >= TFTP_MAXRETRIES) {
227  kprintf("\n[TFTP Get] ERROR: Max retries %d exceeded\n",
228  TFTP_MAXRETRIES);
229  udp_release(sock);
230  return SYSERR;
231  }
232 
233  if(verbose & TFTP_VERBOSE) {
234  kprintf(".");
235  }
236 
237  /* Compute size of data in the message */
238 
239  dlen = n - sizeof(inmsg.tf_opcode) - sizeof(inmsg.tf_dblk);
240 
241  /* Move the contents of this block into the file buffer */
242 
243  for (i=0; i<dlen; i++) {
244  if (curr_used >= rcv_buf_sizes[curr_buf_ind]) {
245  curr_buf_ind++;
246  if(curr_buf_ind >= rcv_buf_count) {
247  udp_release(sock);
248  if(verbose & TFTP_VERBOSE) {
249  kprintf("\n");
250  }
251  return filesiz;
252  }
253  curr_buf = (char*)rcv_bufs[curr_buf_ind];
254  curr_used = 0;
255  }
256  *curr_buf++ = inmsg.tf_data[i];
257  curr_used++;
258  filesiz++;
259  }
260 
261  /* Form an ACK */
262 
263  outmsg.tf_opcode = htons(TFTP_ACK);
264  outmsg.tf_ablk = htons(expected);
265  mlen = sizeof(outmsg.tf_opcode) + sizeof(outmsg.tf_ablk);
266 
267  /* If this was the last packet, send final ACK */
268 
269  if (dlen < 512) {
270  ret = udp_sendto(sock, serverip, remport,
271  (char *) &outmsg, mlen);
272  udp_release(sock);
273 
274  if(verbose & TFTP_VERBOSE) {
275  kprintf("\n");
276  }
277 
278  if (ret == SYSERR) {
279  kprintf("\n[TFTP GET] Error on final ack\n");
280  return SYSERR;
281  }
282  return filesiz;
283  }
284 
285  /* Move to next block and continue */
286 
287  expected++;
288  }
289 }
syscall kprintf(char *fmt,...)
ポーリングI/Oを使用して、フォーマットされた文字列をコンソールに出力する。
Definition: kprintf.c:98
#define TFTP_MAXNAM
Definition: tftp.h:21
#define NULL
連結リスト用のNULLポインタ
Definition: kernel.h:68
unsigned char byte
符号なし8ビット値(unsigned char)
Definition: kernel.h:7
char * strncpy(char *, const char *, int32)
文字列s1に文字列s2をN文字(Byte)分コピーする。
Definition: strncpy.c:15
status tftpget_mb(uint32 serverip, const char *filename, char **rcv_bufs, uint32 *rcv_buf_sizes, uint32 rcv_buf_count, byte verbose)
Definition: tftp.c:118
全てのシステムヘッダファイルをインクルードする。
#define SYSERR
処理が失敗した場合
Definition: kernel.h:79
int32 udp_recvaddr(uid32, uint32 *, uint16 *, char *, int32, uint32)
Definition: udp.c:227
status tftp_send1(int32 sock, uint32 remip, uint16 *remport, struct tftp_msg *msg, int32 mlen, struct tftp_msg *inmsg, uint16 expected)
Definition: tftp.c:16
#define TFTP_ACK
Definition: tftp.h:7
#define TIMEOUT
システムコールがタイムアウトした場合
Definition: kernel.h:83
int32 status
ステータスを意味する返り値の型(OK/SYSERR)
Definition: kernel.h:57
status tftpget(uint32 serverip, const char *filename, char *rcv_buf, uint32 rcv_buf_size, byte verbose)
Definition: tftp.c:100
char tf_data[TFTP_MAXDATA]
Definition: tftp.h:47
Definition: tftp.h:33
#define TFTP_WAIT
Definition: tftp.h:24
uint16 tf_opcode
Definition: tftp.h:34
int int32
符号あり32ビット整数(int)
Definition: kernel.h:11
#define TFTP_PORT
Definition: tftp.h:20
#define TFTP_ERROR
Definition: tftp.h:8
uid32 udp_register(uint32, uint16, uint16)
Definition: udp.c:85
unsigned short uint16
符号なし16ビット整数(unsigned short)
Definition: kernel.h:17
void * memset(void *, const int, int32)
指定のByteブロックに対して、同じ値をNバイト分書き込む。
Definition: memset.c:13
#define ntohs(x)
Definition: prototypes.h:622
status udp_release(uid32)
Definition: udp.c:502
uint16 getport(void)
Definition: net.c:150
status udp_sendto(uid32, uint32, uint16, char *, int32)
Definition: udp.c:417
uint16 tf_dblk
Definition: tftp.h:46
#define TFTP_DATA
Definition: tftp.h:6
unsigned int uint32
符号なし32ビット整数(unsigned int)
Definition: kernel.h:15
#define htons(x)
Definition: prototypes.h:619
int32 strnlen(const char *, uint32)
NULL終端された文字列の長さを返す。NULL終端は長さに含まない。
Definition: strnlen.c:14
#define NULLCH
NULL文字(NULL終端)
Definition: kernel.h:70
#define TFTP_MAXRETRIES
Definition: tftp.h:23
#define TFTP_RRQ
Definition: tftp.h:4
#define TFTP_VERBOSE
Definition: tftp.h:28