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