]> git.sur5r.net Git - freertos/blob - Demo/lwIP_AVR32_UC3/NETWORK/BasicTFTP/BasicTFTP.c
Configure release configuration for the Win32 lwIP project.
[freertos] / Demo / lwIP_AVR32_UC3 / NETWORK / BasicTFTP / BasicTFTP.c
1 /*This file has been prepared for Doxygen automatic documentation generation.*/\r
2 /*! \file *********************************************************************\r
3  *\r
4  * \brief Basic TFTP Server for AVR32 UC3.\r
5  *\r
6  * - Compiler:           GNU GCC for AVR32\r
7  * - Supported devices:  All AVR32 devices can be used.\r
8  * - AppNote:\r
9  *\r
10  * \author               Atmel Corporation: http://www.atmel.com \n\r
11  *                       Support and FAQ: http://support.atmel.no/\r
12  *\r
13  *****************************************************************************/\r
14 \r
15 /* Copyright (c) 2007, Atmel Corporation All rights reserved.\r
16  *\r
17  * Redistribution and use in source and binary forms, with or without\r
18  * modification, are permitted provided that the following conditions are met:\r
19  *\r
20  * 1. Redistributions of source code must retain the above copyright notice,\r
21  * this list of conditions and the following disclaimer.\r
22  *\r
23  * 2. Redistributions in binary form must reproduce the above copyright notice,\r
24  * this list of conditions and the following disclaimer in the documentation\r
25  * and/or other materials provided with the distribution.\r
26  *\r
27  * 3. The name of ATMEL may not be used to endorse or promote products derived\r
28  * from this software without specific prior written permission.\r
29  *\r
30  * THIS SOFTWARE IS PROVIDED BY ATMEL ``AS IS'' AND ANY EXPRESS OR IMPLIED\r
31  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
32  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND\r
33  * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT,\r
34  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
35  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
36  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\r
37  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
38  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\r
39  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
40  */\r
41 \r
42 \r
43 /*\r
44   Implements a simplistic TFTP server.\r
45   \r
46   In order to put data on the TFTP server (not over 2048 bytes)\r
47   tftp 192.168.0.2 PUT <src_filename>\r
48   this will copy file from your hard drive to the RAM buffer of the application\r
49 \r
50   tftp 192.168.0.2 GET <dst_filename>\r
51   this will copy file from the RAM buffer of the application to your hard drive\r
52   You can then check that src_filename and dst_filename are identical    \r
53 */\r
54 \r
55 #if (TFTP_USED == 1)\r
56 \r
57 #include <string.h>\r
58 \r
59 \r
60 /* Scheduler includes. */\r
61 #include "FreeRTOS.h"\r
62 #include "task.h"\r
63 #include "partest.h"\r
64 #include "BasicTFTP.h"\r
65 \r
66 \r
67 /* Demo includes. */\r
68 #include "portmacro.h"\r
69 \r
70 /* lwIP includes. */\r
71 #include "lwip/api.h"\r
72 #include "lwip/tcpip.h"\r
73 #include "lwip/memp.h"\r
74 #include "lwip/stats.h"\r
75 #include "lwip/opt.h"\r
76 #include "lwip/api.h"\r
77 #include "lwip/arch.h"\r
78 #include "lwip/sys.h"\r
79 #include "netif/loopif.h"\r
80 #include "lwip/sockets.h"\r
81 \r
82 #define O_WRONLY 1\r
83 #define O_RDONLY 2\r
84 \r
85 \r
86 /* The port on which we listen. */\r
87 #define TFTP_PORT    ( 69 )\r
88 \r
89 /* Delay on close error. */\r
90 #define TFTP_DELAY         ( 10 )\r
91 \r
92 /* Delay on bind error. */\r
93 #define TFTP_ERROR_DELAY    ( 40 )\r
94 \r
95 #define TFTP_LED     ( 4 )\r
96 \r
97 char data_out[SEGSIZE+sizeof(struct tftphdr)];\r
98 char data_in[SEGSIZE+sizeof(struct tftphdr)];\r
99 \r
100 //struct tftp_server *server;\r
101 \r
102 /*------------------------------------------------------------*/\r
103 static char * errmsg[] = {\r
104   "Undefined error code",               // 0 nothing defined\r
105   "File not found",                     // 1 TFTP_ENOTFOUND \r
106   "Access violation",                   // 2 TFTP_EACCESS   \r
107   "Disk full or allocation exceeded",   // 3 TFTP_ENOSPACE  \r
108   "Illegal TFTP operation",             // 4 TFTP_EBADOP    \r
109   "Unknown transfer ID",                // 5 TFTP_EBADID    \r
110   "File already exists",                // 6 TFTP_EEXISTS   \r
111   "No such user",                       // 7 TFTP_ENOUSER   \r
112 };\r
113 \r
114 \r
115 /* Send an error packet to the client */\r
116 static void \r
117 tftpd_send_error(int s, struct tftphdr * reply, int err,\r
118      struct sockaddr_in *from_addr, int from_len)\r
119 {\r
120     if ( ( 0 <= err ) && ( sizeof(errmsg)/sizeof(errmsg[0]) > err) ) {\r
121     reply->th_opcode = htons(ERROR);\r
122     reply->th_code = htons(err);\r
123     if ( (0 > err) || (sizeof(errmsg)/sizeof(errmsg[0]) <= err) )\r
124         err = 0; // Do not copy a random string from hyperspace\r
125     strcpy(reply->th_msg, errmsg[err]);\r
126     sendto(s, reply, 4+strlen(reply->th_msg)+1, 0, \r
127      (struct sockaddr *)from_addr, from_len);\r
128     }\r
129 }\r
130 \r
131 portCHAR cRamBuffer[2048];\r
132 int lCurrentBlock = 0;\r
133 int lTotalLength = 0;\r
134 \r
135 \r
136 int tftpd_close_data_file(int fd)\r
137 {\r
138   lCurrentBlock = 0;\r
139   return (5);\r
140 }\r
141 \r
142 int tftpd_open_data_file(int fd, int mode)\r
143 {\r
144   lCurrentBlock = 0; \r
145   return (5);\r
146 }\r
147 \r
148 int tftpd_read_data_file(int fd, portCHAR * buffer, int length)\r
149 {\r
150 int lReturnValue;\r
151 \r
152   if ((lTotalLength -= length) >= 0) {\r
153     lReturnValue = length;\r
154   }\r
155   else\r
156   {\r
157     lReturnValue = lTotalLength + length;\r
158     lTotalLength = 0;\r
159   }\r
160   memcpy(buffer, &cRamBuffer[lCurrentBlock * SEGSIZE], lReturnValue);\r
161   lCurrentBlock++;\r
162   return (lReturnValue);\r
163 }\r
164 \r
165 //\r
166 // callback to store data to the RAM buffer\r
167 //\r
168 int tftpd_write_data_file(int fd, portCHAR * buffer, int length)\r
169 {\r
170   lTotalLength += length;\r
171   memcpy(&cRamBuffer[lCurrentBlock * SEGSIZE], buffer, length);\r
172   lCurrentBlock++;\r
173   return (length);\r
174 }\r
175 \r
176 //\r
177 // Receive a file from the client\r
178 //\r
179 static void\r
180 tftpd_write_file(struct tftphdr *hdr,\r
181                  struct sockaddr_in *from_addr, int from_len)\r
182 {\r
183 \r
184     struct tftphdr *reply = (struct tftphdr *)data_out;\r
185     struct tftphdr *response = (struct tftphdr *)data_in;\r
186     int fd, len, ok, tries, closed, data_len, s;\r
187     unsigned short block;\r
188     struct timeval timeout;\r
189     fd_set fds;\r
190     int total_timeouts = 0;\r
191     struct sockaddr_in client_addr, local_addr;\r
192     int client_len;\r
193 \r
194 \r
195     s = socket(AF_INET, SOCK_DGRAM, 0);\r
196     if (s < 0) {\r
197         return;\r
198     }\r
199 \r
200     memset((char *)&local_addr, 0, sizeof(local_addr));\r
201     local_addr.sin_family = AF_INET;\r
202     local_addr.sin_len = sizeof(local_addr);\r
203     local_addr.sin_addr.s_addr = htonl(INADDR_ANY);\r
204     local_addr.sin_port = htons(INADDR_ANY);\r
205 \r
206     if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {\r
207         // Problem setting up my end\r
208         close(s);\r
209         return;\r
210     }\r
211 \r
212     if ((fd = tftpd_open_data_file((int)hdr->th_stuff, O_WRONLY)) < 0) {\r
213         tftpd_send_error(s,reply,TFTP_ENOTFOUND,from_addr, from_len);\r
214         close(s);\r
215         return;\r
216     }\r
217 \r
218     ok = pdTRUE;\r
219     closed = pdFALSE;\r
220     block = 0;\r
221     while (ok) {\r
222         // Send ACK telling client he can send data\r
223         reply->th_opcode = htons(ACK);\r
224         reply->th_block = htons(block++); // postincrement\r
225         for (tries = 0;  tries < TFTP_RETRIES_MAX;  tries++) {\r
226             sendto(s, reply, 4, 0, (struct sockaddr *)from_addr, from_len);\r
227         repeat_select:\r
228             timeout.tv_sec = TFTP_TIMEOUT_PERIOD;\r
229             timeout.tv_usec = 0;\r
230             FD_ZERO(&fds);\r
231             FD_SET(s, &fds);\r
232            vParTestToggleLED( TFTP_LED );\r
233            if (lwip_select(s+1, &fds, 0, 0, &timeout) <= 0) {\r
234                 if (++total_timeouts > TFTP_TIMEOUT_MAX) {\r
235                     tftpd_send_error(s,reply,TFTP_EBADOP,from_addr, from_len);\r
236                     ok = pdFALSE;\r
237                     break;\r
238                 }\r
239                 continue; // retry the send, using up one retry.\r
240             }\r
241             vParTestToggleLED( TFTP_LED );\r
242             // Some data has arrived\r
243             data_len = sizeof(data_in);\r
244             client_len = sizeof(client_addr);\r
245             if ((data_len = recvfrom(s, data_in, data_len, 0, \r
246                       (struct sockaddr *)&client_addr, &client_len)) < 0) {\r
247                 // What happened?  No data here!\r
248                 continue; // retry the send, using up one retry.\r
249             }\r
250             if (ntohs(response->th_opcode) == DATA &&\r
251                 ntohs(response->th_block) < block) {\r
252                 // Then it is repeat DATA with an old block; listen again,\r
253                 // but do not repeat sending the current ack, and do not\r
254                 // use up a retry count.  (we do re-send the ack if\r
255                 // subsequently we time out)\r
256                 goto repeat_select;\r
257             }\r
258             if (ntohs(response->th_opcode) == DATA &&\r
259                 ntohs(response->th_block) == block) {\r
260                 // Good data - write to file\r
261                 len = tftpd_write_data_file(fd, response->th_data, data_len-4);\r
262                 if (len < (data_len-4)) {\r
263                     // File is "full"\r
264                     tftpd_send_error(s,reply,TFTP_ENOSPACE,\r
265                                      from_addr, from_len);     \r
266                     ok = pdFALSE;  // Give up\r
267                     break; // out of the retries loop\r
268                 }\r
269                 if (data_len < (SEGSIZE+4)) {\r
270                     // End of file\r
271                     closed = pdTRUE;\r
272                     ok = pdFALSE;\r
273                     vParTestSetLED( 0 , 0 );\r
274 \r
275                     if (tftpd_close_data_file(fd) == -1) {\r
276                         tftpd_send_error(s,reply,TFTP_EACCESS,\r
277                                          from_addr, from_len);\r
278 \r
279                        break;  // out of the retries loop\r
280                     }\r
281                     // Exception to the loop structure: we must ACK the last\r
282                     // packet, the one that implied EOF:\r
283                     reply->th_opcode = htons(ACK);\r
284                     reply->th_block = htons(block++); // postincrement\r
285                     sendto(s, reply, 4, 0, (struct sockaddr *)from_addr, from_len);\r
286                     break; // out of the retries loop\r
287                 }\r
288                 // Happy!  Break out of the retries loop.\r
289                 break;\r
290             }\r
291         } // End of the retries loop.\r
292         if (TFTP_RETRIES_MAX <= tries) {\r
293             tftpd_send_error(s,reply,TFTP_EBADOP,from_addr, from_len);\r
294             ok = pdFALSE;\r
295         }\r
296     }\r
297     close(s);\r
298     if (!closed) {\r
299       tftpd_close_data_file(fd);\r
300     }\r
301 }\r
302 \r
303 \r
304 //\r
305 // Send a file to the client\r
306 //\r
307 static void\r
308 tftpd_read_file(struct tftphdr *hdr,\r
309                 struct sockaddr_in *from_addr, int from_len)\r
310 {\r
311     struct tftphdr *reply = (struct tftphdr *)data_out;\r
312     struct tftphdr *response = (struct tftphdr *)data_in;\r
313     int fd, len, tries, ok, data_len, s;\r
314     unsigned short block;\r
315     struct timeval timeout;\r
316     fd_set fds;\r
317     int total_timeouts = 0;\r
318     struct sockaddr_in client_addr, local_addr;\r
319     int client_len;\r
320 \r
321     s = socket(AF_INET, SOCK_DGRAM, 0);\r
322     if (s < 0) {\r
323         return;\r
324     }\r
325     memset((char *)&local_addr, 0, sizeof(local_addr));\r
326     local_addr.sin_family = AF_INET;\r
327     local_addr.sin_len = sizeof(local_addr);\r
328     local_addr.sin_addr.s_addr = htonl(INADDR_ANY);\r
329     local_addr.sin_port = htons(INADDR_ANY);\r
330     if (bind(s, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {\r
331         // Problem setting up my end\r
332         close(s);\r
333         return;\r
334     }\r
335     if ((fd = tftpd_open_data_file((int)hdr->th_stuff, O_RDONLY)) < 0) {\r
336         tftpd_send_error(s,reply,TFTP_ENOTFOUND,from_addr, from_len);\r
337         close(s);\r
338         return;\r
339     }\r
340     block = 0;\r
341     ok = pdTRUE;\r
342     while (ok) {\r
343         // Read next chunk of file\r
344         len = tftpd_read_data_file(fd, reply->th_data, SEGSIZE);\r
345         reply->th_block = htons(++block); // preincrement\r
346         reply->th_opcode = htons(DATA);\r
347         for (tries = 0;  tries < TFTP_RETRIES_MAX;  tries++) {\r
348             if (sendto(s, reply, 4+len, 0,\r
349                        (struct sockaddr *)from_addr, from_len) < 0) {\r
350                 // Something went wrong with the network!\r
351                 ok = pdFALSE;\r
352                 break;\r
353             }\r
354         repeat_select:\r
355             timeout.tv_sec = TFTP_TIMEOUT_PERIOD;\r
356             timeout.tv_usec = 0;\r
357             FD_ZERO(&fds);\r
358             FD_SET(s, &fds);\r
359             vParTestToggleLED( TFTP_LED );\r
360             if (select(s+1, &fds, 0, 0, &timeout) <= 0) {\r
361                 if (++total_timeouts > TFTP_TIMEOUT_MAX) {\r
362                     tftpd_send_error(s,reply,TFTP_EBADOP,from_addr, from_len);\r
363                     ok = pdFALSE;\r
364                     break;\r
365                 }\r
366                 continue; // retry the send, using up one retry.\r
367             }\r
368             vParTestToggleLED( TFTP_LED );\r
369             data_len = sizeof(data_in);\r
370             client_len = sizeof(client_addr);\r
371             if ((data_len = recvfrom(s, data_in, data_len, 0, \r
372                                      (struct sockaddr *)&client_addr,\r
373                                      &client_len)) < 0) {\r
374                 // What happened?  Maybe someone lied to us...\r
375                 continue; // retry the send, using up one retry.\r
376             }\r
377             if ((ntohs(response->th_opcode) == ACK) &&\r
378                 (ntohs(response->th_block) < block)) {\r
379                 // Then it is a repeat ACK for an old block; listen again,\r
380                 // but do not repeat sending the current block, and do not\r
381                 // use up a retry count.  (we do re-send the data if\r
382                 // subsequently we time out)\r
383                 goto repeat_select;\r
384             }\r
385             if ((ntohs(response->th_opcode) == ACK) &&\r
386                 (ntohs(response->th_block) == block)) {\r
387                 // Happy!  Break out of the retries loop.\r
388                 break;\r
389             }\r
390         } // End of the retries loop.\r
391         if (TFTP_RETRIES_MAX <= tries) {\r
392             tftpd_send_error(s,reply,TFTP_EBADOP,from_addr, from_len);\r
393             ok = pdFALSE;\r
394         }\r
395         if (len < SEGSIZE) {\r
396             break; // That's end of file then.\r
397         }\r
398     }\r
399     close(s);\r
400     tftpd_close_data_file(fd);\r
401 }\r
402 \r
403 \r
404 \r
405 portTASK_FUNCTION( vBasicTFTPServer, pvParameters )\r
406 {\r
407     int lSocket;\r
408     int lDataLen, lRecvLen, lFromLen;\r
409     struct sockaddr_in sLocalAddr, sFromAddr;\r
410     portCHAR cData[SEGSIZE+sizeof(struct tftphdr)];\r
411     struct tftphdr *sHdr = (struct tftphdr *)cData;\r
412 \r
413     // Set up port\r
414     // Network order in info; host order in server:\r
415 \r
416     for (;;) {\r
417         // Create socket\r
418         lSocket = socket(AF_INET, SOCK_DGRAM, 0);\r
419         if (lSocket < 0) {\r
420             return;\r
421         }\r
422         memset((char *)&sLocalAddr, 0, sizeof(sLocalAddr));\r
423         sLocalAddr.sin_family = AF_INET;\r
424         sLocalAddr.sin_len = sizeof(sLocalAddr);\r
425         sLocalAddr.sin_addr.s_addr = htonl(INADDR_ANY);\r
426         sLocalAddr.sin_port = TFTP_PORT;\r
427 \r
428         if (bind(lSocket, (struct sockaddr *)&sLocalAddr, sizeof(sLocalAddr)) < 0) {\r
429             // Problem setting up my end\r
430             close(lSocket);\r
431             return;\r
432         }\r
433 \r
434 \r
435         lRecvLen = sizeof(cData);\r
436         lFromLen = sizeof(sFromAddr);\r
437         lDataLen = recvfrom(lSocket, sHdr, lRecvLen, 0,\r
438                             (struct sockaddr *)&sFromAddr, &lFromLen);\r
439         vParTestSetLED( TFTP_LED , pdTRUE );\r
440         close(lSocket); // so that other servers can bind to the TFTP socket\r
441 \r
442         if ( lDataLen < 0) {\r
443 \r
444         } else {\r
445             switch (ntohs(sHdr->th_opcode)) {\r
446             case WRQ:\r
447                 tftpd_write_file(sHdr, &sFromAddr, lFromLen);\r
448                 vParTestSetLED( TFTP_LED , pdFALSE );\r
449                 break;\r
450             case RRQ:\r
451                 tftpd_read_file(sHdr, &sFromAddr, lFromLen);\r
452                 vParTestSetLED( TFTP_LED , pdFALSE );\r
453                 break;\r
454             case ACK:\r
455             case DATA:\r
456             case ERROR:\r
457                 vParTestSetLED( TFTP_LED , pdFALSE );\r
458                 // Ignore\r
459                 break;\r
460             default:\r
461                 for(;;)\r
462                 {\r
463                   vParTestToggleLED( TFTP_LED );\r
464                   vTaskDelay(200);                    \r
465                 }\r
466              }\r
467         }\r
468     }\r
469 }\r
470 #endif\r