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/opt.h"
51 #include "lwip/udp.h"
52 #include "lwip/timeouts.h"
53 #include "lwip/debug.h"
54 #include "lwip/apps/tftp.h"
55
56 #include <string.h>
57 #include <stdio.h>
58
59 #ifdef WITH_LWIP_TFTP_FS
60 #include <fcntl.h>
61 #include "aos/vfs.h"
62 /* file description for tftp mount to file system. thread safe is not consider
63 as currently only one session is allowed for tftp */
64 static int fd_tftp_fs = -1;
65 #endif
66
67
68 struct tftp_state {
69 const tftp_context_t *ctx;
70 void *handle;
71 struct pbuf *last_data;
72 struct udp_pcb *upcb;
73 ip_addr_t addr;
74 u16_t port;
75 int timer;
76 int last_pkt;
77 u16_t blknum;
78 u8_t retries;
79 u8_t mode_write;
80 };
81
82 static struct tftp_state tftp_state;
83
84 static void tftp_tmr(void* arg);
85 void tftp_send_error(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port,
86 tftp_error_t code, const char *str);
87 void tftp_send_ack(struct udp_pcb *pcb, const ip_addr_t *addr, u16_t port, u16_t blknum);
88
89 static void
close_handle(void)90 close_handle(void)
91 {
92 tftp_state.port = 0;
93 ip_addr_set_any(0, &tftp_state.addr);
94
95 if(tftp_state.last_data != NULL) {
96 pbuf_free(tftp_state.last_data);
97 tftp_state.last_data = NULL;
98 }
99
100 sys_untimeout(tftp_tmr, NULL);
101
102 if (tftp_state.handle) {
103 tftp_state.ctx->close(tftp_state.handle);
104 tftp_state.handle = NULL;
105 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp server: closing\n"));
106 }
107 }
108
109 static void
resend_data(void)110 resend_data(void)
111 {
112 struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, tftp_state.last_data->len, PBUF_RAM);
113 if(p == NULL) {
114 return;
115 }
116
117 if(pbuf_copy(p, tftp_state.last_data) != ERR_OK) {
118 pbuf_free(p);
119 return;
120 }
121
122 udp_sendto(tftp_state.upcb, p, &tftp_state.addr, tftp_state.port);
123 pbuf_free(p);
124 }
125
126 static void
send_data(void)127 send_data(void)
128 {
129 u16_t *payload;
130 int ret;
131
132 if(tftp_state.last_data != NULL) {
133 pbuf_free(tftp_state.last_data);
134 }
135
136 tftp_state.last_data = pbuf_alloc(PBUF_TRANSPORT, TFTP_HEADER_LENGTH + TFTP_MAX_PAYLOAD_SIZE, PBUF_RAM);
137 if(tftp_state.last_data == NULL) {
138 return;
139 }
140
141 payload = (u16_t *) tftp_state.last_data->payload;
142 payload[0] = PP_HTONS(TFTP_DATA);
143 payload[1] = lwip_htons(tftp_state.blknum);
144
145 ret = tftp_state.ctx->read(tftp_state.handle, &payload[2], TFTP_MAX_PAYLOAD_SIZE);
146 if (ret < 0) {
147 tftp_send_error(tftp_state.upcb, &tftp_state.addr, tftp_state.port, TFTP_ERROR_ACCESS_VIOLATION, "Error occured while reading the file.");
148 close_handle();
149 return;
150 }
151
152 pbuf_realloc(tftp_state.last_data, (u16_t)(TFTP_HEADER_LENGTH + ret));
153 resend_data();
154 }
155
156 static void
recv(void * arg,struct udp_pcb * upcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)157 recv(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
158 {
159 u16_t *sbuf = (u16_t *) p->payload;
160 int opcode;
161
162 LWIP_UNUSED_ARG(arg);
163 LWIP_UNUSED_ARG(upcb);
164
165 if (((tftp_state.port != 0) && (port != tftp_state.port)) ||
166 (!ip_addr_isany_val(tftp_state.addr) && !ip_addr_cmp(&tftp_state.addr, addr))) {
167 tftp_send_error(tftp_state.upcb, addr, port,
168 TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
169 pbuf_free(p);
170 return;
171 }
172
173 opcode = sbuf[0];
174
175 tftp_state.last_pkt = tftp_state.timer;
176 tftp_state.retries = 0;
177
178 switch (opcode) {
179 case PP_HTONS(TFTP_RRQ): /* fall through */
180 case PP_HTONS(TFTP_WRQ):
181 {
182 const char tftp_null = 0;
183 char filename[TFTP_MAX_FILENAME_LEN] = {0};
184 char mode[TFTP_MAX_MODE_LEN];
185 u16_t filename_end_offset;
186 u16_t mode_end_offset;
187
188 if(tftp_state.handle != NULL) {
189 tftp_send_error(tftp_state.upcb, addr, port,
190 TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
191 break;
192 }
193
194 sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
195
196 /* find \0 in pbuf -> end of filename string */
197 filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
198 if((u16_t)(filename_end_offset-2) > sizeof(filename)) {
199 tftp_send_error(tftp_state.upcb, addr, port,
200 TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
201 break;
202 }
203 pbuf_copy_partial(p, filename, filename_end_offset-2, 2);
204
205 /* find \0 in pbuf -> end of mode string */
206 mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1);
207 if((u16_t)(mode_end_offset-filename_end_offset) > sizeof(mode)) {
208 tftp_send_error(tftp_state.upcb, addr, port,
209 TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
210 break;
211 }
212 pbuf_copy_partial(p, mode, mode_end_offset-filename_end_offset, filename_end_offset+1);
213
214 tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
215 tftp_state.blknum = 1;
216
217 if (!tftp_state.handle) {
218 tftp_send_error(tftp_state.upcb, addr, port,
219 TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
220 break;
221 }
222
223 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ",
224 (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
225 ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
226 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
227
228 ip_addr_copy(tftp_state.addr, *addr);
229 tftp_state.port = port;
230
231 if (opcode == PP_HTONS(TFTP_WRQ)) {
232 tftp_state.mode_write = 1;
233 tftp_send_ack(tftp_state.upcb, &tftp_state.addr, tftp_state.port, 0);
234 } else {
235 tftp_state.mode_write = 0;
236 send_data();
237 }
238
239 break;
240 }
241
242 case PP_HTONS(TFTP_DATA):
243 {
244 int ret;
245 u16_t blknum;
246
247 if (tftp_state.handle == NULL) {
248 tftp_send_error(tftp_state.upcb, addr, port,
249 TFTP_ERROR_ACCESS_VIOLATION, "No connection");
250 break;
251 }
252
253 if (tftp_state.mode_write != 1) {
254 tftp_send_error(tftp_state.upcb, addr, port,
255 TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
256 break;
257 }
258
259 blknum = lwip_ntohs(sbuf[1]);
260 pbuf_header(p, -TFTP_HEADER_LENGTH);
261
262 ret = tftp_state.ctx->write(tftp_state.handle, p);
263 if (ret < 0) {
264 tftp_send_error(tftp_state.upcb, addr, port,
265 TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
266 close_handle();
267 } else {
268 tftp_send_ack(tftp_state.upcb, &tftp_state.addr, tftp_state.port, blknum);
269 }
270
271 if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
272 close_handle();
273 }
274 break;
275 }
276
277 case PP_HTONS(TFTP_ACK):
278 {
279 u16_t blknum;
280 int lastpkt;
281
282 if (tftp_state.handle == NULL) {
283 tftp_send_error(tftp_state.upcb, addr, port,
284 TFTP_ERROR_ACCESS_VIOLATION, "No connection");
285 break;
286 }
287
288 if (tftp_state.mode_write != 0) {
289 tftp_send_error(tftp_state.upcb, addr, port,
290 TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
291 break;
292 }
293
294 blknum = lwip_ntohs(sbuf[1]);
295 if (blknum != tftp_state.blknum) {
296 tftp_send_error(tftp_state.upcb, addr, port,
297 TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
298 break;
299 }
300
301 lastpkt = 0;
302
303 if (tftp_state.last_data != NULL) {
304 lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
305 }
306
307 if (!lastpkt) {
308 tftp_state.blknum++;
309 send_data();
310 } else {
311 close_handle();
312 }
313
314 break;
315 }
316
317 default:
318 tftp_send_error(tftp_state.upcb, addr, port,
319 TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
320 break;
321 }
322
323 pbuf_free(p);
324 }
325
326 static void
tftp_tmr(void * arg)327 tftp_tmr(void* arg)
328 {
329 LWIP_UNUSED_ARG(arg);
330
331 tftp_state.timer++;
332
333 if (tftp_state.handle == NULL) {
334 return;
335 }
336
337 sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
338
339 if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
340 if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
341 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
342 resend_data();
343 tftp_state.retries++;
344 } else {
345 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
346 close_handle();
347 }
348 }
349 }
350
tftp_fopen(const char * fname,const char * mode,u8_t write)351 static void* tftp_fopen(const char* fname, const char* mode, u8_t write)
352 {
353 #ifdef WITH_LWIP_TFTP_FS
354 /* mount to the file system if both comp vfs and spiffs exit */
355 /* omit mode of BIN or ASCII */
356 if(write){
357 fd_tftp_fs = aos_open(fname, O_RDWR);
358 }else{
359 fd_tftp_fs = aos_open(fname, O_RDONLY);
360 }
361 if (fd_tftp_fs >= 0) {
362 printf(">>>open fd %d\n", fd_tftp_fs);
363 return &fd_tftp_fs;
364 } else {
365 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp fs failed: %d",fd_tftp_fs));
366 fd_tftp_fs = -1;
367 return NULL;
368 }
369 #else
370 FILE *fp = NULL;
371
372 if (strncmp(mode, "netascii", 8) == 0) {
373 fp = fopen(fname, write == 0 ? "r" : "w");
374 } else if (strncmp(mode, "octet", 5) == 0) {
375 fp = fopen(fname, write == 0 ? "rb" : "wb");
376 }
377 return (void*)fp;
378 #endif
379 }
380
tftp_fclose(void * handle)381 static void tftp_fclose(void* handle)
382 {
383 #ifdef WITH_LWIP_TFTP_FS
384 aos_close(*((int*)handle));
385 #else
386 fclose((FILE*)handle);
387 #endif
388 }
389
tftp_fread(void * handle,void * buf,int bytes)390 static int tftp_fread(void* handle, void* buf, int bytes)
391 {
392 #ifdef WITH_LWIP_TFTP_FS
393 return aos_read(*((int*)handle), buf, bytes);
394 #else
395 size_t readbytes;
396 readbytes = fread(buf, 1, (size_t)bytes, (FILE*)handle);
397 return (int)readbytes;
398 #endif
399 }
400
tftp_fwrite(void * handle,struct pbuf * p)401 static int tftp_fwrite(void* handle, struct pbuf* p)
402 {
403 char buff[512];
404 size_t writebytes = -1;
405 pbuf_copy_partial(p, buff, p->tot_len, 0);
406 #ifdef WITH_LWIP_TFTP_FS
407 writebytes = aos_write(*((int*)handle), buff, p->tot_len);
408 #else
409 writebytes = fwrite(buff, 1, p->tot_len, (FILE *)handle);
410 #endif
411 return (int)writebytes;
412 }
413
414 const tftp_context_t server_ctx = {
415 .open = tftp_fopen,
416 .close = tftp_fclose,
417 .read = tftp_fread,
418 .write = tftp_fwrite
419 };
420
421 /** @ingroup tftp
422 * Initialize TFTP server.
423 * @param ctx TFTP callback struct
424 */
425 err_t
tftp_server_start(void)426 tftp_server_start(void)
427 {
428 err_t ret;
429
430 if (tftp_state.upcb != NULL)
431 return ERR_OK;
432
433 struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
434 if (pcb == NULL) {
435 return ERR_MEM;
436 }
437
438 ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
439 if (ret != ERR_OK) {
440 udp_remove(pcb);
441 return ret;
442 }
443
444 tftp_state.handle = NULL;
445 tftp_state.port = 0;
446 tftp_state.ctx = &server_ctx;
447 tftp_state.timer = 0;
448 tftp_state.last_data = NULL;
449 tftp_state.upcb = pcb;
450 udp_recv(pcb, recv, NULL);
451
452 return ERR_OK;
453 }
454
455 void
tftp_server_stop(void)456 tftp_server_stop(void)
457 {
458 if (tftp_state.handle)
459 close_handle();
460
461 if (tftp_state.upcb != NULL) {
462 udp_remove(tftp_state.upcb);
463 memset(&tftp_state, 0, sizeof(tftp_state));
464 }
465 }
466