diff --git a/README.md b/README.md index 5c2275f..43827c3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -FTP server for lwip and tinyfatfs -================================= +# FTP server for lwip and tinyfatfs This repository contains the ftp-server for lwip Copyright (c) 2002 Florian Schulze and an API translation layer Copyright (c) 2013 Philipp Tölke. @@ -18,3 +17,11 @@ The translation-layer All code in this repository is licensed under a 3-clause BSD license Patches, comments and pull-requests are welcome. + +## To build a docker container for testing + +``` +docker build -f test-in-docker\Dockerfile -t lwip-ftpd-test . +``` + +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. diff --git a/test-in-docker/Dockerfile b/test-in-docker/Dockerfile new file mode 100644 index 0000000..782a1d7 --- /dev/null +++ b/test-in-docker/Dockerfile @@ -0,0 +1,31 @@ +FROM debian:10 AS builder + +RUN apt update && apt install -y unzip libpcap-dev build-essential git cmake && rm -rf /var/lib/dpkg/lists/* + +WORKDIR /src + +RUN git clone https://git.savannah.nongnu.org/git/lwip.git + +RUN git clone https://github.com/toelke/lwip-ftpd.git + +COPY . lwip-ftpd + +ADD http://elm-chan.org/fsw/ff/arc/ff14.zip . + +RUN unzip -q ff14.zip -d ff + +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 + +WORKDIR /build + +RUN cmake /src/lwip-ftpd/test-in-docker/src && make -j + +FROM debian:10 AS runner + +RUN apt update && apt install -y libpcap0.8 valgrind && rm -rf /var/lib/dpkg/lists/* + +WORKDIR /app + +COPY --from=builder /build/lwip-runner . + +CMD valgrind /app/lwip-runner diff --git a/test-in-docker/lwip-include/arch/bpstruct.h b/test-in-docker/lwip-include/arch/bpstruct.h new file mode 100644 index 0000000..1d81e3f --- /dev/null +++ b/test-in-docker/lwip-include/arch/bpstruct.h @@ -0,0 +1 @@ +#pragma pack(push,1) diff --git a/test-in-docker/lwip-include/arch/cc.h b/test-in-docker/lwip-include/arch/cc.h new file mode 100644 index 0000000..da5703f --- /dev/null +++ b/test-in-docker/lwip-include/arch/cc.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * + */ +#ifndef LWIP_ARCH_CC_H +#define LWIP_ARCH_CC_H + +#include /* printf, fflush, FILE */ +#include /* abort */ +#include +#include + +#ifdef _MSC_VER +#pragma warning (disable: 4127) /* conditional expression is constant */ +#pragma warning (disable: 4996) /* 'strncpy' was declared deprecated */ +#pragma warning (disable: 4103) /* structure packing changed by including file */ +#pragma warning (disable: 4820) /* 'x' bytes padding added after data member 'y' */ +#endif + +//#define LWIP_PROVIDE_ERRNO + +/* Define platform endianness (might already be defined) */ +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif /* BYTE_ORDER */ + +/* Define generic types used in lwIP */ +typedef uint8_t u8_t; +typedef int8_t s8_t; +typedef uint16_t u16_t; +typedef int16_t s16_t; +typedef uint32_t u32_t; +typedef int32_t s32_t; + +typedef size_t mem_ptr_t; +typedef u32_t sys_prot_t; + +/* Define (sn)printf formatters for these lwIP types */ +#define X8_F "02x" +#define U16_F "hu" +#define U32_F "lu" +#define S32_F "ld" +#define X32_F "lx" + +#ifdef __GNUC__ +#define S16_F "d" +#define X16_F "uX" +#define SZT_F "u" +#else +#define S16_F "hd" +#define X16_F "hx" +#define SZT_F "lu" +#endif + +/* Compiler hints for packing structures */ +#define PACK_STRUCT_STRUCT +#define PACK_STRUCT_USE_INCLUDES + +/* Plaform specific diagnostic output */ +#define LWIP_PLATFORM_DIAG(x) do { printf x; } while(0) + +#define LWIP_PLATFORM_ASSERT(x) do { printf("Assertion \"%s\" failed at line %d in %s\n", \ + x, __LINE__, __FILE__); fflush(NULL); abort(); } while(0) + +#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \ + printf("Assertion \"%s\" failed at line %d in %s\n", message, __LINE__, __FILE__); \ + fflush(NULL);handler;} } while(0) + +#ifdef _MSC_VER +/* C runtime functions redefined */ +#define snprintf _snprintf +#endif + +//u32_t dns_lookup_external_hosts_file(const char *name); + +#define LWIP_RAND() ((u32_t)rand()) + +#endif /* LWIP_ARCH_CC_H */ + diff --git a/test-in-docker/lwip-include/arch/epstruct.h b/test-in-docker/lwip-include/arch/epstruct.h new file mode 100644 index 0000000..65898b5 --- /dev/null +++ b/test-in-docker/lwip-include/arch/epstruct.h @@ -0,0 +1 @@ +#pragma pack(pop) diff --git a/test-in-docker/lwip-include/arch/sys_arch.h b/test-in-docker/lwip-include/arch/sys_arch.h new file mode 100644 index 0000000..b8f86d0 --- /dev/null +++ b/test-in-docker/lwip-include/arch/sys_arch.h @@ -0,0 +1,4 @@ +#ifndef LWIP_ARCH_SYS_ARCH_H +#define LWIP_ARCH_SYS_ARCH_H + +#endif /* LWIP_ARCH_SYS_ARCH_H */ diff --git a/test-in-docker/lwip-include/lwipopts.h b/test-in-docker/lwip-include/lwipopts.h new file mode 100644 index 0000000..959f61d --- /dev/null +++ b/test-in-docker/lwip-include/lwipopts.h @@ -0,0 +1,31 @@ +#define NO_SYS 1 +#define SYS_LIGHTWEIGHT_PROT 0 + +#define MEM_LIBC_MALLOC 1 +#define MEMP_MEM_MALLOC 1 +#define MEM_USE_POOLS 0 +#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 1 + +#define LWIP_ETHERNET 1 +#define LWIP_IPV4 1 +#define LWIP_TCP 1 +#define LWIP_ARP 1 +#define LWIP_ICMP 1 +#define IP_FRAG 1 + +#define LWIP_DEBUG 1 +#define FTPD_DEBUG LWIP_DBG_ON +//#define IP4_DEBUG LWIP_DBG_ON +//#define NETIF_DEBUG LWIP_DBG_ON +//#define TCP_DEBUG LWIP_DBG_ON +//#define ETHARP_DEBUG LWIP_DBG_ON + +#define PPP_SUPPORT 0 +#define LWIP_SOCKET 0 +#define LWIP_NETCONN 0 +#define LWIP_RAW 0 +#define LWIP_COMPAT_SOCKETS 0 +#define LWIP_STATS 0 + +#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 + diff --git a/test-in-docker/lwip-include/sys_arch.c b/test-in-docker/lwip-include/sys_arch.c new file mode 100644 index 0000000..71f08ae --- /dev/null +++ b/test-in-docker/lwip-include/sys_arch.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2001-2003 Swedish Institute of Computer Science. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * This file is part of the lwIP TCP/IP stack. + * + * Author: Adam Dunkels + * Simon Goldschmidt + * + */ + +#include +#include + +#ifdef _WIN32 + +#include + +LARGE_INTEGER freq, sys_start_time; + +static void sys_init_timing(void) +{ + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&sys_start_time); +} + +static LONGLONG sys_get_ms_longlong(void) +{ + LONGLONG ret; + LARGE_INTEGER now; +#if NO_SYS + if (freq.QuadPart == 0) { + sys_init_timing(); + } +#endif /* NO_SYS */ + QueryPerformanceCounter(&now); + ret = now.QuadPart-sys_start_time.QuadPart; + return (u32_t)(((ret)*1000)/freq.QuadPart); +} + +u32_t sys_jiffies(void) +{ + return (u32_t)sys_get_ms_longlong(); +} + +u32_t sys_now(void) +{ + return (u32_t)sys_get_ms_longlong(); +} + +#else + +#include + +u32_t sys_now(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000L + ts.tv_nsec / 1000000L; +} + +u32_t sys_jiffies(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000000000L + ts.tv_nsec; +} + +#endif + +void sys_init(void) +{ +#ifdef _WIN32 + sys_init_timing(); +#endif +} diff --git a/test-in-docker/src/CMakeLists.txt b/test-in-docker/src/CMakeLists.txt new file mode 100644 index 0000000..e6dba85 --- /dev/null +++ b/test-in-docker/src/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required (VERSION 2.8) + +project(lwip-runner) + +find_path( PCAP_INCLUDE_DIR NAMES pcap/pcap.h pcap.h HINTS "${PCAP_HINTS}/include") + +find_library( PCAP_LIBRARY NAMES pcap wpcap HINTS "${PCAP_HINTS}/lib") + +find_path(LWIP_DIR src/include/lwip/init.h ${CMAKE_CURRENT_SOURCE_DIR}/../../../lwip) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../lwip-include + ${PCAP_INCLUDE_DIR} + ${LWIP_DIR}/src/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../../ff + ${CMAKE_CURRENT_SOURCE_DIR}/../.. +) + +include(${LWIP_DIR}/src/Filelists.cmake) + +add_executable(lwip-runner + ${CMAKE_CURRENT_SOURCE_DIR}/main.c + ${CMAKE_CURRENT_SOURCE_DIR}/../lwip-include/sys_arch.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../ftpd.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../vfs.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../ff/source/ff.c +) +target_link_libraries(lwip-runner ${PCAP_LIBRARY} lwipcore) diff --git a/test-in-docker/src/main.c b/test-in-docker/src/main.c new file mode 100644 index 0000000..8ddb474 --- /dev/null +++ b/test-in-docker/src/main.c @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +// 4MiB should be enough for everyone +#define DATA_SIZE (4*1024*1024) + +int data; + +DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void *buff) { + if (pdrv != 0) return RES_NOTRDY; + + switch(cmd) { + case CTRL_SYNC: + fsync(data); + return RES_OK; + case GET_SECTOR_COUNT: + *((LBA_t*)buff) = DATA_SIZE/512; + return RES_OK; + case GET_BLOCK_SIZE: + *((WORD*)buff) = 512; + return RES_OK; + } + return RES_PARERR; +} + +DWORD get_fattime() { + // I am lazy. + return 0; +} + +DSTATUS disk_initialize(BYTE pdrv) { + if (pdrv != 0) return STA_NOINIT; + + data = open("data.img", O_CREAT | O_RDWR); + printf("disk_initialize: open returns %d\n", data); + + return 0; +} + +DSTATUS disk_status (BYTE pdrv) { + if (pdrv != 0) return STA_NOINIT; + return 0; +} + +DRESULT disk_write(BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count) { + if (pdrv != 0) return RES_NOTRDY; + + size_t r = lseek(data, 512*sector, SEEK_SET); + printf("disk_write: lseek returns %d\n", r); + + r = write(data, buff, 512 * count); + printf("disk_write(%d): write returns %d\n", count, r); + + return RES_OK; +} + +DRESULT disk_read(BYTE pdrv, BYTE* buff, LBA_t sector, UINT count) { + if (pdrv != 0) return RES_NOTRDY; + + size_t r = lseek(data, 512*sector, SEEK_SET); + printf("disk_read: lseek returns %d\n", r); + + r = read(data, buff, 512 * count); + printf("disk_read(%d): read returns %d\n", count, r); + + return RES_OK; +} + +int dbg_printf(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + int r = vprintf(fmt, args); + va_end(args); + return r; +} + +static err_t pcap_output(struct netif* netif, struct pbuf* p) { + pcap_t *pcap = netif->state; + printf("Sending packet with length %d\n", p->tot_len); + + int r = pcap_sendpacket(pcap, (uint8_t*)p->payload, p->tot_len); + + if (r != 0) { + printf("Error sending packet\n"); + printf("Error: %s\n", pcap_geterr(pcap)); + return ERR_IF; + } + return ERR_OK; +} + +static err_t init_callback(struct netif* netif) { + netif->name[0] = 't'; + netif->name[1] = 'p'; + netif->linkoutput = pcap_output; + netif->output = etharp_output; + + netif->mtu = 1500; + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET; + + netif_set_link_up(netif); + + return ERR_OK; +} + +int main(size_t argc, char **argv) { + pcap_t *pcap = pcap_open_live("eth0", 65536, 1, 100, NULL); + char errbuf[PCAP_ERRBUF_SIZE]; + int r = pcap_setnonblock(pcap, 1, errbuf); + if (r == PCAP_ERROR) { + printf("pcap_setnonblock returned: %s", errbuf); + return 1; + } + + FATFS fs; + unsigned char *buf[4096]; + + { + FRESULT r = f_mount(&fs, "", 1); + printf("f_mount returns %d\n", r); + if (r == FR_NO_FILESYSTEM) { + r = f_mkfs("", NULL, buf, sizeof buf); + printf("f_mkfs returns %d\n", r); + r = f_mount(&fs, "", 1); + printf("f_mount returns %d\n", r); + } + } + + struct netif netif; + memset(&netif, 0, sizeof netif); + netif.hwaddr_len = 6; + memcpy(netif.hwaddr, "\xaa\x00\x00\x00\x00\x01", 6); + + ip4_addr_t ip, mask, gw; + IP4_ADDR(&ip, 172,17,0,5); + IP4_ADDR(&mask, 255,255,0,0); + IP4_ADDR(&gw, 172,17,0,1); + + netif_add(&netif, &ip, &mask, &gw, pcap, init_callback, ethernet_input); + netif_set_up(&netif); + + NETIF_SET_CHECKSUM_CTRL(&netif, 0x00FF); + + ftpd_init(); + + sys_restart_timeouts(); + + struct pcap_pkthdr *hdr = NULL; + const unsigned char *data = NULL; + while(1) { + sys_check_timeouts(); + int r = pcap_next_ex(pcap, &hdr, &data); + switch (r) { + case 0: + // Timeout + continue; + case -1: + printf("Error: %s\n", pcap_geterr(pcap)); + continue; + case 1: + break; + default: + printf("Unknown result: %d\n", r); + continue; + } + printf("Packet length: %d / %d\n", hdr->len, hdr->caplen); + struct pbuf *pbuf = pbuf_alloc(PBUF_RAW, hdr->len, PBUF_RAM); + memcpy(pbuf->payload, data, hdr->len); + netif.input(pbuf, &netif); + } + + return 0; +}