├── test-in-docker ├── lwip-include │ ├── arch │ │ ├── bpstruct.h │ │ ├── epstruct.h │ │ ├── sys_arch.h │ │ └── cc.h │ ├── lwipopts.h │ └── sys_arch.c ├── Dockerfile └── src │ ├── CMakeLists.txt │ └── main.c ├── renovate.json ├── README.md ├── LICENSE ├── ftpd.h ├── vfs.h ├── vfs.c └── ftpd.c /test-in-docker/lwip-include/arch/bpstruct.h: -------------------------------------------------------------------------------- 1 | #pragma pack(push,1) 2 | -------------------------------------------------------------------------------- /test-in-docker/lwip-include/arch/epstruct.h: -------------------------------------------------------------------------------- 1 | #pragma pack(pop) 2 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test-in-docker/lwip-include/arch/sys_arch.h: -------------------------------------------------------------------------------- 1 | #ifndef LWIP_ARCH_SYS_ARCH_H 2 | #define LWIP_ARCH_SYS_ARCH_H 3 | 4 | #endif /* LWIP_ARCH_SYS_ARCH_H */ 5 | -------------------------------------------------------------------------------- /test-in-docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:13 AS builder 2 | 3 | RUN apt update && apt install -y unzip libpcap-dev build-essential git cmake && rm -rf /var/lib/dpkg/lists/* 4 | 5 | WORKDIR /src 6 | 7 | RUN git clone https://git.savannah.nongnu.org/git/lwip.git 8 | 9 | ADD http://elm-chan.org/fsw/ff/arc/ff14.zip . 10 | RUN unzip -q ff14.zip -d ff 11 | RUN sed -i -e 's/FF_FS_RPATH[ ]*0/FF_FS_RPATH 2/;s/FF_USE_MKFS[ ]*0/FF_USE_MKFS 1/;' ff/source/ffconf.h 12 | 13 | COPY . lwip-ftpd 14 | 15 | WORKDIR /build 16 | 17 | RUN cmake /src/lwip-ftpd/test-in-docker/src && make -j 18 | 19 | FROM debian:13 AS runner 20 | 21 | RUN apt update && apt install -y libpcap0.8 valgrind && rm -rf /var/lib/dpkg/lists/* 22 | 23 | WORKDIR /app 24 | 25 | COPY --from=builder /build/lwip-runner . 26 | 27 | CMD valgrind /app/lwip-runner 28 | -------------------------------------------------------------------------------- /test-in-docker/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8) 2 | 3 | project(lwip-runner) 4 | 5 | find_path( PCAP_INCLUDE_DIR NAMES pcap/pcap.h pcap.h HINTS "${PCAP_HINTS}/include") 6 | 7 | find_library( PCAP_LIBRARY NAMES pcap wpcap HINTS "${PCAP_HINTS}/lib") 8 | 9 | find_path(LWIP_DIR src/include/lwip/init.h ${CMAKE_CURRENT_SOURCE_DIR}/../../../lwip) 10 | 11 | include_directories( 12 | ${CMAKE_CURRENT_SOURCE_DIR}/../lwip-include 13 | ${PCAP_INCLUDE_DIR} 14 | ${LWIP_DIR}/src/include 15 | ${CMAKE_CURRENT_SOURCE_DIR}/../../../ff 16 | ${CMAKE_CURRENT_SOURCE_DIR}/../.. 17 | ) 18 | 19 | include(${LWIP_DIR}/src/Filelists.cmake) 20 | 21 | add_compile_options(-Wall -Wextra -Werror -g3) 22 | 23 | add_executable(lwip-runner 24 | ${CMAKE_CURRENT_SOURCE_DIR}/main.c 25 | ${CMAKE_CURRENT_SOURCE_DIR}/../lwip-include/sys_arch.c 26 | ${CMAKE_CURRENT_SOURCE_DIR}/../../ftpd.c 27 | ${CMAKE_CURRENT_SOURCE_DIR}/../../vfs.c 28 | ${CMAKE_CURRENT_SOURCE_DIR}/../../../ff/source/ff.c 29 | ) 30 | target_link_libraries(lwip-runner ${PCAP_LIBRARY} lwipcore) 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FTP server for lwip and tinyfatfs 2 | 3 | This repository contains the ftp-server for lwip Copyright (c) 2002 Florian 4 | Schulze and an API translation layer Copyright (c) 2013 Philipp Tölke. 5 | 6 | The translation layer translates between the vfs-API the ftp server uses and 7 | the well known tinyfatfs by ChaN, which can be downloaded at 8 | http://elm-chan.org/fsw/ff/00index_e.html . 9 | 10 | The translation-layer 11 | 12 | * is not reentrant and it is possible that multiple 13 | concurrent ftp-connections are *not* supported. 14 | * relies on malloc(). It would be easy to change this to use a pooled allocator 15 | * does not use long filenames for any function apart from getcwd(). 16 | 17 | All code in this repository is licensed under a 3-clause BSD license 18 | 19 | Patches, comments and pull-requests are welcome. 20 | 21 | ## To build a docker container for testing 22 | 23 | ``` 24 | docker build -f test-in-docker\Dockerfile -t lwip-ftpd-test . 25 | ``` 26 | 27 | It will start lwip running on IP `172.17.0.5`, be sure to change that if it is not your docker network. You can then access ftp by dialing that address. It uses a file called `/app/data` as backing store, you can mount that in, if needed. The tester will `mkfs` when there is no file-system in that file. 28 | -------------------------------------------------------------------------------- /test-in-docker/lwip-include/lwipopts.h: -------------------------------------------------------------------------------- 1 | #define TCP_MSS 1500 2 | #define TCP_WND 65535 3 | #define NO_SYS 1 4 | #define SYS_LIGHTWEIGHT_PROT 0 5 | 6 | #define MEM_LIBC_MALLOC 1 7 | #define MEMP_MEM_MALLOC 1 8 | #define MEM_USE_POOLS 0 9 | #define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 1 10 | 11 | #define LWIP_ETHERNET 1 12 | #define LWIP_IPV4 1 13 | #define LWIP_TCP 1 14 | #define LWIP_ARP 1 15 | #define LWIP_ICMP 1 16 | #define IP_FRAG 1 17 | 18 | #define LWIP_DEBUG 1 19 | #define FTPD_DEBUG LWIP_DBG_ON 20 | //#define IP4_DEBUG LWIP_DBG_ON 21 | //#define NETIF_DEBUG LWIP_DBG_ON 22 | //#define TCP_DEBUG LWIP_DBG_ON 23 | //#define ETHARP_DEBUG LWIP_DBG_ON 24 | 25 | #define PPP_SUPPORT 0 26 | #define LWIP_SOCKET 0 27 | #define LWIP_NETCONN 0 28 | #define LWIP_RAW 0 29 | #define LWIP_COMPAT_SOCKETS 0 30 | #define LWIP_STATS 0 31 | 32 | #define LWIP_CHECKSUM_CTRL_PER_NETIF 1 33 | 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Philipp Tölke 2 | Copyright (c) 2002, Florian Schulze 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of the {organization} nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /ftpd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002 Florian Schulze. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the authors nor the names of the contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | * 30 | * ftpd.h - This file is part of the FTP daemon for lwIP 31 | * 32 | */ 33 | 34 | #ifndef __FTPD_H__ 35 | #define __FTPD_H__ 36 | 37 | #ifdef __cplusplus 38 | extern "C" 39 | { 40 | #endif 41 | 42 | void ftpd_init(void); 43 | 44 | #ifdef __cplusplus 45 | } /* extern "C" */ 46 | #endif 47 | 48 | #endif /* __FTPD_H__ */ 49 | -------------------------------------------------------------------------------- /test-in-docker/lwip-include/sys_arch.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | * 27 | * This file is part of the lwIP TCP/IP stack. 28 | * 29 | * Author: Adam Dunkels 30 | * Simon Goldschmidt 31 | * 32 | */ 33 | 34 | #include 35 | #include 36 | 37 | #ifdef _WIN32 38 | 39 | #include 40 | 41 | LARGE_INTEGER freq, sys_start_time; 42 | 43 | static void sys_init_timing(void) 44 | { 45 | QueryPerformanceFrequency(&freq); 46 | QueryPerformanceCounter(&sys_start_time); 47 | } 48 | 49 | static LONGLONG sys_get_ms_longlong(void) 50 | { 51 | LONGLONG ret; 52 | LARGE_INTEGER now; 53 | #if NO_SYS 54 | if (freq.QuadPart == 0) { 55 | sys_init_timing(); 56 | } 57 | #endif /* NO_SYS */ 58 | QueryPerformanceCounter(&now); 59 | ret = now.QuadPart-sys_start_time.QuadPart; 60 | return (u32_t)(((ret)*1000)/freq.QuadPart); 61 | } 62 | 63 | u32_t sys_jiffies(void) 64 | { 65 | return (u32_t)sys_get_ms_longlong(); 66 | } 67 | 68 | u32_t sys_now(void) 69 | { 70 | return (u32_t)sys_get_ms_longlong(); 71 | } 72 | 73 | #else 74 | 75 | #include 76 | 77 | u32_t sys_now(void) 78 | { 79 | struct timespec ts; 80 | 81 | clock_gettime(CLOCK_MONOTONIC, &ts); 82 | return ts.tv_sec * 1000L + ts.tv_nsec / 1000000L; 83 | } 84 | 85 | u32_t sys_jiffies(void) 86 | { 87 | struct timespec ts; 88 | 89 | clock_gettime(CLOCK_MONOTONIC, &ts); 90 | return ts.tv_sec * 1000000000L + ts.tv_nsec; 91 | } 92 | 93 | #endif 94 | 95 | void sys_init(void) 96 | { 97 | #ifdef _WIN32 98 | sys_init_timing(); 99 | #endif 100 | } 101 | -------------------------------------------------------------------------------- /vfs.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013, Philipp Tölke 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef INCLUDE_VFS_H 28 | #define INCLUDE_VFS_H 29 | 30 | #ifdef __cplusplus 31 | extern "C" 32 | { 33 | #endif 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #define vfs_load_plugin(x) 41 | #define bcopy(src, dest, len) memmove(dest, src, len) 42 | 43 | #ifndef __time_t_defined 44 | typedef struct { 45 | short date; 46 | short time; 47 | } time_t; 48 | 49 | struct tm { 50 | int tm_year; 51 | int tm_mon; 52 | int tm_mday; 53 | int tm_hour; 54 | int tm_min; 55 | int tm_sec; 56 | }; 57 | #endif // __time_t_defined 58 | typedef DIR vfs_dir_t; 59 | typedef FIL vfs_file_t; 60 | typedef struct { 61 | long st_size; 62 | char st_mode; 63 | time_t st_mtime; 64 | } vfs_stat_t; 65 | typedef struct { 66 | char name[13]; 67 | } vfs_dirent_t; 68 | typedef FIL vfs_t; 69 | 70 | #define time(x) 71 | #define vfs_eof f_eof 72 | #define VFS_ISDIR(st_mode) ((st_mode) & AM_DIR) 73 | #define VFS_ISREG(st_mode) !((st_mode) & AM_DIR) 74 | #define vfs_rename(vfs, from, to) f_rename(from, to) 75 | #define VFS_IRWXU 0 76 | #define VFS_IRWXG 0 77 | #define VFS_IRWXO 0 78 | #define vfs_mkdir(vfs, name, mode) f_mkdir(name) 79 | #define vfs_rmdir(vfs, name) f_unlink(name) 80 | #define vfs_remove(vfs, name) f_unlink(name) 81 | #define vfs_chdir(vfs, dir) f_chdir(dir) 82 | char* vfs_getcwd(vfs_t* vfs, void*, int dummy); 83 | int vfs_read (void* buffer, int dummy, int len, vfs_file_t* file); 84 | int vfs_write (void* buffer, int dummy, int len, vfs_file_t* file); 85 | vfs_dirent_t* vfs_readdir(vfs_dir_t* dir); 86 | vfs_file_t* vfs_open(vfs_t* vfs, const char* filename, const char* mode); 87 | vfs_t* vfs_openfs(); 88 | void vfs_close(vfs_t* vfs); 89 | int vfs_stat(vfs_t* vfs, const char* filename, vfs_stat_t* st); 90 | void vfs_closedir(vfs_dir_t* dir); 91 | vfs_dir_t* vfs_opendir(vfs_t* vfs, const char* path); 92 | struct tm* gmtime(const time_t *c_t); 93 | 94 | #ifdef __cplusplus 95 | } /* extern "C" */ 96 | #endif 97 | 98 | #endif /* INCLUDE_VFS_H */ 99 | -------------------------------------------------------------------------------- /test-in-docker/lwip-include/arch/cc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2001-2003 Swedish Institute of Computer Science. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 3. The name of the author may not be used to endorse or promote products 14 | * derived from this software without specific prior written permission. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 25 | * OF SUCH DAMAGE. 26 | * 27 | * This file is part of the lwIP TCP/IP stack. 28 | * 29 | * Author: Adam Dunkels 30 | * 31 | */ 32 | #ifndef LWIP_ARCH_CC_H 33 | #define LWIP_ARCH_CC_H 34 | 35 | #include /* printf, fflush, FILE */ 36 | #include /* abort */ 37 | #include 38 | #include 39 | 40 | #ifdef _MSC_VER 41 | #pragma warning (disable: 4127) /* conditional expression is constant */ 42 | #pragma warning (disable: 4996) /* 'strncpy' was declared deprecated */ 43 | #pragma warning (disable: 4103) /* structure packing changed by including file */ 44 | #pragma warning (disable: 4820) /* 'x' bytes padding added after data member 'y' */ 45 | #endif 46 | 47 | //#define LWIP_PROVIDE_ERRNO 48 | 49 | /* Define platform endianness (might already be defined) */ 50 | #ifndef BYTE_ORDER 51 | #define BYTE_ORDER LITTLE_ENDIAN 52 | #endif /* BYTE_ORDER */ 53 | 54 | /* Define generic types used in lwIP */ 55 | typedef uint8_t u8_t; 56 | typedef int8_t s8_t; 57 | typedef uint16_t u16_t; 58 | typedef int16_t s16_t; 59 | typedef uint32_t u32_t; 60 | typedef int32_t s32_t; 61 | 62 | typedef size_t mem_ptr_t; 63 | typedef u32_t sys_prot_t; 64 | 65 | /* Define (sn)printf formatters for these lwIP types */ 66 | #define X8_F "02x" 67 | #define U16_F "hu" 68 | #define U32_F "lu" 69 | #define S32_F "ld" 70 | #define X32_F "lx" 71 | 72 | #ifdef __GNUC__ 73 | #define S16_F "d" 74 | #define X16_F "uX" 75 | #define SZT_F "u" 76 | #else 77 | #define S16_F "hd" 78 | #define X16_F "hx" 79 | #define SZT_F "lu" 80 | #endif 81 | 82 | /* Compiler hints for packing structures */ 83 | #define PACK_STRUCT_STRUCT 84 | #define PACK_STRUCT_USE_INCLUDES 85 | 86 | /* Plaform specific diagnostic output */ 87 | #define LWIP_PLATFORM_DIAG(x) do { printf x; } while(0) 88 | 89 | #define LWIP_PLATFORM_ASSERT(x) do { printf("Assertion \"%s\" failed at line %d in %s\n", \ 90 | x, __LINE__, __FILE__); fflush(NULL); abort(); } while(0) 91 | 92 | #define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ 93 | printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \ 94 | fflush(NULL);handler;} } while(0) 95 | 96 | #ifdef _MSC_VER 97 | /* C runtime functions redefined */ 98 | #define snprintf _snprintf 99 | #endif 100 | 101 | //u32_t dns_lookup_external_hosts_file(const char *name); 102 | 103 | #define LWIP_RAND() ((u32_t)rand()) 104 | 105 | #endif /* LWIP_ARCH_CC_H */ 106 | 107 | -------------------------------------------------------------------------------- /test-in-docker/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | // 4MiB should be enough for everyone 23 | #define DATA_SIZE (4*1024*1024) 24 | 25 | int data; 26 | 27 | DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void *buff) { 28 | if (pdrv != 0) return RES_NOTRDY; 29 | 30 | switch(cmd) { 31 | case CTRL_SYNC: 32 | fsync(data); 33 | return RES_OK; 34 | case GET_SECTOR_COUNT: 35 | *((LBA_t*)buff) = DATA_SIZE/512; 36 | return RES_OK; 37 | case GET_BLOCK_SIZE: 38 | *((WORD*)buff) = 512; 39 | return RES_OK; 40 | } 41 | return RES_PARERR; 42 | } 43 | 44 | DWORD get_fattime() { 45 | // I am lazy. 46 | return 0; 47 | } 48 | 49 | DSTATUS disk_initialize(BYTE pdrv) { 50 | if (pdrv != 0) return STA_NOINIT; 51 | 52 | data = open("data.img", O_CREAT | O_RDWR); 53 | printf("disk_initialize: open returns %d\n", data); 54 | 55 | return 0; 56 | } 57 | 58 | DSTATUS disk_status (BYTE pdrv) { 59 | if (pdrv != 0) return STA_NOINIT; 60 | return 0; 61 | } 62 | 63 | DRESULT disk_write(BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count) { 64 | if (pdrv != 0) return RES_NOTRDY; 65 | 66 | size_t r = lseek(data, 512*sector, SEEK_SET); 67 | printf("disk_write: lseek returns %u\n", (unsigned) r); 68 | 69 | r = write(data, buff, 512 * count); 70 | printf("disk_write(%u): write returns %u\n", count, (unsigned) r); 71 | 72 | return RES_OK; 73 | } 74 | 75 | DRESULT disk_read(BYTE pdrv, BYTE* buff, LBA_t sector, UINT count) { 76 | if (pdrv != 0) return RES_NOTRDY; 77 | 78 | size_t r = lseek(data, 512*sector, SEEK_SET); 79 | printf("disk_read: lseek returns %u\n", (unsigned) r); 80 | 81 | r = read(data, buff, 512 * count); 82 | printf("disk_read(%u): read returns %u\n", count, (unsigned) r); 83 | 84 | return RES_OK; 85 | } 86 | 87 | int dbg_printf(const char *fmt, ...) { 88 | va_list args; 89 | va_start(args, fmt); 90 | int r = vprintf(fmt, args); 91 | va_end(args); 92 | return r; 93 | } 94 | 95 | static err_t pcap_output(struct netif* netif, struct pbuf* p) { 96 | pcap_t *pcap = netif->state; 97 | printf("Sending packet with length %d\n", p->tot_len); 98 | 99 | int r = pcap_sendpacket(pcap, (uint8_t*)p->payload, p->tot_len); 100 | 101 | if (r != 0) { 102 | printf("Error sending packet\n"); 103 | printf("Error: %s\n", pcap_geterr(pcap)); 104 | return ERR_IF; 105 | } 106 | return ERR_OK; 107 | } 108 | 109 | static err_t init_callback(struct netif* netif) { 110 | netif->name[0] = 't'; 111 | netif->name[1] = 'p'; 112 | netif->linkoutput = pcap_output; 113 | netif->output = etharp_output; 114 | 115 | netif->mtu = 1500; 116 | netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET; 117 | 118 | netif_set_link_up(netif); 119 | 120 | return ERR_OK; 121 | } 122 | 123 | int main(int argc, char **argv) { 124 | pcap_t *pcap = pcap_open_live("eth0", 65536, 1, 100, NULL); 125 | char errbuf[PCAP_ERRBUF_SIZE]; 126 | int r = pcap_setnonblock(pcap, 1, errbuf); 127 | if (r == PCAP_ERROR) { 128 | printf("pcap_setnonblock returned: %s", errbuf); 129 | return 1; 130 | } 131 | 132 | FATFS fs = {}; 133 | unsigned char *buf[4096]; 134 | 135 | (void) argc; /* suppress unused warning */ 136 | (void) argv; 137 | 138 | { 139 | FRESULT r = f_mount(&fs, "", 1); 140 | printf("f_mount returns %d\n", r); 141 | if (r == FR_NO_FILESYSTEM) { 142 | r = f_mkfs("", NULL, buf, sizeof buf); 143 | printf("f_mkfs returns %d\n", r); 144 | r = f_mount(&fs, "", 1); 145 | printf("f_mount returns %d\n", r); 146 | } 147 | } 148 | 149 | struct netif netif; 150 | memset(&netif, 0, sizeof netif); 151 | netif.hwaddr_len = 6; 152 | memcpy(netif.hwaddr, "\xaa\x00\x00\x00\x00\x01", 6); 153 | 154 | ip4_addr_t ip, mask, gw; 155 | IP4_ADDR(&ip, 172,17,0,5); 156 | IP4_ADDR(&mask, 255,255,0,0); 157 | IP4_ADDR(&gw, 172,17,0,1); 158 | 159 | netif_add(&netif, &ip, &mask, &gw, pcap, init_callback, ethernet_input); 160 | netif_set_up(&netif); 161 | 162 | NETIF_SET_CHECKSUM_CTRL(&netif, 0x00FF); 163 | 164 | ftpd_init(); 165 | 166 | sys_restart_timeouts(); 167 | 168 | struct pcap_pkthdr *hdr = NULL; 169 | const unsigned char *data = NULL; 170 | while(1) { 171 | sys_check_timeouts(); 172 | int r = pcap_next_ex(pcap, &hdr, &data); 173 | switch (r) { 174 | case 0: 175 | // Timeout 176 | continue; 177 | case -1: 178 | printf("Error: %s\n", pcap_geterr(pcap)); 179 | continue; 180 | case 1: 181 | break; 182 | default: 183 | printf("Unknown result: %d\n", r); 184 | continue; 185 | } 186 | printf("Packet length: %d / %d\n", hdr->len, hdr->caplen); 187 | struct pbuf *pbuf = pbuf_alloc(PBUF_RAW, hdr->len, PBUF_RAM); 188 | memcpy(pbuf->payload, data, hdr->len); 189 | netif.input(pbuf, &netif); 190 | } 191 | 192 | return 0; 193 | } 194 | -------------------------------------------------------------------------------- /vfs.c: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2013, Philipp Tölke 2 | * All rights reserved. 3 | * 4 | * Redistribution and use in source and binary forms, with or without 5 | * modification, are permitted provided that the following conditions are met: 6 | * * Redistributions of source code must retain the above copyright 7 | * notice, this list of conditions and the following disclaimer. 8 | * * Redistributions in binary form must reproduce the above copyright 9 | * notice, this list of conditions and the following disclaimer in the 10 | * documentation and/or other materials provided with the distribution. 11 | * * Neither the name of the nor the 12 | * names of its contributors may be used to endorse or promote products 13 | * derived from this software without specific prior written permission. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 19 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include "vfs.h" 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | /* dirent that will be given to callers; 34 | * note: both APIs assume that only one dirent ever exists 35 | */ 36 | vfs_dirent_t dir_ent; 37 | 38 | FIL guard_for_the_whole_fs; 39 | 40 | int vfs_read (void* buffer, int dummy, int len, vfs_file_t* file) { 41 | unsigned int bytesread; 42 | (void) dummy; /* suppress unused warning */ 43 | FRESULT r = f_read(file, buffer, len, &bytesread); 44 | if (r != FR_OK) return 0; 45 | return bytesread; 46 | } 47 | 48 | vfs_dirent_t* vfs_readdir(vfs_dir_t* dir) { 49 | FILINFO fi; 50 | #if _USE_LFN 51 | fi.lfname = NULL; 52 | #endif 53 | FRESULT r = f_readdir(dir, &fi); 54 | if (r != FR_OK) return NULL; 55 | if (fi.fname[0] == 0) return NULL; 56 | memcpy(dir_ent.name, fi.fname, sizeof(fi.fname)); 57 | return &dir_ent; 58 | } 59 | 60 | int vfs_stat(vfs_t* vfs, const char* filename, vfs_stat_t* st) { 61 | FILINFO f; 62 | #if _USE_LFN 63 | f.lfname = NULL; 64 | #endif 65 | (void) vfs; /* suppress unused warning */ 66 | if (FR_OK != f_stat(filename, &f)) { 67 | return 1; 68 | } 69 | st->st_size = f.fsize; 70 | st->st_mode = f.fattrib; 71 | struct tm tm = { 72 | .tm_sec = 2*(f.ftime & 0x1f), 73 | .tm_min = (f.ftime >> 5) & 0x3f, 74 | .tm_hour = (f.ftime >> 11) & 0x1f, 75 | .tm_mday = f.fdate & 0x1f, 76 | .tm_mon = (f.fdate >> 5) & 0xf, 77 | .tm_year = 80 + ((f.fdate >> 9) & 0x7f), 78 | }; 79 | st->st_mtime = mktime(&tm); 80 | return 0; 81 | } 82 | 83 | void vfs_close(vfs_t* vfs) { 84 | if (vfs != &guard_for_the_whole_fs) { 85 | /* Close a file */ 86 | f_close(vfs); 87 | free(vfs); 88 | } 89 | } 90 | 91 | int vfs_write (void* buffer, int dummy, int len, vfs_file_t* file) { 92 | unsigned int byteswritten; 93 | (void) dummy; /* suppress unused warning */ 94 | 95 | FRESULT r = f_write(file, buffer, len, &byteswritten); 96 | if (r != FR_OK) return 0; 97 | return byteswritten; 98 | } 99 | 100 | vfs_t* vfs_openfs() { 101 | return &guard_for_the_whole_fs; 102 | } 103 | 104 | vfs_file_t* vfs_open(vfs_t* vfs, const char* filename, const char* mode) { 105 | vfs_file_t *f = malloc(sizeof(vfs_file_t)); 106 | BYTE flags = 0; 107 | (void) vfs; /* suppress unused warning */ 108 | 109 | while (*mode != '\0') { 110 | if (*mode == 'r') flags |= FA_READ; 111 | if (*mode == 'w') flags |= FA_WRITE | FA_CREATE_ALWAYS; 112 | mode++; 113 | } 114 | FRESULT r = f_open(f, filename, flags); 115 | if (FR_OK != r) { 116 | free(f); 117 | return NULL; 118 | } 119 | return f; 120 | } 121 | 122 | char* vfs_getcwd(vfs_t* vfs, void* dummy1, int dummy2) { 123 | char* cwd = malloc(255); 124 | FRESULT r = f_getcwd(cwd, 255); 125 | (void) dummy1; /* suppress unused warning */ 126 | (void) dummy2; 127 | (void) vfs; 128 | 129 | if (r != FR_OK) { 130 | free(cwd); 131 | return NULL; 132 | } 133 | return cwd; 134 | } 135 | 136 | vfs_dir_t* vfs_opendir(vfs_t* vfs, const char* path) { 137 | vfs_dir_t* dir = malloc(sizeof *dir); 138 | FRESULT r = f_opendir(dir, path); 139 | (void) vfs; /* suppress unused warning */ 140 | if (FR_OK != r) { 141 | free(dir); 142 | return NULL; 143 | } 144 | return dir; 145 | } 146 | 147 | void vfs_closedir(vfs_dir_t* dir) { 148 | if (dir) { 149 | f_closedir(dir); 150 | free(dir); 151 | } 152 | } 153 | 154 | struct tm dummy = { 155 | .tm_year = 70, 156 | .tm_mon = 0, 157 | .tm_mday = 1, 158 | .tm_hour = 0, 159 | .tm_min = 0 160 | }; 161 | struct tm* gmtime(const time_t* c_t) { 162 | (void) c_t; 163 | return &dummy; 164 | } 165 | -------------------------------------------------------------------------------- /ftpd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002 Florian Schulze. 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the authors nor the names of the contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | * 30 | * ftpd.c - This file is part of the FTP daemon for lwIP 31 | * 32 | */ 33 | 34 | #include "lwip/debug.h" 35 | 36 | #include "lwip/stats.h" 37 | 38 | #include "ftpd.h" 39 | 40 | #include "lwip/tcp.h" 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #include "vfs.h" 49 | 50 | #define EINVAL 1 51 | #define ENOMEM 2 52 | #define ENODEV 3 53 | 54 | #define msg110 "110 MARK %s = %s." 55 | /* 56 | 110 Restart marker reply. 57 | In this case, the text is exact and not left to the 58 | particular implementation; it must read: 59 | MARK yyyy = mmmm 60 | Where yyyy is User-process data stream marker, and mmmm 61 | server's equivalent marker (note the spaces between markers 62 | and "="). 63 | */ 64 | #define msg120 "120 Service ready in nnn minutes." 65 | #define msg125 "125 Data connection already open; transfer starting." 66 | #define msg150 "150 File status okay; about to open data connection." 67 | #define msg150recv "150 Opening BINARY mode data connection for %s (%i bytes)." 68 | #define msg150stor "150 Opening BINARY mode data connection for %s." 69 | #define msg200 "200 Command okay." 70 | #define msg202 "202 Command not implemented, superfluous at this site." 71 | #define msg211 "211 System status, or system help reply." 72 | #define msg212 "212 Directory status." 73 | #define msg213 "213 File status." 74 | #define msg214 "214 %s." 75 | /* 76 | 214 Help message. 77 | On how to use the server or the meaning of a particular 78 | non-standard command. This reply is useful only to the 79 | human user. 80 | */ 81 | #define msg214SYST "214 %s system type." 82 | /* 83 | 215 NAME system type. 84 | Where NAME is an official system name from the list in the 85 | Assigned Numbers document. 86 | */ 87 | #define msg220 "220 lwIP FTP Server ready." 88 | /* 89 | 220 Service ready for new user. 90 | */ 91 | #define msg221 "221 Goodbye." 92 | /* 93 | 221 Service closing control connection. 94 | Logged out if appropriate. 95 | */ 96 | #define msg225 "225 Data connection open; no transfer in progress." 97 | #define msg226 "226 Closing data connection." 98 | /* 99 | Requested file action successful (for example, file 100 | transfer or file abort). 101 | */ 102 | #define msg227 "227 Entering Passive Mode (%i,%i,%i,%i,%i,%i)." 103 | /* 104 | 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). 105 | */ 106 | #define msg230 "230 User logged in, proceed." 107 | #define msg250 "250 Requested file action okay, completed." 108 | #define msg257PWD "257 \"%s\" is current directory." 109 | #define msg257 "257 \"%s\" created." 110 | /* 111 | 257 "PATHNAME" created. 112 | */ 113 | #define msg331 "331 User name okay, need password." 114 | #define msg332 "332 Need account for login." 115 | #define msg350 "350 Requested file action pending further information." 116 | #define msg421 "421 Service not available, closing control connection." 117 | /* 118 | This may be a reply to any command if the service knows it 119 | must shut down. 120 | */ 121 | #define msg425 "425 Can't open data connection." 122 | #define msg426 "426 Connection closed; transfer aborted." 123 | #define msg450 "450 Requested file action not taken." 124 | /* 125 | File unavailable (e.g., file busy). 126 | */ 127 | #define msg451 "451 Requested action aborted: local error in processing." 128 | #define msg452 "452 Requested action not taken." 129 | /* 130 | Insufficient storage space in system. 131 | */ 132 | #define msg500 "500 Syntax error, command unrecognized." 133 | /* 134 | This may include errors such as command line too long. 135 | */ 136 | #define msg501 "501 Syntax error in parameters or arguments." 137 | #define msg502 "502 Command not implemented." 138 | #define msg503 "503 Bad sequence of commands." 139 | #define msg504 "504 Command not implemented for that parameter." 140 | #define msg530 "530 Not logged in." 141 | #define msg532 "532 Need account for storing files." 142 | #define msg550 "550 Requested action not taken." 143 | /* 144 | File unavailable (e.g., file not found, no access). 145 | */ 146 | #define msg551 "551 Requested action aborted: page type unknown." 147 | #define msg552 "552 Requested file action aborted." 148 | /* 149 | Exceeded storage allocation (for current directory or 150 | dataset). 151 | */ 152 | #define msg553 "553 Requested action not taken." 153 | /* 154 | File name not allowed. 155 | */ 156 | 157 | enum ftpd_state_e { 158 | FTPD_USER, 159 | FTPD_PASS, 160 | FTPD_IDLE, 161 | FTPD_NLST, 162 | FTPD_LIST, 163 | FTPD_RETR, 164 | FTPD_RNFR, 165 | FTPD_STOR, 166 | FTPD_QUIT 167 | }; 168 | 169 | static const char *month_table[12] = { 170 | "Jan", 171 | "Feb", 172 | "Mar", 173 | "Apr", 174 | "May", 175 | "Jun", 176 | "Jul", 177 | "Aug", 178 | "Sep", 179 | "Oct", 180 | "Nov", 181 | "Dec" 182 | }; 183 | 184 | /* 185 | ------------------------------------------------------------ 186 | SFIFO 1.3 187 | ------------------------------------------------------------ 188 | * Simple portable lock-free FIFO 189 | * (c) 2000-2002, David Olofson 190 | * 191 | * Platform support: 192 | * gcc / Linux / x86: Works 193 | * gcc / Linux / x86 kernel: Works 194 | * gcc / FreeBSD / x86: Works 195 | * gcc / NetBSD / x86: Works 196 | * gcc / Mac OS X / PPC: Works 197 | * gcc / Win32 / x86: Works 198 | * Borland C++ / DOS / x86RM: Works 199 | * Borland C++ / Win32 / x86PM16: Untested 200 | * ? / Various Un*ces / ?: Untested 201 | * ? / Mac OS / PPC: Untested 202 | * gcc / BeOS / x86: Untested 203 | * gcc / BeOS / PPC: Untested 204 | * ? / ? / Alpha: Untested 205 | * 206 | * 1.2: Max buffer size halved, to avoid problems with 207 | * the sign bit... 208 | * 209 | * 1.3: Critical buffer allocation bug fixed! For certain 210 | * requested buffer sizes, older version would 211 | * allocate a buffer of insufficient size, which 212 | * would result in memory thrashing. (Amazing that 213 | * I've manage to use this to the extent I have 214 | * without running into this... *heh*) 215 | */ 216 | 217 | /* 218 | * Porting note: 219 | * Reads and writes of a variable of this type in memory 220 | * must be *atomic*! 'int' is *not* atomic on all platforms. 221 | * A safe type should be used, and sfifo should limit the 222 | * maximum buffer size accordingly. 223 | */ 224 | typedef int sfifo_atomic_t; 225 | #ifdef __TURBOC__ 226 | # define SFIFO_MAX_BUFFER_SIZE 0x7fff 227 | #else /* Kludge: Assume 32 bit platform */ 228 | # define SFIFO_MAX_BUFFER_SIZE 0x7fffffff 229 | #endif 230 | 231 | typedef struct sfifo_t 232 | { 233 | char *buffer; 234 | int size; /* Number of bytes */ 235 | sfifo_atomic_t readpos; /* Read position */ 236 | sfifo_atomic_t writepos; /* Write position */ 237 | } sfifo_t; 238 | 239 | #define SFIFO_SIZEMASK(x) ((x)->size - 1) 240 | 241 | #define sfifo_used(x) (((x)->writepos - (x)->readpos) & SFIFO_SIZEMASK(x)) 242 | #define sfifo_space(x) ((x)->size - 1 - sfifo_used(x)) 243 | 244 | /* 245 | * Alloc buffer, init FIFO etc... 246 | */ 247 | static int sfifo_init(sfifo_t *f, int size) 248 | { 249 | memset(f, 0, sizeof(sfifo_t)); 250 | 251 | if(size > SFIFO_MAX_BUFFER_SIZE) 252 | return -EINVAL; 253 | 254 | /* 255 | * Set sufficient power-of-2 size. 256 | * 257 | * No, there's no bug. If you need 258 | * room for N bytes, the buffer must 259 | * be at least N+1 bytes. (The fifo 260 | * can't tell 'empty' from 'full' 261 | * without unsafe index manipulations 262 | * otherwise.) 263 | */ 264 | f->size = 1; 265 | for(; f->size <= size; f->size <<= 1) 266 | ; 267 | 268 | /* Get buffer */ 269 | if( 0 == (f->buffer = (void *)malloc(f->size)) ) 270 | return -ENOMEM; 271 | 272 | return 0; 273 | } 274 | 275 | /* 276 | * Dealloc buffer etc... 277 | */ 278 | static void sfifo_close(sfifo_t *f) 279 | { 280 | if(f->buffer) 281 | free(f->buffer); 282 | } 283 | 284 | /* 285 | * Write bytes to a FIFO 286 | * Return number of bytes written, or an error code 287 | */ 288 | static int sfifo_write(sfifo_t *f, const void *_buf, int len) 289 | { 290 | int total; 291 | int i; 292 | const char *buf = (const char *)_buf; 293 | 294 | if(!f->buffer) 295 | return -ENODEV; /* No buffer! */ 296 | 297 | /* total = len = min(space, len) */ 298 | total = sfifo_space(f); 299 | LWIP_DEBUGF(FTPD_DEBUG, ("sfifo_space() = %d\n",total)); 300 | if(len > total) 301 | len = total; 302 | else 303 | total = len; 304 | 305 | i = f->writepos; 306 | if(i + len > f->size) 307 | { 308 | memcpy(f->buffer + i, buf, f->size - i); 309 | buf += f->size - i; 310 | len -= f->size - i; 311 | i = 0; 312 | } 313 | memcpy(f->buffer + i, buf, len); 314 | f->writepos = i + len; 315 | 316 | return total; 317 | } 318 | 319 | struct ftpd_datastate { 320 | int connected; 321 | vfs_dir_t *vfs_dir; 322 | vfs_dirent_t *vfs_dirent; 323 | vfs_file_t *vfs_file; 324 | sfifo_t fifo; 325 | struct tcp_pcb *msgpcb; 326 | struct ftpd_msgstate *msgfs; 327 | }; 328 | 329 | struct ftpd_msgstate { 330 | enum ftpd_state_e state; 331 | sfifo_t fifo; 332 | vfs_t *vfs; 333 | struct ip4_addr dataip; 334 | u16_t dataport; 335 | struct tcp_pcb *datalistenpcb; 336 | struct tcp_pcb *datapcb; 337 | struct ftpd_datastate *datafs; 338 | int passive; 339 | char *renamefrom; 340 | }; 341 | 342 | static void send_msg(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm, char *msg, ...); 343 | 344 | static void ftpd_dataerr(void *arg, err_t err) 345 | { 346 | struct ftpd_datastate *fsd = arg; 347 | 348 | LWIP_DEBUGF(FTPD_DEBUG, ("ftpd_dataerr: %s (%i)\n", lwip_strerr(err), err)); 349 | if (fsd == NULL) 350 | return; 351 | fsd->msgfs->datafs = NULL; 352 | fsd->msgfs->state = FTPD_IDLE; 353 | free(fsd); 354 | } 355 | 356 | static void ftpd_dataclose(struct tcp_pcb *pcb, struct ftpd_datastate *fsd) 357 | { 358 | tcp_arg(pcb, NULL); 359 | tcp_sent(pcb, NULL); 360 | tcp_recv(pcb, NULL); 361 | 362 | if (fsd->msgfs->datalistenpcb) { 363 | tcp_arg(fsd->msgfs->datalistenpcb, NULL); 364 | tcp_accept(fsd->msgfs->datalistenpcb, NULL); 365 | tcp_close(fsd->msgfs->datalistenpcb); 366 | fsd->msgfs->datalistenpcb = NULL; 367 | } 368 | 369 | fsd->msgfs->datafs = NULL; 370 | sfifo_close(&fsd->fifo); 371 | free(fsd); 372 | tcp_arg(pcb, NULL); 373 | tcp_close(pcb); 374 | } 375 | 376 | static void close_with_message(struct ftpd_datastate *fsd, struct tcp_pcb *pcb, char* msg) { 377 | struct ftpd_msgstate *fsm; 378 | struct tcp_pcb *msgpcb; 379 | 380 | fsm = fsd->msgfs; 381 | msgpcb = fsd->msgpcb; 382 | ftpd_dataclose(pcb, fsd); 383 | fsm->datapcb = NULL; 384 | fsm->datafs = NULL; 385 | fsm->state = FTPD_IDLE; 386 | send_msg(msgpcb, fsm, msg); 387 | } 388 | 389 | 390 | static void send_data(struct tcp_pcb *pcb, struct ftpd_datastate *fsd) 391 | { 392 | err_t err; 393 | u16_t len; 394 | 395 | if (sfifo_used(&fsd->fifo) > 0) { 396 | int i; 397 | 398 | /* We cannot send more data than space available in the send 399 | buffer. */ 400 | if (tcp_sndbuf(pcb) < sfifo_used(&fsd->fifo)) { 401 | len = tcp_sndbuf(pcb); 402 | } else { 403 | len = (u16_t) sfifo_used(&fsd->fifo); 404 | } 405 | 406 | i = fsd->fifo.readpos; 407 | if ((i + len) > fsd->fifo.size) { 408 | err = tcp_write(pcb, fsd->fifo.buffer + i, (u16_t)(fsd->fifo.size - i), 1); 409 | if (err != ERR_OK) { 410 | LWIP_DEBUGF(FTPD_DEBUG, ("send_data: error writing!\n")); 411 | return; 412 | } 413 | len -= fsd->fifo.size - i; 414 | fsd->fifo.readpos = 0; 415 | i = 0; 416 | } 417 | 418 | err = tcp_write(pcb, fsd->fifo.buffer + i, len, 1); 419 | if (err != ERR_OK) { 420 | LWIP_DEBUGF(FTPD_DEBUG, ("send_data: error writing!\n")); 421 | return; 422 | } 423 | fsd->fifo.readpos += len; 424 | } 425 | } 426 | 427 | static void send_file(struct ftpd_datastate *fsd, struct tcp_pcb *pcb) 428 | { 429 | if (!fsd->connected) 430 | return; 431 | 432 | if (fsd->vfs_file) { 433 | char buffer[2048]; 434 | int len; 435 | 436 | len = sfifo_space(&fsd->fifo); 437 | if (len == 0) { 438 | send_data(pcb, fsd); 439 | return; 440 | } 441 | if (len > 2048) 442 | len = 2048; 443 | len = vfs_read(buffer, 1, len, fsd->vfs_file); 444 | if (len == 0) { 445 | if (vfs_eof(fsd->vfs_file) == 0) 446 | return; 447 | vfs_close(fsd->vfs_file); 448 | fsd->vfs_file = NULL; 449 | return; 450 | } 451 | sfifo_write(&fsd->fifo, buffer, len); 452 | send_data(pcb, fsd); 453 | } else { 454 | if (sfifo_used(&fsd->fifo) > 0) { 455 | send_data(pcb, fsd); 456 | return; 457 | } 458 | close_with_message(fsd, pcb, msg226); 459 | return; 460 | } 461 | } 462 | 463 | static void send_next_directory(struct ftpd_datastate *fsd, struct tcp_pcb *pcb, int shortlist) 464 | { 465 | char buffer[1024]; 466 | int len; 467 | 468 | while (1) { 469 | if (fsd->vfs_dirent == NULL) 470 | fsd->vfs_dirent = vfs_readdir(fsd->vfs_dir); 471 | 472 | if (fsd->vfs_dirent) { 473 | if (shortlist) { 474 | len = sprintf(buffer, "%s\r\n", fsd->vfs_dirent->name); 475 | if (sfifo_space(&fsd->fifo) < len) { 476 | send_data(pcb, fsd); 477 | return; 478 | } 479 | sfifo_write(&fsd->fifo, buffer, len); 480 | fsd->vfs_dirent = NULL; 481 | } else { 482 | vfs_stat_t st; 483 | time_t current_time; 484 | int current_year; 485 | struct tm *s_time; 486 | 487 | time(¤t_time); 488 | s_time = gmtime(¤t_time); 489 | current_year = s_time->tm_year; 490 | 491 | vfs_stat(fsd->msgfs->vfs, fsd->vfs_dirent->name, &st); 492 | s_time = gmtime(&st.st_mtime); 493 | if (s_time->tm_year == current_year) 494 | len = sprintf(buffer, "-rw-rw-rw- 1 user ftp %11ld %s %02i %02i:%02i %s\r\n", st.st_size, month_table[s_time->tm_mon], s_time->tm_mday, s_time->tm_hour, s_time->tm_min, fsd->vfs_dirent->name); 495 | else 496 | len = sprintf(buffer, "-rw-rw-rw- 1 user ftp %11ld %s %02i %5i %s\r\n", st.st_size, month_table[s_time->tm_mon], s_time->tm_mday, s_time->tm_year + 1900, fsd->vfs_dirent->name); 497 | if (VFS_ISDIR(st.st_mode)) 498 | buffer[0] = 'd'; 499 | if (sfifo_space(&fsd->fifo) < len) { 500 | send_data(pcb, fsd); 501 | return; 502 | } 503 | sfifo_write(&fsd->fifo, buffer, len); 504 | fsd->vfs_dirent = NULL; 505 | } 506 | } else { 507 | if (sfifo_used(&fsd->fifo) > 0) { 508 | send_data(pcb, fsd); 509 | return; 510 | } 511 | 512 | vfs_closedir(fsd->vfs_dir); 513 | fsd->vfs_dir = NULL; 514 | close_with_message(fsd, pcb, msg226); 515 | return; 516 | } 517 | } 518 | } 519 | 520 | static err_t ftpd_datasent(void *arg, struct tcp_pcb *pcb, u16_t len) 521 | { 522 | struct ftpd_datastate *fsd = arg; 523 | (void) len; /* suppress unused warning */ 524 | 525 | switch (fsd->msgfs->state) { 526 | case FTPD_LIST: 527 | send_next_directory(fsd, pcb, 0); 528 | break; 529 | case FTPD_NLST: 530 | send_next_directory(fsd, pcb, 1); 531 | break; 532 | case FTPD_RETR: 533 | send_file(fsd, pcb); 534 | break; 535 | default: 536 | break; 537 | } 538 | 539 | return ERR_OK; 540 | } 541 | 542 | static err_t ftpd_datarecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) 543 | { 544 | struct ftpd_datastate *fsd = arg; 545 | 546 | if (err == ERR_OK && p != NULL) { 547 | struct pbuf *q; 548 | u16_t tot_len = 0; 549 | 550 | for (q = p; q != NULL; q = q->next) { 551 | int len; 552 | 553 | len = vfs_write(q->payload, 1, q->len, fsd->vfs_file); 554 | tot_len += len; 555 | if (len != q->len) { 556 | vfs_close(fsd->vfs_file); 557 | fsd->vfs_file = NULL; 558 | close_with_message(fsd, pcb, msg553); 559 | break; 560 | } 561 | } 562 | 563 | /* Inform TCP that we have taken the data. */ 564 | tcp_recved(pcb, tot_len); 565 | 566 | pbuf_free(p); 567 | } 568 | if (err == ERR_OK && p == NULL) { 569 | vfs_close(fsd->vfs_file); 570 | fsd->vfs_file = NULL; 571 | close_with_message(fsd, pcb, msg226); 572 | } 573 | 574 | return ERR_OK; 575 | } 576 | 577 | static err_t ftpd_dataconnected(void *arg, struct tcp_pcb *pcb, err_t err) 578 | { 579 | struct ftpd_datastate *fsd = arg; 580 | (void) err; /* suppress unused warning */ 581 | 582 | fsd->msgfs->datapcb = pcb; 583 | fsd->connected = 1; 584 | 585 | /* Tell TCP that we wish to be informed of incoming data by a call 586 | to the http_recv() function. */ 587 | tcp_recv(pcb, ftpd_datarecv); 588 | 589 | /* Tell TCP that we wish be to informed of data that has been 590 | successfully sent by a call to the ftpd_sent() function. */ 591 | tcp_sent(pcb, ftpd_datasent); 592 | 593 | tcp_err(pcb, ftpd_dataerr); 594 | 595 | switch (fsd->msgfs->state) { 596 | case FTPD_LIST: 597 | send_next_directory(fsd, pcb, 0); 598 | break; 599 | case FTPD_NLST: 600 | send_next_directory(fsd, pcb, 1); 601 | break; 602 | case FTPD_RETR: 603 | send_file(fsd, pcb); 604 | break; 605 | default: 606 | break; 607 | } 608 | 609 | return ERR_OK; 610 | } 611 | 612 | static err_t ftpd_dataaccept(void *arg, struct tcp_pcb *pcb, err_t err) 613 | { 614 | struct ftpd_datastate *fsd = arg; 615 | 616 | (void) err; /* suppress unused warning */ 617 | 618 | fsd->msgfs->datapcb = pcb; 619 | fsd->connected = 1; 620 | 621 | /* Tell TCP that we wish to be informed of incoming data by a call 622 | to the http_recv() function. */ 623 | tcp_recv(pcb, ftpd_datarecv); 624 | 625 | /* Tell TCP that we wish be to informed of data that has been 626 | successfully sent by a call to the ftpd_sent() function. */ 627 | tcp_sent(pcb, ftpd_datasent); 628 | 629 | tcp_err(pcb, ftpd_dataerr); 630 | 631 | switch (fsd->msgfs->state) { 632 | case FTPD_LIST: 633 | send_next_directory(fsd, pcb, 0); 634 | break; 635 | case FTPD_NLST: 636 | send_next_directory(fsd, pcb, 1); 637 | break; 638 | case FTPD_RETR: 639 | send_file(fsd, pcb); 640 | break; 641 | default: 642 | break; 643 | } 644 | 645 | return ERR_OK; 646 | } 647 | 648 | static int open_dataconnection(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 649 | { 650 | if (fsm->passive) 651 | return 0; 652 | 653 | /* Allocate memory for the structure that holds the state of the 654 | connection. */ 655 | fsm->datafs = malloc(sizeof(struct ftpd_datastate)); 656 | 657 | if (fsm->datafs == NULL) { 658 | LWIP_DEBUGF(FTPD_DEBUG, ("open_dataconnection: Out of memory\n")); 659 | send_msg(pcb, fsm, msg451); 660 | return 1; 661 | } 662 | memset(fsm->datafs, 0, sizeof(struct ftpd_datastate)); 663 | fsm->datafs->msgfs = fsm; 664 | fsm->datafs->msgpcb = pcb; 665 | 666 | if (sfifo_init(&fsm->datafs->fifo, 2000) != 0) { 667 | free(fsm->datafs); 668 | send_msg(pcb, fsm, msg451); 669 | return 1; 670 | } 671 | 672 | fsm->datapcb = tcp_new(); 673 | 674 | if (fsm->datapcb == NULL) { 675 | sfifo_close(&fsm->datafs->fifo); 676 | free(fsm->datafs); 677 | send_msg(pcb, fsm, msg451); 678 | return 1; 679 | } 680 | 681 | /* Tell TCP that this is the structure we wish to be passed for our 682 | callbacks. */ 683 | tcp_arg(fsm->datapcb, fsm->datafs); 684 | ip_addr_t dataip; 685 | IP_SET_TYPE_VAL(dataip, IPADDR_TYPE_V4); 686 | ip4_addr_copy(*ip_2_ip4(&dataip), fsm->dataip); 687 | tcp_connect(fsm->datapcb, &dataip, fsm->dataport, ftpd_dataconnected); 688 | 689 | return 0; 690 | } 691 | 692 | static void cmd_user(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 693 | { 694 | (void) arg; /* suppress unused warning */ 695 | 696 | send_msg(pcb, fsm, msg331); 697 | fsm->state = FTPD_PASS; 698 | /* 699 | send_msg(pcb, fs, msgLoginFailed); 700 | fs->state = FTPD_QUIT; 701 | */ 702 | } 703 | 704 | static void cmd_pass(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 705 | { 706 | (void) arg; /* suppress unused warning */ 707 | send_msg(pcb, fsm, msg230); 708 | fsm->state = FTPD_IDLE; 709 | /* 710 | send_msg(pcb, fs, msgLoginFailed); 711 | fs->state = FTPD_QUIT; 712 | */ 713 | } 714 | 715 | static void cmd_port(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 716 | { 717 | int nr; 718 | unsigned pHi, pLo; 719 | unsigned ip[4]; 720 | 721 | nr = sscanf(arg, "%u,%u,%u,%u,%u,%u", &(ip[0]), &(ip[1]), &(ip[2]), &(ip[3]), &pHi, &pLo); 722 | if (nr != 6) { 723 | send_msg(pcb, fsm, msg501); 724 | } else { 725 | IP4_ADDR(&fsm->dataip, (u8_t) ip[0], (u8_t) ip[1], (u8_t) ip[2], (u8_t) ip[3]); 726 | fsm->dataport = ((u16_t) pHi << 8) | (u16_t) pLo; 727 | send_msg(pcb, fsm, msg200); 728 | } 729 | } 730 | 731 | static void cmd_quit(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 732 | { 733 | (void) arg; /* suppress unused warning */ 734 | send_msg(pcb, fsm, msg221); 735 | fsm->state = FTPD_QUIT; 736 | } 737 | 738 | static void cmd_cwd(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 739 | { 740 | if (!vfs_chdir(fsm->vfs, arg)) { 741 | send_msg(pcb, fsm, msg250); 742 | } else { 743 | send_msg(pcb, fsm, msg550); 744 | } 745 | } 746 | 747 | static void cmd_cdup(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 748 | { 749 | (void) arg; /* suppress unused warning */ 750 | if (!vfs_chdir(fsm->vfs, "..")) { 751 | send_msg(pcb, fsm, msg250); 752 | } else { 753 | send_msg(pcb, fsm, msg550); 754 | } 755 | } 756 | 757 | static void cmd_pwd(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 758 | { 759 | char *path; 760 | (void) arg; /* suppress unused warning */ 761 | 762 | if ((path = vfs_getcwd(fsm->vfs, NULL, 0))) { 763 | send_msg(pcb, fsm, msg257PWD, path); 764 | free(path); 765 | } 766 | } 767 | 768 | static void cmd_list_common(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm, int shortlist) 769 | { 770 | vfs_dir_t *vfs_dir; 771 | char *cwd; 772 | (void) arg; /* suppress unused warning */ 773 | 774 | cwd = vfs_getcwd(fsm->vfs, NULL, 0); 775 | if ((!cwd)) { 776 | send_msg(pcb, fsm, msg451); 777 | return; 778 | } 779 | vfs_dir = vfs_opendir(fsm->vfs, cwd); 780 | free(cwd); 781 | if (!vfs_dir) { 782 | send_msg(pcb, fsm, msg451); 783 | return; 784 | } 785 | 786 | if (open_dataconnection(pcb, fsm) != 0) { 787 | vfs_closedir(vfs_dir); 788 | return; 789 | } 790 | 791 | fsm->datafs->vfs_dir = vfs_dir; 792 | fsm->datafs->vfs_dirent = NULL; 793 | if (shortlist != 0) 794 | fsm->state = FTPD_NLST; 795 | else 796 | fsm->state = FTPD_LIST; 797 | 798 | send_msg(pcb, fsm, msg150); 799 | } 800 | 801 | static void cmd_nlst(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 802 | { 803 | cmd_list_common(arg, pcb, fsm, 1); 804 | } 805 | 806 | static void cmd_list(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 807 | { 808 | cmd_list_common(arg, pcb, fsm, 0); 809 | } 810 | 811 | static void cmd_retr(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 812 | { 813 | vfs_file_t *vfs_file; 814 | vfs_stat_t st; 815 | 816 | vfs_stat(fsm->vfs, arg, &st); 817 | if (!VFS_ISREG(st.st_mode)) { 818 | send_msg(pcb, fsm, msg550); 819 | return; 820 | } 821 | vfs_file = vfs_open(fsm->vfs, arg, "rb"); 822 | if (!vfs_file) { 823 | send_msg(pcb, fsm, msg550); 824 | return; 825 | } 826 | 827 | send_msg(pcb, fsm, msg150recv, arg, st.st_size); 828 | 829 | if (open_dataconnection(pcb, fsm) != 0) { 830 | vfs_close(vfs_file); 831 | return; 832 | } 833 | 834 | fsm->datafs->vfs_file = vfs_file; 835 | fsm->state = FTPD_RETR; 836 | } 837 | 838 | static void cmd_stor(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 839 | { 840 | vfs_file_t *vfs_file; 841 | 842 | vfs_file = vfs_open(fsm->vfs, arg, "wb"); 843 | if (!vfs_file) { 844 | send_msg(pcb, fsm, msg550); 845 | return; 846 | } 847 | 848 | send_msg(pcb, fsm, msg150stor, arg); 849 | 850 | if (open_dataconnection(pcb, fsm) != 0) { 851 | vfs_close(vfs_file); 852 | return; 853 | } 854 | 855 | fsm->datafs->vfs_file = vfs_file; 856 | fsm->state = FTPD_STOR; 857 | } 858 | 859 | static void cmd_noop(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 860 | { 861 | (void) arg; /* suppress unused warning */ 862 | send_msg(pcb, fsm, msg200); 863 | } 864 | 865 | static void cmd_syst(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 866 | { 867 | (void) arg; /* suppress unused warning */ 868 | send_msg(pcb, fsm, msg214SYST, "UNIX"); 869 | } 870 | 871 | static void cmd_pasv(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 872 | { 873 | static u16_t port = 4096; 874 | static u16_t start_port = 4096; 875 | struct tcp_pcb *temppcb; 876 | (void) arg; /* suppress unused warning */ 877 | 878 | /* Allocate memory for the structure that holds the state of the 879 | connection. */ 880 | fsm->datafs = malloc(sizeof(struct ftpd_datastate)); 881 | 882 | if (fsm->datafs == NULL) { 883 | LWIP_DEBUGF(FTPD_DEBUG, ("cmd_pasv: Out of memory\n")); 884 | send_msg(pcb, fsm, msg451); 885 | return; 886 | } 887 | memset(fsm->datafs, 0, sizeof(struct ftpd_datastate)); 888 | 889 | if (sfifo_init(&fsm->datafs->fifo, 2000) != 0) { 890 | free(fsm->datafs); 891 | send_msg(pcb, fsm, msg451); 892 | return; 893 | } 894 | 895 | fsm->datalistenpcb = tcp_new(); 896 | 897 | if (fsm->datalistenpcb == NULL) { 898 | free(fsm->datafs); 899 | sfifo_close(&fsm->datafs->fifo); 900 | send_msg(pcb, fsm, msg451); 901 | return; 902 | } 903 | 904 | start_port = port; 905 | 906 | while (1) { 907 | err_t err; 908 | 909 | if(++port > 0x7fff) 910 | port = 4096; 911 | 912 | fsm->dataport = port; 913 | err = tcp_bind(fsm->datalistenpcb, (ip_addr_t*)&pcb->local_ip, fsm->dataport); 914 | if (err == ERR_OK) 915 | break; 916 | if (start_port == port) 917 | err = ERR_CLSD; 918 | if (err == ERR_USE) { 919 | continue; 920 | } else { 921 | ftpd_dataclose(fsm->datalistenpcb, fsm->datafs); 922 | fsm->datalistenpcb = NULL; 923 | fsm->datafs = NULL; 924 | return; 925 | } 926 | } 927 | 928 | temppcb = tcp_listen(fsm->datalistenpcb); 929 | if (!temppcb) { 930 | LWIP_DEBUGF(FTPD_DEBUG, ("cmd_pasv: tcp_listen failed\n")); 931 | ftpd_dataclose(fsm->datalistenpcb, fsm->datafs); 932 | fsm->datalistenpcb = NULL; 933 | fsm->datafs = NULL; 934 | return; 935 | } 936 | fsm->datalistenpcb = temppcb; 937 | 938 | fsm->passive = 1; 939 | fsm->datafs->connected = 0; 940 | fsm->datafs->msgfs = fsm; 941 | fsm->datafs->msgpcb = pcb; 942 | 943 | /* Tell TCP that this is the structure we wish to be passed for our 944 | callbacks. */ 945 | tcp_arg(fsm->datalistenpcb, fsm->datafs); 946 | tcp_accept(fsm->datalistenpcb, ftpd_dataaccept); 947 | send_msg(pcb, fsm, msg227, ip4_addr1(ip_2_ip4(&pcb->local_ip)), ip4_addr2(ip_2_ip4(&pcb->local_ip)), ip4_addr3(ip_2_ip4(&pcb->local_ip)), ip4_addr4(ip_2_ip4(&pcb->local_ip)), (fsm->dataport >> 8) & 0xff, (fsm->dataport) & 0xff); 948 | } 949 | 950 | static void cmd_abrt(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 951 | { 952 | (void) arg; /* suppress unused warning */ 953 | if (fsm->datafs != NULL) { 954 | tcp_arg(fsm->datapcb, NULL); 955 | tcp_sent(fsm->datapcb, NULL); 956 | tcp_recv(fsm->datapcb, NULL); 957 | tcp_abort(pcb); 958 | sfifo_close(&fsm->datafs->fifo); 959 | free(fsm->datafs); 960 | fsm->datafs = NULL; 961 | } 962 | fsm->state = FTPD_IDLE; 963 | } 964 | 965 | static void cmd_type(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 966 | { 967 | LWIP_DEBUGF(FTPD_DEBUG, ("Got TYPE -%s-\n", arg)); 968 | 969 | if(strcmp(arg, "I") != 0) { 970 | send_msg(pcb, fsm, msg502); 971 | return; 972 | } 973 | 974 | send_msg(pcb, fsm, msg200); 975 | } 976 | 977 | static void cmd_mode(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 978 | { 979 | LWIP_DEBUGF(FTPD_DEBUG, ("Got MODE -%s-\n", arg)); 980 | send_msg(pcb, fsm, msg502); 981 | } 982 | 983 | static void cmd_rnfr(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 984 | { 985 | if (arg == NULL) { 986 | send_msg(pcb, fsm, msg501); 987 | return; 988 | } 989 | if (*arg == '\0') { 990 | send_msg(pcb, fsm, msg501); 991 | return; 992 | } 993 | if (fsm->renamefrom) 994 | free(fsm->renamefrom); 995 | fsm->renamefrom = malloc(strlen(arg) + 1); 996 | if (fsm->renamefrom == NULL) { 997 | LWIP_DEBUGF(FTPD_DEBUG, ("cmd_rnfr: Out of memory\n")); 998 | send_msg(pcb, fsm, msg451); 999 | return; 1000 | } 1001 | strcpy(fsm->renamefrom, arg); 1002 | fsm->state = FTPD_RNFR; 1003 | send_msg(pcb, fsm, msg350); 1004 | } 1005 | 1006 | static void cmd_rnto(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 1007 | { 1008 | if (fsm->state != FTPD_RNFR) { 1009 | send_msg(pcb, fsm, msg503); 1010 | return; 1011 | } 1012 | fsm->state = FTPD_IDLE; 1013 | if (arg == NULL) { 1014 | send_msg(pcb, fsm, msg501); 1015 | return; 1016 | } 1017 | if (*arg == '\0') { 1018 | send_msg(pcb, fsm, msg501); 1019 | return; 1020 | } 1021 | if (vfs_rename(fsm->vfs, fsm->renamefrom, arg)) { 1022 | send_msg(pcb, fsm, msg450); 1023 | } else { 1024 | send_msg(pcb, fsm, msg250); 1025 | } 1026 | } 1027 | 1028 | static void cmd_mkd(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 1029 | { 1030 | if (arg == NULL) { 1031 | send_msg(pcb, fsm, msg501); 1032 | return; 1033 | } 1034 | if (*arg == '\0') { 1035 | send_msg(pcb, fsm, msg501); 1036 | return; 1037 | } 1038 | if (vfs_mkdir(fsm->vfs, arg, VFS_IRWXU | VFS_IRWXG | VFS_IRWXO) != 0) { 1039 | send_msg(pcb, fsm, msg550); 1040 | } else { 1041 | send_msg(pcb, fsm, msg257, arg); 1042 | } 1043 | } 1044 | 1045 | static void cmd_rmd(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 1046 | { 1047 | vfs_stat_t st; 1048 | 1049 | if (arg == NULL) { 1050 | send_msg(pcb, fsm, msg501); 1051 | return; 1052 | } 1053 | if (*arg == '\0') { 1054 | send_msg(pcb, fsm, msg501); 1055 | return; 1056 | } 1057 | if (vfs_stat(fsm->vfs, arg, &st) != 0) { 1058 | send_msg(pcb, fsm, msg550); 1059 | return; 1060 | } 1061 | if (!VFS_ISDIR(st.st_mode)) { 1062 | send_msg(pcb, fsm, msg550); 1063 | return; 1064 | } 1065 | if (vfs_rmdir(fsm->vfs, arg) != 0) { 1066 | send_msg(pcb, fsm, msg550); 1067 | } else { 1068 | send_msg(pcb, fsm, msg250); 1069 | } 1070 | } 1071 | 1072 | static void cmd_dele(const char *arg, struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 1073 | { 1074 | vfs_stat_t st; 1075 | 1076 | if (arg == NULL) { 1077 | send_msg(pcb, fsm, msg501); 1078 | return; 1079 | } 1080 | if (*arg == '\0') { 1081 | send_msg(pcb, fsm, msg501); 1082 | return; 1083 | } 1084 | if (vfs_stat(fsm->vfs, arg, &st) != 0) { 1085 | send_msg(pcb, fsm, msg550); 1086 | return; 1087 | } 1088 | if (!VFS_ISREG(st.st_mode)) { 1089 | send_msg(pcb, fsm, msg550); 1090 | return; 1091 | } 1092 | if (vfs_remove(fsm->vfs, arg) != 0) { 1093 | send_msg(pcb, fsm, msg550); 1094 | } else { 1095 | send_msg(pcb, fsm, msg250); 1096 | } 1097 | } 1098 | 1099 | struct ftpd_command { 1100 | char *cmd; 1101 | void (*func) (const char *arg, struct tcp_pcb * pcb, struct ftpd_msgstate * fsm); 1102 | }; 1103 | 1104 | static struct ftpd_command ftpd_commands[] = { 1105 | {"USER", cmd_user}, 1106 | {"PASS", cmd_pass}, 1107 | {"PORT", cmd_port}, 1108 | {"QUIT", cmd_quit}, 1109 | {"CWD", cmd_cwd}, 1110 | {"CDUP", cmd_cdup}, 1111 | {"PWD", cmd_pwd}, 1112 | {"XPWD", cmd_pwd}, 1113 | {"NLST", cmd_nlst}, 1114 | {"LIST", cmd_list}, 1115 | {"RETR", cmd_retr}, 1116 | {"STOR", cmd_stor}, 1117 | {"NOOP", cmd_noop}, 1118 | {"SYST", cmd_syst}, 1119 | {"ABOR", cmd_abrt}, 1120 | {"TYPE", cmd_type}, 1121 | {"MODE", cmd_mode}, 1122 | {"RNFR", cmd_rnfr}, 1123 | {"RNTO", cmd_rnto}, 1124 | {"MKD", cmd_mkd}, 1125 | {"XMKD", cmd_mkd}, 1126 | {"RMD", cmd_rmd}, 1127 | {"XRMD", cmd_rmd}, 1128 | {"DELE", cmd_dele}, 1129 | {"PASV", cmd_pasv}, 1130 | {NULL, NULL} 1131 | }; 1132 | 1133 | static void send_msgdata(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 1134 | { 1135 | err_t err; 1136 | u16_t len; 1137 | 1138 | if (sfifo_used(&fsm->fifo) > 0) { 1139 | int i; 1140 | 1141 | /* We cannot send more data than space available in the send 1142 | buffer. */ 1143 | if (tcp_sndbuf(pcb) < sfifo_used(&fsm->fifo)) { 1144 | len = tcp_sndbuf(pcb); 1145 | } else { 1146 | len = (u16_t) sfifo_used(&fsm->fifo); 1147 | } 1148 | 1149 | i = fsm->fifo.readpos; 1150 | if ((i + len) > fsm->fifo.size) { 1151 | err = tcp_write(pcb, fsm->fifo.buffer + i, (u16_t)(fsm->fifo.size - i), 1); 1152 | if (err != ERR_OK) { 1153 | LWIP_DEBUGF(FTPD_DEBUG, ("send_msgdata: error writing!\n")); 1154 | return; 1155 | } 1156 | len -= fsm->fifo.size - i; 1157 | fsm->fifo.readpos = 0; 1158 | i = 0; 1159 | } 1160 | 1161 | err = tcp_write(pcb, fsm->fifo.buffer + i, len, 1); 1162 | if (err != ERR_OK) { 1163 | LWIP_DEBUGF(FTPD_DEBUG, ("send_msgdata: error writing!\n")); 1164 | return; 1165 | } 1166 | fsm->fifo.readpos += len; 1167 | } 1168 | } 1169 | 1170 | static void send_msg(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm, char *msg, ...) 1171 | { 1172 | va_list arg; 1173 | char buffer[1024]; 1174 | int len; 1175 | 1176 | va_start(arg, msg); 1177 | vsprintf(buffer, msg, arg); 1178 | va_end(arg); 1179 | strcat(buffer, "\r\n"); 1180 | len = strlen(buffer); 1181 | if (sfifo_space(&fsm->fifo) < len) 1182 | return; 1183 | sfifo_write(&fsm->fifo, buffer, len); 1184 | LWIP_DEBUGF(FTPD_DEBUG, ("response: %s", buffer)); 1185 | send_msgdata(pcb, fsm); 1186 | } 1187 | 1188 | static void ftpd_msgerr(void *arg, err_t err) 1189 | { 1190 | struct ftpd_msgstate *fsm = arg; 1191 | 1192 | LWIP_DEBUGF(FTPD_DEBUG, ("ftpd_msgerr: %s (%i)\n", lwip_strerr(err), err)); 1193 | if (fsm == NULL) 1194 | return; 1195 | if (fsm->datafs) 1196 | ftpd_dataclose(fsm->datapcb, fsm->datafs); 1197 | sfifo_close(&fsm->fifo); 1198 | vfs_close(fsm->vfs); 1199 | fsm->vfs = NULL; 1200 | if (fsm->renamefrom) 1201 | free(fsm->renamefrom); 1202 | fsm->renamefrom = NULL; 1203 | free(fsm); 1204 | } 1205 | 1206 | static void ftpd_msgclose(struct tcp_pcb *pcb, struct ftpd_msgstate *fsm) 1207 | { 1208 | tcp_arg(pcb, NULL); 1209 | tcp_sent(pcb, NULL); 1210 | tcp_recv(pcb, NULL); 1211 | if (fsm->datafs) 1212 | ftpd_dataclose(fsm->datapcb, fsm->datafs); 1213 | sfifo_close(&fsm->fifo); 1214 | vfs_close(fsm->vfs); 1215 | fsm->vfs = NULL; 1216 | if (fsm->renamefrom) 1217 | free(fsm->renamefrom); 1218 | fsm->renamefrom = NULL; 1219 | free(fsm); 1220 | tcp_arg(pcb, NULL); 1221 | tcp_close(pcb); 1222 | } 1223 | 1224 | static err_t ftpd_msgsent(void *arg, struct tcp_pcb *pcb, u16_t len) 1225 | { 1226 | struct ftpd_msgstate *fsm = arg; 1227 | (void) len; /* suppress unused warning */ 1228 | 1229 | if ((sfifo_used(&fsm->fifo) == 0) && (fsm->state == FTPD_QUIT)) { 1230 | ftpd_msgclose(pcb, fsm); 1231 | return ERR_OK; 1232 | } 1233 | 1234 | if (pcb->state <= ESTABLISHED) send_msgdata(pcb, fsm); 1235 | return ERR_OK; 1236 | } 1237 | 1238 | static err_t ftpd_msgrecv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) 1239 | { 1240 | char *text; 1241 | struct ftpd_msgstate *fsm = arg; 1242 | 1243 | if (err == ERR_OK && p != NULL) { 1244 | 1245 | /* Inform TCP that we have taken the data. */ 1246 | tcp_recved(pcb, p->tot_len); 1247 | 1248 | text = malloc(p->tot_len + 1); 1249 | if (text) { 1250 | char cmd[5]; 1251 | struct pbuf *q; 1252 | char *pt = text; 1253 | struct ftpd_command *ftpd_cmd; 1254 | 1255 | for (q = p; q != NULL; q = q->next) { 1256 | bcopy(q->payload, pt, q->len); 1257 | pt += q->len; 1258 | } 1259 | *pt = '\0'; 1260 | 1261 | pt = &text[strlen(text) - 1]; 1262 | while (((*pt == '\r') || (*pt == '\n')) && pt >= text) 1263 | *pt-- = '\0'; 1264 | 1265 | LWIP_DEBUGF(FTPD_DEBUG, ("query: %s\n", text)); 1266 | 1267 | strncpy(cmd, text, 4); 1268 | for (pt = cmd; isalpha(*pt) && pt < &cmd[4]; pt++) 1269 | *pt = toupper(*pt); 1270 | *pt = '\0'; 1271 | 1272 | for (ftpd_cmd = ftpd_commands; ftpd_cmd->cmd != NULL; ftpd_cmd++) { 1273 | if (!strcmp(ftpd_cmd->cmd, cmd)) 1274 | break; 1275 | } 1276 | 1277 | if (strlen(text) < (strlen(cmd) + 1)) 1278 | pt = ""; 1279 | else 1280 | pt = &text[strlen(cmd) + 1]; 1281 | 1282 | if (ftpd_cmd->func) 1283 | ftpd_cmd->func(pt, pcb, fsm); 1284 | else 1285 | send_msg(pcb, fsm, msg502); 1286 | 1287 | free(text); 1288 | } 1289 | pbuf_free(p); 1290 | } else if (err == ERR_OK && p == NULL) { 1291 | ftpd_msgclose(pcb, fsm); 1292 | } 1293 | 1294 | return ERR_OK; 1295 | } 1296 | 1297 | static err_t ftpd_msgpoll(void *arg, struct tcp_pcb *pcb) 1298 | { 1299 | struct ftpd_msgstate *fsm = arg; 1300 | (void) pcb; /* suppress unused warning */ 1301 | 1302 | if (fsm == NULL) 1303 | return ERR_OK; 1304 | 1305 | if (fsm->datafs) { 1306 | if (fsm->datafs->connected) { 1307 | switch (fsm->state) { 1308 | case FTPD_LIST: 1309 | send_next_directory(fsm->datafs, fsm->datapcb, 0); 1310 | break; 1311 | case FTPD_NLST: 1312 | send_next_directory(fsm->datafs, fsm->datapcb, 1); 1313 | break; 1314 | case FTPD_RETR: 1315 | send_file(fsm->datafs, fsm->datapcb); 1316 | break; 1317 | default: 1318 | break; 1319 | } 1320 | } 1321 | } 1322 | 1323 | return ERR_OK; 1324 | } 1325 | 1326 | static err_t ftpd_msgaccept(void *arg, struct tcp_pcb *pcb, err_t err) 1327 | { 1328 | LWIP_PLATFORM_DIAG(("ftpd_msgaccept called")); 1329 | struct ftpd_msgstate *fsm; 1330 | (void) err; /* suppress unused warning */ 1331 | (void) arg; 1332 | 1333 | /* Allocate memory for the structure that holds the state of the 1334 | connection. */ 1335 | fsm = malloc(sizeof(struct ftpd_msgstate)); 1336 | 1337 | if (fsm == NULL) { 1338 | LWIP_DEBUGF(FTPD_DEBUG, ("ftpd_msgaccept: Out of memory\n")); 1339 | return ERR_MEM; 1340 | } 1341 | memset(fsm, 0, sizeof(struct ftpd_msgstate)); 1342 | 1343 | /* Initialize the structure. */ 1344 | if (sfifo_init(&fsm->fifo, 2000) != 0) { 1345 | free(fsm); 1346 | return ERR_MEM; 1347 | } 1348 | fsm->state = FTPD_IDLE; 1349 | fsm->vfs = vfs_openfs(); 1350 | if (fsm->vfs == NULL) { 1351 | sfifo_close(&fsm->fifo); 1352 | free(fsm); 1353 | return ERR_CLSD; 1354 | } 1355 | 1356 | /* Tell TCP that this is the structure we wish to be passed for our 1357 | callbacks. */ 1358 | tcp_arg(pcb, fsm); 1359 | 1360 | /* Tell TCP that we wish to be informed of incoming data by a call 1361 | to the http_recv() function. */ 1362 | tcp_recv(pcb, ftpd_msgrecv); 1363 | 1364 | /* Tell TCP that we wish be to informed of data that has been 1365 | successfully sent by a call to the ftpd_sent() function. */ 1366 | tcp_sent(pcb, ftpd_msgsent); 1367 | 1368 | tcp_err(pcb, ftpd_msgerr); 1369 | 1370 | tcp_poll(pcb, ftpd_msgpoll, 1); 1371 | 1372 | send_msg(pcb, fsm, msg220); 1373 | 1374 | return ERR_OK; 1375 | } 1376 | 1377 | void ftpd_init(void) 1378 | { 1379 | struct tcp_pcb *pcb; 1380 | 1381 | vfs_load_plugin(vfs_default_fs); 1382 | 1383 | pcb = tcp_new(); 1384 | LWIP_DEBUGF(FTPD_DEBUG, ("ftpd_init: pcb: %lx\n", (unsigned long) pcb)); 1385 | int r = tcp_bind(pcb, IP_ADDR_ANY, 21); 1386 | LWIP_DEBUGF(FTPD_DEBUG, ("ftpd_init: tcp_bind: %d\n", r)); 1387 | pcb = tcp_listen(pcb); 1388 | LWIP_DEBUGF(FTPD_DEBUG, ("ftpd_init: listen-pcb: %lx\n", (unsigned long) pcb)); 1389 | tcp_accept(pcb, ftpd_msgaccept); 1390 | } 1391 | --------------------------------------------------------------------------------