Skip to content

Commit

Permalink
Added Memory Mapped Files, Tests, fixed Path Vulnerability and much m…
Browse files Browse the repository at this point in the history
…ore (#58)

- Performance improvements for FTP Downloads through memory-mapped file (Thanks to @bjuulp)
- Added googletest as submodule
- Added unit tests for fineftp-server. The tests require C++17 to compile and `curl` to be present in the `PATH` when executing them
- New CMake Options for Enabling / Disabling different components (See Readme.md)
- Binary downloads for Windows are now built with VS 2017 / v140 toolset
- Fixed a race condition on Windows that caused files not being fully flushed after fineftp-server reported the finished data upload (Thanks to @bjuulp)
- Reordered internal asio::strand implementation in order to prevent race conditions that haven't been detected, yet. (Thanks to @bjuulp)
- Fixed a vulnerability that enabled an attacker to access files above the root directory (reported by #52)
- Fixed many clang-tidy warnings
- The `APPE` (= append-to-file) command now creates a new file if it didn't exist already (this is the correct behavior according to RFC 959
- Updated the asio submodule to 1.28.2

---------

Co-authored-by: Bjarne Juul Pasgaard <24828375+bjuulp@users.noreply.github.com>
Co-authored-by: Bjarne Juul Pasgaard <bjp@skillson.dk>
  • Loading branch information
3 people committed Nov 2, 2023
1 parent c12589a commit 4e8ae35
Show file tree
Hide file tree
Showing 37 changed files with 3,517 additions and 274 deletions.
34 changes: 30 additions & 4 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,31 @@
# -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling
# This warns about memcpy and wants us to use memcpy_s, which is not available in our gcc setup.
#
# -cppcoreguidelines-avoid-const-or-ref-data-members
# I seriously don't understand why I shouldn't make a member variable const,
# if I want to prevent people from changing it. I actually consider it good
# style to do that
#
# -cppcoreguidelines-pro-type-vararg
# This forbids using functions like printf, snprintf etc. We would like to use those either way.
#
# -misc-no-recursion
# Recursion with functions can be an elegant way of solving recursive problems
#
# -misc-include-cleaner
# I would love to keep this option, but it would force me to include all
# Win32 headers by hand instead of just including Windows.h. It would also force
# me to copy the entire content of <asio/asio.hpp> into all of my cpp files and
# there is no way that this would improve my code.
#
# -performance-avoid-endl
# std::endl seems to be a good idea often, as it also flushes the stream buffer
#
# -bugprone-unused-return-value
# asio returns error codes as parameter AND return value. The paraemter is
# necessary to get teh error-code at all, but the return value is absolutely
# useless in that case.
#
# These checks have been disabled to keep compatibility with C++14:
# -modernize-concat-nested-namespaces
# -modernize-use-nodiscard
Expand All @@ -23,9 +42,13 @@ Checks: "-*,
-bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-narrowing-conversions,
-bugprone-unused-return-value,
cppcoreguidelines-*,
-cppcoreguidelines-avoid-const-or-ref-data-members,
-cppcoreguidelines-avoid-do-while,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-narrowing-conversions,
-cppcoreguidelines-non-private-member-variables-in-classes,
Expand All @@ -35,19 +58,22 @@ Checks: "-*,
-cppcoreguidelines-pro-type-reinterpret-cast,
misc-*,
-misc-include-cleaner,
-misc-non-private-member-variables-in-classes,
-misc-no-recursion,
modernize-*,
-modernize-pass-by-value,
-modernize-use-trailing-return-type,
-modernize-use-auto,
-modernize-avoid-bind,
-modernize-concat-nested-namespaces,
-modernize-pass-by-value,
-modernize-raw-string-literal,
-modernize-return-braced-init-list,
-modernize-use-auto,
-modernize-use-nodiscard,
-modernize-avoid-bind,
-modernize-use-trailing-return-type,
performance-*,
-performance-avoid-endl
readability-*,
-readability-braces-around-statements,
Expand Down
11 changes: 9 additions & 2 deletions .github/workflows/build-macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,20 @@ jobs:
submodules: 'true'
fetch-depth: 0


- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: cmake -B ${{github.workspace}}/_build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
run: |
cmake -B ${{github.workspace}}/_build \
-DFINEFTP_SERVER_BUILD_TESTS=ON \
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
shell: bash

- name: Build
# Build your program with the given configuration
run: cmake --build ${{github.workspace}}/_build --config ${{env.BUILD_TYPE}}

- name: Run Tests
run: ctest -C Release -V
working-directory: ${{ github.workspace }}/_build

5 changes: 5 additions & 0 deletions .github/workflows/build-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,18 @@ jobs:
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: |
cmake -B ${{github.workspace}}/_build \
-DFINEFTP_SERVER_BUILD_TESTS=ON \
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
-DBUILD_SHARED_LIBS=${{ env.build_shared_libs }}
- name: Build
# Build your program with the given configuration
run: cmake --build ${{github.workspace}}/_build --config ${{env.BUILD_TYPE}}

- name: Run Tests
run: ctest -C Release -V
working-directory: ${{ github.workspace }}/_build

- name: Read Project Version from CMakeCache
run: |
cmake_project_version_string=$(cat "${{github.workspace}}/_build/CMakeCache.txt" | grep "^CMAKE_PROJECT_VERSION:")
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/build-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
INSTALL_PREFIX: _install
PROJECT_NAME: fineftp-server
VS_TOOLSET: v140
VS_NAME: vs2015
VS_TOOLSET: v141
VS_NAME: vs2017

jobs:
build-windows:
Expand Down Expand Up @@ -59,6 +59,7 @@ jobs:
-G "Visual Studio 16 2019" ^
-A ${{ matrix.build_arch }} ^
-T ${{ env.VS_TOOLSET }} ^
-DFINEFTP_SERVER_BUILD_TESTS=ON ^
-DCMAKE_INSTALL_PREFIX=${{env.INSTALL_PREFIX}} ^
-DBUILD_SHARED_LIBS=${{ env.build_shared_libs }}
Expand All @@ -73,6 +74,10 @@ jobs:
run: |
cmake --build ${{github.workspace}}/_build --config Debug --parallel
cmake --build ${{github.workspace}}/_build --config Debug --target INSTALL
- name: Run Tests
run: ctest -C Release -V
working-directory: ${{ github.workspace }}/_build

- name: Read Project Version from CMakeCache
run: |
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ ipch
*.user
*.opendb
*.db
*.vscode
/_build
/samples/integration_test/_build
/_install
/.vs
/CMakeLists.txt.user

# Common build directories in CMake projects
build*
/build*

# Temporary Vim files
*.swp
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "thirdparty/asio"]
path = thirdparty/asio
url = https://github.com/chriskohlhoff/asio.git
[submodule "thirdparty/googletest"]
path = thirdparty/googletest
url = https://github.com/google/googletest.git
42 changes: 39 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.5.1)

include(CMakeDependentOption)

# Project call
include("${CMAKE_CURRENT_LIST_DIR}/fineftp-server/version.cmake")
project(fineftp VERSION ${FINEFTP_SERVER_VERSION_MAJOR}.${FINEFTP_SERVER_VERSION_MINOR}.${FINEFTP_SERVER_VERSION_PATCH})
Expand All @@ -12,23 +14,57 @@ message(STATUS "Prefix Path: ${CMAKE_PREFIX_PATH}")

# CMake Options
option(FINEFTP_SERVER_BUILD_SAMPLES
"Build project samples"
"Build project samples."
ON)
option(FINEFTP_SERVER_BUILD_TESTS
"Build the the fineftp-server tests. Requires C++17. For executing the tests, curl must be available from the PATH."
OFF)

# Module path for finding asio
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
option(FINEFTP_SERVER_USE_BUILTIN_ASIO
"Use the builtin asio submodule. If set to OFF, asio must be available from somewhere else (e.g. system libs)."
ON)
cmake_dependent_option(FINEFTP_SERVER_USE_BUILTIN_GTEST
"Use the builtin GoogleTest submodule. Only needed if FINEFTP_SERVER_BUILD_TESTS is ON. If set to OFF, GoogleTest must be available from somewhere else (e.g. system libs)."
ON # Default value if dependency is met
"FINEFTP_SERVER_BUILD_TESTS" # Dependency
OFF) # Default value if dependency is not met

# Set Debug postfix
set(CMAKE_DEBUG_POSTFIX d)
set(CMAKE_MINSIZEREL_POSTFIX minsize)
set(CMAKE_RELWITHDEBINFO_POSTFIX reldbg)

# Use builtin asio
if (FINEFTP_SERVER_USE_BUILTIN_ASIO)
include("${CMAKE_CURRENT_LIST_DIR}/thirdparty/build-asio.cmake")
endif()

# Use builtin gtest
if (FINEFTP_SERVER_USE_BUILTIN_GTEST)
include("${CMAKE_CURRENT_LIST_DIR}/thirdparty/build-gtest.cmake")
endif()

# For tests we need to make sure that all shared libraries and executables are
# put into the same directory. Otherwise the tests will fail on windows.
if(FINEFTP_SERVER_BUILD_TESTS AND BUILD_SHARED_LIBS AND FINEFTP_SERVER_USE_BUILTIN_GTEST)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
endif()

# Add main fineftp::server library
add_subdirectory(fineftp-server)

# Add the fineftp::server dummy module
# Module path for finding asio
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/fineftp-module)

if (FINEFTP_SERVER_BUILD_SAMPLES)
add_subdirectory(samples/fineftp_example)
endif()

if (FINEFTP_SERVER_BUILD_TESTS)
enable_testing()
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/tests/fineftp_test")
endif()

# Make this package available for packing with CPack
include("${CMAKE_CURRENT_LIST_DIR}/cpack_config.cmake")
59 changes: 58 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

FineFTP is a minimal FTP server library for Windows and Unix flavors. The project is CMake based and only depends on asio, which is integrated as git submodule. No boost is required.

You can easily embed this library into your own project in order to create an embedded FTP Server. It was developed and tested on Windows 10 (Visual Studio 2015 / 2019, MinGW) and Ubuntu 16.04 - 21.10 (gcc 5.4.0 - 11.2.0).
You can easily embed this library into your own project in order to create an embedded FTP Server. It was developed and tested on Windows 10 (Visual Studio 2015 and newer, MinGW) and Ubuntu 18.04 - 22.04 (gcc 7.4.0 - 11.2.0). It should also run fine on macOS.

## Features

Expand Down Expand Up @@ -74,6 +74,63 @@ There is an example project provided that will create an FTP Server at `C:\` (Wi

5. Start `fineftp_example` / `fineftp_example.exe` and connect with your favorite FTP Client (e.g. FileZilla) on port 2121 *(This port is used so you don't need root privileges to start the FTP server)*

## CMake Options

You can set the following CMake Options to control how fineFTP Server is built:

**Option** | **Type** | **Default** | **Explanation** |
|--------------------------------|----------|-------------|-----------------------------------------------------------------------------------------------------------------|
| `FINEFTP_SERVER_BUILD_SAMPLES` | `BOOL` | `ON` | Build the fineFTP Server sample project. |
| `FINEFTP_SERVER_BUILD_TESTS` | `BOOL` | `OFF` | Build the the fineftp-server tests. Requires C++17. For executing the tests, `curl` must be available from the `PATH`. |
| `FINEFTP_SERVER_USE_BUILTIN_ASIO`| `BOOL`| `ON` | Use the builtin asio submodule. If set to `OFF`, asio must be available from somewhere else (e.g. system libs). |
| `FINEFTP_SERVER_USE_BUILTIN_GTEST`| `BOOL`| `ON` <br>_(when building tests)_ | Use the builtin GoogleTest submodule. Only needed if `FINEFTP_SERVER_BUILD_TESTS` is `ON`. If set to `OFF`, GoogleTest must be available from somewhere else (e.g. system libs). |
| `BUILD_SHARED_LIBS` | `BOOL` | | Not a fineFTP Server option, but use this to control whether you want to have a static or shared library. |

## How to integrate in your project

### Option 1: Integrate as binaries

1. Download the latest release from the releases page or compile the binaries yourself.

2. Add the fineFTP Server directory to your `CMAKE_PREFIX_PATH`:

```shell
cmake your_command_line -DCMAKE_PREFIX_PATH=path/to/fineftp/install/dir
```

### Option 2: Integrate as source

1. Make the fineFTP Server directory available in your project. You can either add it as a git submodule, or use CMake FetchContent to download it.

2. Add it to your CMake Project:

- **Either** by adding the top-level CMakeLists.txt to your project

```cmake
add_subdirectory(path/to/fineftp-server)
```

This which will inherit some behavior:

- You can use the CMake options described below
- You will get the asio version shipped with fineFTP
- The debug / minsize / relwithdebinfo postfix will be set automatically


- **Or** if you want to get a very clean version, which doesn't set any unnecessary options, include the `fineftp-server/server` subdirectory:

```cmake
add_subdirectory(path/to/fineftp-server/server)
```

You have to provide the required asio target on your own.

### Link against fineFTP Server

```cmake
find_package(fineftp REQUIRED)
target_link_libraries(your_target PRIVATE fineftp::server)
```

## Contribute

Expand Down
File renamed without changes.
10 changes: 10 additions & 0 deletions fineftp-server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ set(sources
src/win_str_convert.h
)

if (WIN32)
list(APPEND sources src/win32/file_man.cpp)
list(APPEND sources src/win32/file_man.h)
set(platform_include src/win32)
else()
list(APPEND sources src/unix/file_man.cpp)
list(APPEND sources src/unix/file_man.h)
set(platform_include src/unix)
endif()

add_library (${PROJECT_NAME}
${includes}
Expand Down Expand Up @@ -92,6 +101,7 @@ target_include_directories(${PROJECT_NAME}
$<INSTALL_INTERFACE:include>
PRIVATE
src/
${platform_include}
)

set_target_properties(${PROJECT_NAME} PROPERTIES
Expand Down
4 changes: 4 additions & 0 deletions fineftp-server/include/fineftp/server.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
#pragma once

#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>

// IWYU pragma: begin_exports
#include <fineftp/permissions.h>

#include <fineftp/fineftp_version.h>
#include <fineftp/fineftp_export.h>
// IWYU pragma: end_exports

namespace fineftp
{
Expand Down

0 comments on commit 4e8ae35

Please sign in to comment.