source: azure_iot_hub_f767zi/trunk/asp_baseplatform/lwip/lwip-2.1.2/src/apps/tftp/tftp_server.c@ 457

Last change on this file since 457 was 457, checked in by coas-nagasima, 4 years ago

ファイルを追加

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 11.4 KB
Line 
1/**
2 *
3 * @file tftp_server.c
4 *
5 * @author Logan Gunthorpe <logang@deltatee.com>
6 * Dirk Ziegelmeier <dziegel@gmx.de>
7 *
8 * @brief Trivial File Transfer Protocol (RFC 1350)
9 *
10 * Copyright (c) Deltatee Enterprises Ltd. 2013
11 * All rights reserved.
12 *
13 */
14
15/*
16 * Redistribution and use in source and binary forms, with or without
17 * modification,are permitted provided that the following conditions are met:
18 *
19 * 1. Redistributions of source code must retain the above copyright notice,
20 * this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright notice,
22 * this list of conditions and the following disclaimer in the documentation
23 * and/or other materials provided with the distribution.
24 * 3. The name of the author may not be used to endorse or promote products
25 * derived from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
28 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
29 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
30 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
32 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 *
38 * Author: Logan Gunthorpe <logang@deltatee.com>
39 * Dirk Ziegelmeier <dziegel@gmx.de>
40 *
41 */
42
43/**
44 * @defgroup tftp TFTP server
45 * @ingroup apps
46 *
47 * This is simple TFTP server for the lwIP raw API.
48 */
49
50#include "lwip/apps/tftp_server.h"
51
52#if LWIP_UDP
53
54#include "lwip/udp.h"
55#include "lwip/timeouts.h"
56#include "lwip/debug.h"
57
58#define TFTP_MAX_PAYLOAD_SIZE 512
59#define TFTP_HEADER_LENGTH 4
60
61#define TFTP_RRQ 1
62#define TFTP_WRQ 2
63#define TFTP_DATA 3
64#define TFTP_ACK 4
65#define TFTP_ERROR 5
66
67enum tftp_error {
68 TFTP_ERROR_FILE_NOT_FOUND = 1,
69 TFTP_ERROR_ACCESS_VIOLATION = 2,
70 TFTP_ERROR_DISK_FULL = 3,
71 TFTP_ERROR_ILLEGAL_OPERATION = 4,
72 TFTP_ERROR_UNKNOWN_TRFR_ID = 5,
73 TFTP_ERROR_FILE_EXISTS = 6,
74 TFTP_ERROR_NO_SUCH_USER = 7
75};
76
77#include <string.h>
78
79struct tftp_state {
80 const struct tftp_context *ctx;
81 void *handle;
82 struct pbuf *last_data;
83 struct udp_pcb *upcb;
84 ip_addr_t addr;
85 u16_t port;
86 int timer;
87 int last_pkt;
88 u16_t blknum;
89 u8_t retries;
90 u8_t mode_write;
91};
92
93static struct tftp_state tftp_state;
94
95static void tftp_tmr(void *arg);
96
97static void
98close_handle(void)
99{
100 tftp_state.port = 0;
101 ip_addr_set_any(0, &tftp_state.addr);
102
103 if (tftp_state.last_data != NULL) {
104 pbuf_free(tftp_state.last_data);
105 tftp_state.last_data = NULL;
106 }
107
108 sys_untimeout(tftp_tmr, NULL);
109
110 if (tftp_state.handle) {
111 tftp_state.ctx->close(tftp_state.handle);
112 tftp_state.handle = NULL;
113 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: closing\n"));
114 }
115}
116
117static void
118send_error(const ip_addr_t *addr, u16_t port, enum tftp_error code, const char *str)
119{
120 int str_length = strlen(str);
121 struct pbuf *p;
122 u16_t *payload;
123
124 p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(TFTP_HEADER_LENGTH + str_length + 1), PBUF_RAM);
125 if (p == NULL) {
126 return;
127 }
128
129 payload = (u16_t *) p->payload;
130 payload[0] = PP_HTONS(TFTP_ERROR);
131 payload[1] = lwip_htons(code);
132 MEMCPY(&payload[2], str, str_length + 1);
133
134 udp_sendto(tftp_state.upcb, p, addr, port);
135 pbuf_free(p);
136}
137
138static void
139send_ack(u16_t blknum)
140{
141 struct pbuf *p;
142 u16_t *payload;
143
144 p = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH, PBUF_RAM);
145 if (p == NULL) {
146 return;
147 }
148 payload = (u16_t *) p->payload;
149
150 payload[0] = PP_HTONS(TFTP_ACK);
151 payload[1] = lwip_htons(blknum);
152 udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
153 pbuf_free(p);
154}
155
156static void
157resend_data(void)
158{
159 struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM);
160 if (p == NULL) {
161 return;
162 }
163
164 if (pbuf_copy(p, tftp_state.last_data) != ERR_OK) {
165 pbuf_free(p);
166 return;
167 }
168
169 udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
170 pbuf_free(p);
171}
172
173static void
174send_data(void)
175{
176 u16_t *payload;
177 int ret;
178
179 if (tftp_state.last_data != NULL) {
180 pbuf_free(tftp_state.last_data);
181 }
182
183 tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM);
184 if (tftp_state.last_data == NULL) {
185 return;
186 }
187
188 payload = (u16_t *) tftp_state.last_data->payload;
189 payload[0] = PP_HTONS(TFTP_DATA);
190 payload[1] = lwip_htons(tftp_state.blknum);
191
192 ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
193 if (ret < 0) {
194 send_error(&tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file.");
195 close_handle();
196 return;
197 }
198
199 pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret));
200 resend_data();
201}
202
203static void
204recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
205{
206 u16_t *sbuf = (u16_t *) p->payload;
207 int opcode;
208
209 LWIP_UNUSED_ARG(arg);
210 LWIP_UNUSED_ARG(upcb);
211
212 if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
213 (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) {
214 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
215 pbuf_free(p);
216 return;
217 }
218
219 opcode = sbuf[0];
220
221 tftp_state.last_pkt = tftp_state.timer;
222 tftp_state.retries = 0;
223
224 switch (opcode) {
225 case PP_HTONS(TFTP_RRQ): /* fall through */
226 case PP_HTONS(TFTP_WRQ): {
227 const char tftp_null = 0;
228 char filename[TFTP_MAX_FILENAME_LEN + 1];
229 char mode[TFTP_MAX_MODE_LEN + 1];
230 u16_t filename_end_offset;
231 u16_t mode_end_offset;
232
233 if (tftp_state.handle != NULL) {
234 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
235 break;
236 }
237
238 sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
239
240 /* find \0 in pbuf -> end of filename string */
241 filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
242 if ((u16_t)(filename_end_offset - 1) > sizeof(filename)) {
243 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
244 break;
245 }
246 pbuf_copy_partial(p, filename, filename_end_offset - 1, 2);
247
248 /* find \0 in pbuf -> end of mode string */
249 mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset + 1);
250 if ((u16_t)(mode_end_offset - filename_end_offset) > sizeof(mode)) {
251 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
252 break;
253 }
254 pbuf_copy_partial(p, mode, mode_end_offset - filename_end_offset, filename_end_offset + 1);
255
256 tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
257 tftp_state.blknum = 1;
258
259 if (!tftp_state.handle) {
260 send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
261 break;
262 }
263
264 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
265 ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
266 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
267
268 ip_addr_copy(tftp_state.addr, *addr);
269 tftp_state.port = port;
270
271 if (opcode == PP_HTONS(TFTP_WRQ)) {
272 tftp_state.mode_write = 1;
273 send_ack(0);
274 } else {
275 tftp_state.mode_write = 0;
276 send_data();
277 }
278
279 break;
280 }
281
282 case PP_HTONS(TFTP_DATA): {
283 int ret;
284 u16_t blknum;
285
286 if (tftp_state.handle == NULL) {
287 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
288 break;
289 }
290
291 if (tftp_state.mode_write != 1) {
292 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
293 break;
294 }
295
296 blknum = lwip_ntohs(sbuf[1]);
297 if (blknum == tftp_state.blknum) {
298 pbuf_remove_header(p, TFTP_HEADER_LENGTH);
299
300 ret = tftp_state.ctx->write(tftp_state.handle, p);
301 if (ret < 0) {
302 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
303 close_handle();
304 } else {
305 send_ack(blknum);
306 }
307
308 if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
309 close_handle();
310 } else {
311 tftp_state.blknum++;
312 }
313 } else if ((u16_t)(blknum + 1) == tftp_state.blknum) {
314 /* retransmit of previous block, ack again (casting to u16_t to care for overflow) */
315 send_ack(blknum);
316 } else {
317 send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
318 }
319 break;
320 }
321
322 case PP_HTONS(TFTP_ACK): {
323 u16_t blknum;
324 int lastpkt;
325
326 if (tftp_state.handle == NULL) {
327 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
328 break;
329 }
330
331 if (tftp_state.mode_write != 0) {
332 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
333 break;
334 }
335
336 blknum = lwip_ntohs(sbuf[1]);
337 if (blknum != tftp_state.blknum) {
338 send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
339 break;
340 }
341
342 lastpkt = 0;
343
344 if (tftp_state.last_data != NULL) {
345 lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
346 }
347
348 if (!lastpkt) {
349 tftp_state.blknum++;
350 send_data();
351 } else {
352 close_handle();
353 }
354
355 break;
356 }
357
358 default:
359 send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
360 break;
361 }
362
363 pbuf_free(p);
364}
365
366static void
367tftp_tmr(void *arg)
368{
369 LWIP_UNUSED_ARG(arg);
370
371 tftp_state.timer++;
372
373 if (tftp_state.handle == NULL) {
374 return;
375 }
376
377 sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
378
379 if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
380 if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
381 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
382 resend_data();
383 tftp_state.retries++;
384 } else {
385 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
386 close_handle();
387 }
388 }
389}
390
391/** @ingroup tftp
392 * Initialize TFTP server.
393 * @param ctx TFTP callback struct
394 */
395err_t
396tftp_init(const struct tftp_context *ctx)
397{
398 err_t ret;
399
400 /* LWIP_ASSERT_CORE_LOCKED(); is checked by udp_new() */
401 struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
402 if (pcb == NULL) {
403 return ERR_MEM;
404 }
405
406 ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
407 if (ret != ERR_OK) {
408 udp_remove(pcb);
409 return ret;
410 }
411
412 tftp_state.handle = NULL;
413 tftp_state.port = 0;
414 tftp_state.ctx = ctx;
415 tftp_state.timer = 0;
416 tftp_state.last_data = NULL;
417 tftp_state.upcb = pcb;
418
419 udp_recv(pcb, recv, NULL);
420
421 return ERR_OK;
422}
423
424/** @ingroup tftp
425 * Deinitialize ("turn off") TFTP server.
426 */
427void tftp_cleanup(void)
428{
429 LWIP_ASSERT("Cleanup called on non-initialized TFTP", tftp_state.upcb != NULL);
430 udp_remove(tftp_state.upcb);
431 close_handle();
432 memset(&tftp_state, 0, sizeof(tftp_state));
433}
434
435#endif /* LWIP_UDP */
Note: See TracBrowser for help on using the repository browser.