1 /****************************************************************//**
2 *
3 * @file tftp_server.c
4 *
5 * @author Logan Gunthorpe <[email protected]>
6 * Dirk Ziegelmeier <[email protected]>
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 <[email protected]>
39 * Dirk Ziegelmeier <[email protected]>
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
67 enum 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
79 struct 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
93 static struct tftp_state tftp_state;
94
95 static void tftp_tmr(void* arg);
96
97 static void
close_handle(void)98 close_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
117 static void
send_error(const ip_addr_t * addr,u16_t port,enum tftp_error code,const char * str)118 send_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
138 static void
send_ack(u16_t blknum)139 send_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
156 static void
resend_data(void)157 resend_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
173 static void
send_data(void)174 send_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
203 static void
recv(void * arg,struct udp_pcb * upcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)204 recv(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 {
228 const char tftp_null = 0;
229 char filename[TFTP_MAX_FILENAME_LEN + 1] = { 0 };
230 char mode[TFTP_MAX_MODE_LEN] = { 0 };
231 u16_t filename_end_offset;
232 u16_t mode_end_offset;
233
234 if(tftp_state.handle != NULL) {
235 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Only one connection at a time is supported");
236 break;
237 }
238
239 sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
240
241 /* find \0 in pbuf -> end of filename string */
242 filename_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), 2);
243 if((u16_t)(filename_end_offset-2) > sizeof(filename)) {
244 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Filename too long/not NULL terminated");
245 break;
246 }
247 pbuf_copy_partial(p, filename, filename_end_offset-2, 2);
248
249 /* find \0 in pbuf -> end of mode string */
250 mode_end_offset = pbuf_memfind(p, &tftp_null, sizeof(tftp_null), filename_end_offset+1);
251 if((u16_t)(mode_end_offset-filename_end_offset) > sizeof(mode)) {
252 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Mode too long/not NULL terminated");
253 break;
254 }
255 pbuf_copy_partial(p, mode, mode_end_offset-filename_end_offset, filename_end_offset+1);
256
257 tftp_state.handle = tftp_state.ctx->open(filename, mode, opcode == PP_HTONS(TFTP_WRQ));
258 tftp_state.blknum = 1;
259
260 if (!tftp_state.handle) {
261 send_error(addr, port, TFTP_ERROR_FILE_NOT_FOUND, "Unable to open requested file.");
262 break;
263 }
264
265 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: %s request from ", (opcode == PP_HTONS(TFTP_WRQ)) ? "write" : "read"));
266 ip_addr_debug_print(TFTP_DEBUG | LWIP_DBG_STATE, addr);
267 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, (" for '%s' mode '%s'\n", filename, mode));
268
269 ip_addr_copy(tftp_state.addr, *addr);
270 tftp_state.port = port;
271
272 if (opcode == PP_HTONS(TFTP_WRQ)) {
273 tftp_state.mode_write = 1;
274 send_ack(0);
275 } else {
276 tftp_state.mode_write = 0;
277 send_data();
278 }
279
280 break;
281 }
282
283 case PP_HTONS(TFTP_DATA):
284 {
285 int ret;
286 u16_t blknum;
287
288 if (tftp_state.handle == NULL) {
289 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
290 break;
291 }
292
293 if (tftp_state.mode_write != 1) {
294 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a write connection");
295 break;
296 }
297
298 blknum = lwip_ntohs(sbuf[1]);
299 pbuf_header(p, -TFTP_HEADER_LENGTH);
300
301 ret = tftp_state.ctx->write(tftp_state.handle, p);
302 if (ret < 0) {
303 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "error writing file");
304 close_handle();
305 } else {
306 send_ack(blknum);
307 }
308
309 if (p->tot_len < TFTP_MAX_PAYLOAD_SIZE) {
310 close_handle();
311 }
312 break;
313 }
314
315 case PP_HTONS(TFTP_ACK):
316 {
317 u16_t blknum;
318 int lastpkt;
319
320 if (tftp_state.handle == NULL) {
321 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "No connection");
322 break;
323 }
324
325 if (tftp_state.mode_write != 0) {
326 send_error(addr, port, TFTP_ERROR_ACCESS_VIOLATION, "Not a read connection");
327 break;
328 }
329
330 blknum = lwip_ntohs(sbuf[1]);
331 if (blknum != tftp_state.blknum) {
332 send_error(addr, port, TFTP_ERROR_UNKNOWN_TRFR_ID, "Wrong block number");
333 break;
334 }
335
336 lastpkt = 0;
337
338 if (tftp_state.last_data != NULL) {
339 lastpkt = tftp_state.last_data->tot_len != (TFTP_MAX_PAYLOAD_SIZE + TFTP_HEADER_LENGTH);
340 }
341
342 if (!lastpkt) {
343 tftp_state.blknum++;
344 send_data();
345 } else {
346 close_handle();
347 }
348
349 break;
350 }
351
352 default:
353 send_error(addr, port, TFTP_ERROR_ILLEGAL_OPERATION, "Unknown operation");
354 break;
355 }
356
357 pbuf_free(p);
358 }
359
360 static void
tftp_tmr(void * arg)361 tftp_tmr(void* arg)
362 {
363 LWIP_UNUSED_ARG(arg);
364
365 tftp_state.timer++;
366
367 if (tftp_state.handle == NULL) {
368 return;
369 }
370
371 sys_timeout(TFTP_TIMER_MSECS, tftp_tmr, NULL);
372
373 if ((tftp_state.timer - tftp_state.last_pkt) > (TFTP_TIMEOUT_MSECS / TFTP_TIMER_MSECS)) {
374 if ((tftp_state.last_data != NULL) && (tftp_state.retries < TFTP_MAX_RETRIES)) {
375 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout, retrying\n"));
376 resend_data();
377 tftp_state.retries++;
378 } else {
379 LWIP_DEBUGF(TFTP_DEBUG | LWIP_DBG_STATE, ("tftp: timeout\n"));
380 close_handle();
381 }
382 }
383 }
384
385 /** @ingroup tftp
386 * Initialize TFTP server.
387 * @param ctx TFTP callback struct
388 */
389 err_t
tftp_init(const struct tftp_context * ctx)390 tftp_init(const struct tftp_context *ctx)
391 {
392 err_t ret;
393
394 struct udp_pcb *pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
395 if (pcb == NULL) {
396 return ERR_MEM;
397 }
398
399 ret = udp_bind(pcb, IP_ANY_TYPE, TFTP_PORT);
400 if (ret != ERR_OK) {
401 udp_remove(pcb);
402 return ret;
403 }
404
405 tftp_state.handle = NULL;
406 tftp_state.port = 0;
407 tftp_state.ctx = ctx;
408 tftp_state.timer = 0;
409 tftp_state.last_data = NULL;
410 tftp_state.upcb = pcb;
411
412 udp_recv(pcb, recv, NULL);
413
414 return ERR_OK;
415 }
416
417 #endif /* LWIP_UDP */
418