Seregon/zftpd

Zero-copy FTP/HTTP Daemon compatible with all POSIX systems

C/11.0 KB/No license
include/ftp_types.h
zftpd / include / ftp_types.h
1/*
2MIT License
3 
4Copyright (c) 2026 Seregon
5 
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12 
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15 
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24 
25/**
26 * @file ftp_types.h
27 * @brief Core type definitions for FTP server
28 *
29 * @author SeregonWar
30 * @version 1.0.0
31 * @date 2026-02-13
32 *
33 */
34 
35#ifndef FTP_TYPES_H
36#define FTP_TYPES_H
37 
38#include "ftp_config.h"
39#include "ftp_crypto.h"
40#include <netinet/in.h>
41#include <pthread.h>
42#include <stdatomic.h>
43#include <stdint.h>
44#include <sys/types.h>
45 
46/*===========================================================================*
47 * ERROR CODES
48 *===========================================================================*/
49 
50/**
51 * FTP server error codes
52 * @note All errors are negative values
53 * @note Zero indicates success
54 */
55typedef enum {
56 FTP_OK = 0, /**< Operation successful */
57 FTP_ERR_INVALID_PARAM = -1, /**< Invalid parameter (NULL pointer, etc) */
58 FTP_ERR_OUT_OF_MEMORY = -2, /**< Memory allocation failed */
59 FTP_ERR_SOCKET_CREATE = -3, /**< Socket creation failed */
60 FTP_ERR_SOCKET_BIND = -4, /**< Socket bind failed */
61 FTP_ERR_SOCKET_LISTEN = -5, /**< Socket listen failed */
62 FTP_ERR_SOCKET_ACCEPT = -6, /**< Socket accept failed */
63 FTP_ERR_SOCKET_SEND = -7, /**< Socket send failed */
64 FTP_ERR_SOCKET_RECV = -8, /**< Socket receive failed */
65 FTP_ERR_THREAD_CREATE = -9, /**< Thread creation failed */
66 FTP_ERR_FILE_OPEN = -10, /**< File open failed */
67 FTP_ERR_FILE_READ = -11, /**< File read failed */
68 FTP_ERR_FILE_WRITE = -12, /**< File write failed */
69 FTP_ERR_FILE_STAT = -13, /**< File stat failed */
70 FTP_ERR_DIR_OPEN = -14, /**< Directory open failed */
71 FTP_ERR_PATH_INVALID = -15, /**< Invalid path (traversal attempt) */
72 FTP_ERR_PATH_TOO_LONG = -16, /**< Path exceeds maximum length */
73 FTP_ERR_NOT_FOUND = -17, /**< File or directory not found */
74 FTP_ERR_PERMISSION = -18, /**< Permission denied */
75 FTP_ERR_TIMEOUT = -19, /**< Operation timed out */
76 FTP_ERR_MAX_SESSIONS = -20, /**< Maximum sessions reached */
77 FTP_ERR_AUTH_FAILED = -21, /**< Authentication failed */
78 FTP_ERR_PROTOCOL = -22, /**< Protocol violation */
79 FTP_ERR_DIR_EXISTS = -23, /**< Directory already exists */
80 FTP_ERR_CROSS_DEVICE = -24, /**< Cross-device link (EXDEV) */
81 FTP_ERR_UNKNOWN = -99, /**< Unknown error */
82} ftp_error_t;
83 
84/*===========================================================================*
85 * FTP REPLY CODES (RFC 959)
86 *===========================================================================*/
87 
88/**
89 * FTP reply codes
90 * @note Standard codes from RFC 959
91 */
92typedef enum {
93 /* Positive Preliminary (1xx) */
94 FTP_REPLY_150_FILE_OK = 150, /**< File status okay */
95 
96 /* Positive Completion (2xx) */
97 FTP_REPLY_200_OK = 200, /**< Command okay */
98 FTP_REPLY_211_SYSTEM_STATUS = 211, /**< System status */
99 FTP_REPLY_212_DIR_STATUS = 212, /**< Directory status */
100 FTP_REPLY_213_FILE_STATUS = 213, /**< File status */
101 FTP_REPLY_214_HELP = 214, /**< Help message */
102 FTP_REPLY_215_SYSTEM_TYPE = 215, /**< NAME system type */
103 FTP_REPLY_220_SERVICE_READY = 220, /**< Service ready */
104 FTP_REPLY_221_GOODBYE = 221, /**< Closing connection */
105 FTP_REPLY_225_DATA_OPEN = 225, /**< Data connection open */
106 FTP_REPLY_226_TRANSFER_COMPLETE = 226, /**< Transfer complete */
107 FTP_REPLY_227_PASV_MODE = 227, /**< Entering passive mode */
108 FTP_REPLY_229_EPSV_MODE = 229, /**< Entering extended passive mode */
109 FTP_REPLY_230_LOGGED_IN = 230, /**< User logged in */
110 FTP_REPLY_234_AUTH_OK = 234, /**< AUTH mechanism accepted */
111 FTP_REPLY_250_FILE_ACTION_OK = 250, /**< File action okay */
112 FTP_REPLY_257_PATH_CREATED = 257, /**< Path created */
113 
114 /* Positive Intermediate (3xx) */
115 FTP_REPLY_331_NEED_PASSWORD = 331, /**< Need password */
116 FTP_REPLY_350_PENDING = 350, /**< Pending further info */
117 
118 /* Transient Negative (4xx) */
119 FTP_REPLY_421_SERVICE_UNAVAIL = 421, /**< Service not available */
120 FTP_REPLY_425_CANT_OPEN_DATA = 425, /**< Can't open data connection */
121 FTP_REPLY_426_TRANSFER_ABORTED = 426, /**< Transfer aborted */
122 FTP_REPLY_450_FILE_UNAVAILABLE = 450, /**< File unavailable */
123 FTP_REPLY_451_LOCAL_ERROR = 451, /**< Local error */
124 FTP_REPLY_452_INSUFFICIENT_STORAGE = 452, /**< Insufficient storage */
125 
126 /* Permanent Negative (5xx) */
127 FTP_REPLY_500_SYNTAX_ERROR = 500, /**< Syntax error */
128 FTP_REPLY_501_SYNTAX_ARGS = 501, /**< Syntax error in args */
129 FTP_REPLY_502_NOT_IMPLEMENTED = 502, /**< Not implemented */
130 FTP_REPLY_503_BAD_SEQUENCE = 503, /**< Bad command sequence */
131 FTP_REPLY_504_NOT_IMPL_PARAM = 504, /**< Not impl for parameter */
132 FTP_REPLY_530_NOT_LOGGED_IN = 530, /**< Not logged in */
133 FTP_REPLY_532_NEED_ACCOUNT = 532, /**< Need account */
134 FTP_REPLY_550_FILE_ERROR = 550, /**< File unavailable */
135 FTP_REPLY_551_PAGE_TYPE_UNKNOWN = 551, /**< Page type unknown */
136 FTP_REPLY_552_STORAGE_EXCEEDED = 552, /**< Storage exceeded */
137 FTP_REPLY_553_FILENAME_INVALID = 553, /**< Filename not allowed */
138} ftp_reply_code_t;
139 
140/*===========================================================================*
141 * SESSION STATE
142 *===========================================================================*/
143 
144/**
145 * FTP session state machine
146 * @note Atomic transitions to prevent race conditions
147 */
148typedef enum {
149 FTP_STATE_INIT = 0, /**< Initial state */
150 FTP_STATE_CONNECTED = 1, /**< TCP connected */
151 FTP_STATE_AUTHENTICATED = 2, /**< User logged in */
152 FTP_STATE_TRANSFERRING = 3, /**< Active transfer */
153 FTP_STATE_TERMINATING = 4, /**< Closing session */
154} ftp_session_state_t;
155 
156/**
157 * Data connection mode
158 */
159typedef enum {
160 FTP_DATA_MODE_NONE = 0, /**< No data connection */
161 FTP_DATA_MODE_ACTIVE = 1, /**< Active mode (PORT) */
162 FTP_DATA_MODE_PASSIVE = 2, /**< Passive mode (PASV) */
163} ftp_data_mode_t;
164 
165/**
166 * Transfer type (TYPE command)
167 */
168typedef enum {
169 FTP_TYPE_ASCII = 'A', /**< ASCII mode */
170 FTP_TYPE_BINARY = 'I', /**< Binary/Image mode */
171} ftp_transfer_type_t;
172 
173/**
174 * Transfer mode (MODE command)
175 */
176typedef enum {
177 FTP_MODE_STREAM = 'S', /**< Stream mode (default) */
178 FTP_MODE_BLOCK = 'B', /**< Block mode (not implemented) */
179 FTP_MODE_COMPRESS = 'C', /**< Compressed mode (not implemented) */
180} ftp_transfer_mode_t;
181 
182/**
183 * File structure (STRU command)
184 */
185typedef enum {
186 FTP_STRU_FILE = 'F', /**< File structure (default) */
187 FTP_STRU_RECORD = 'R', /**< Record structure (not implemented) */
188 FTP_STRU_PAGE = 'P', /**< Page structure (not implemented) */
189} ftp_file_structure_t;
190 
191/*===========================================================================*
192 * SESSION STATISTICS
193 *===========================================================================*/
194 
195/**
196 * Per-session statistics
197 * @note Cache-aligned to prevent false sharing
198 */
199typedef struct {
200 atomic_uint_fast64_t bytes_sent; /**< Total bytes sent */
201 atomic_uint_fast64_t bytes_received; /**< Total bytes received */
202 atomic_uint_fast32_t files_sent; /**< Files sent count */
203 atomic_uint_fast32_t files_received; /**< Files received count */
204 atomic_uint_fast32_t commands_processed; /**< Commands processed */
205 atomic_uint_fast32_t errors; /**< Error count */
206} ftp_session_stats_t;
207 
208/*===========================================================================*
209 * FORWARD DECLARATIONS
210 *===========================================================================*/
211 
212/**
213 * Forward declaration of server context.
214 *
215 * WHY: ftp_session_t needs a back-pointer to its owning ftp_server_context_t
216 * so the session thread can call ftp_server_release_session() at exit.
217 * ftp_server_context_t is defined later in this file (it embeds
218 * ftp_session_t[]), so a forward declaration is required to break the
219 * circular dependency.
220 */
221struct ftp_server_context;
222 
223/*===========================================================================*
224 * SESSION STRUCTURE
225 *===========================================================================*/
226 
227/**
228 * FTP client session
229 *
230 * MEMORY LAYOUT: Optimized for cache-line alignment
231 * SIZE: Approximately 2KB per session
232 * THREAD SAFETY: Access from single thread (session thread)
233 *
234 * @note Structure members ordered to minimize padding
235 */
236typedef struct ftp_session {
237 /* Control connection - accessed frequently (cache-hot) */
238 int ctrl_fd; /**< Control socket descriptor */
239 struct sockaddr_in ctrl_addr; /**< Client address */
240 
241 /* Data connection */
242 int data_fd; /**< Data socket descriptor */
243 int pasv_fd; /**< Passive listener socket */
244 struct sockaddr_in data_addr; /**< Data connection address */
245 ftp_data_mode_t data_mode; /**< Active/Passive/None */
246 
247 /* Session state - atomic for thread-safe state queries */
248 atomic_int state; /**< Current session state */
249 
250 /* Transfer parameters */
251 ftp_transfer_type_t transfer_type; /**< ASCII or Binary */
252 ftp_transfer_mode_t transfer_mode; /**< Stream/Block/Compress */
253 ftp_file_structure_t file_structure; /**< File/Record/Page */
254 off_t restart_offset; /**< REST command offset */
255 
256 /* File system state */
257 char root_path[FTP_PATH_MAX]; /**< Server root directory */
258 char cwd[FTP_PATH_MAX]; /**< Current working directory */
259 char rename_from[FTP_PATH_MAX]; /**< RNFR source path */
260 
261 /* Async Copy State */
262 char copy_from[FTP_PATH_MAX]; /**< CPFR source path */
263 pthread_t copy_thread; /**< Background copy thread */
264 pthread_mutex_t copy_mutex; /**< Mutex for copy thread state */
265 atomic_int copy_in_progress; /**< Flag indicating active background copy */
266 uint8_t
267 copy_thread_valid; /**< Flag indicating if thread handle is joinable */
268 uint8_t copy_is_move; /**< Flag indicating if copy should delete source (RNTO
269 fallback) */
270 uint8_t _padding_copy[2]; /**< Alignment padding */
271 
272 /* Authentication */
273 uint8_t auth_attempts; /**< Failed auth attempts */
274 uint8_t authenticated; /**< Login status (0/1) */
275 uint8_t user_ok; /**< USER accepted (0/1) */
276 uint8_t _padding1[1]; /**< Alignment padding */
277 
278 /* Control channel input buffering */
279 char ctrl_rxbuf[FTP_CMD_BUFFER_SIZE]; /**< Buffered control input */
280 uint16_t ctrl_rx_len; /**< Valid bytes in ctrl_rxbuf */
281 uint16_t ctrl_rx_off; /**< Read offset into ctrl_rxbuf */
282 
283 /* Thread management */
284 pthread_t thread; /**< Session thread handle */
285 uint32_t session_id; /**< Unique session ID */
286 
287 /* Timing */
288 time_t connect_time; /**< Connection timestamp */
289 time_t last_activity; /**< Last command timestamp */
290 
291 uint64_t rl_tokens; /**< Rate limiter tokens (bytes) */
292 uint64_t rl_last_ns; /**< Last refill timestamp (ns) */
293 
294 /* Encryption (ChaCha20 stream cipher) */
295 ftp_crypto_ctx_t crypto; /**< Per-session crypto context */
296 
297 /* Client identification */
298 char client_ip[INET_ADDRSTRLEN]; /**< Client IP (text) */
299 uint16_t client_port; /**< Client port */
300 uint16_t _padding2; /**< Alignment padding */
301 
302 /* Statistics */
303 ftp_session_stats_t stats;
304 
305 /*
306 * Back-pointer to the owning server context.
307 *
308 * WHY: The session thread must decrement active_sessions and reset the
309 * slot state when it exits (Bug #1 fix). Without this pointer the thread
310 * cannot reach the server context to call ftp_server_release_session().
311 *
312 * Set by server_accept_thread immediately after ftp_session_init().
313 * Never NULL for a session that is running in its own thread.
314 *
315 * @note Thread-safety: written once before thread creation, read-only
316 * thereafter — no synchronisation required.
317 */
318 struct ftp_server_context *server_ctx; /**< Owning server context */
319 
320} ftp_session_t;
321 
322/*===========================================================================*
323 * COMMAND HANDLER
324 *===========================================================================*/
325 
326/**
327 * Command argument requirements
328 */
329typedef enum {
330 FTP_ARGS_NONE = 0, /**< No arguments allowed */
331 FTP_ARGS_REQUIRED = 1, /**< Arguments required */
332 FTP_ARGS_OPTIONAL = 2, /**< Arguments optional */
333} ftp_args_req_t;
334 
335/**
336 * Command handler function pointer
337 *
338 * @param session Client session context
339 * @param args Command arguments (NULL if no args)
340 *
341 * @return FTP_OK on success, negative error code on failure
342 *
343 * @pre session != NULL
344 * @pre args != NULL if command requires arguments
345 */
346typedef ftp_error_t (*ftp_cmd_handler_t)(ftp_session_t *session,
347 const char *args);
348 
349/**
350 * Command table entry
351 */
352typedef struct {
353 const char *name; /**< Command name (uppercase) */
354 ftp_cmd_handler_t handler; /**< Handler function */
355 ftp_args_req_t args_req; /**< Argument requirements */
356} ftp_command_entry_t;
357 
358/*===========================================================================*
359 * SERVER CONTEXT
360 *===========================================================================*/
361 
362/**
363 * Global server context
364 *
365 * @note Single instance, initialized at startup
366 * @note Thread-safe: atomic operations for shared state
367 */
368typedef struct ftp_server_context {
369 /* Server socket */
370 int listen_fd; /**< Listening socket */
371 struct sockaddr_in listen_addr; /**< Server bind address */
372 uint16_t port; /**< Server port */
373 uint16_t _padding; /**< Alignment padding */
374 
375 /* Server state */
376 atomic_int running; /**< Server running flag */
377 atomic_uint_fast32_t active_sessions; /**< Active session count */
378 
379 /* Session management */
380 ftp_session_t sessions[FTP_MAX_SESSIONS]; /**< Session pool */
381 pthread_mutex_t session_lock; /**< Session pool lock */
382 
383 /* Default paths */
384 char root_path[FTP_PATH_MAX]; /**< Server root directory */
385 
386 /* Statistics */
387 _Alignas(64) struct {
388 atomic_uint_fast64_t total_connections;
389 atomic_uint_fast64_t total_bytes_sent;
390 atomic_uint_fast64_t total_bytes_received;
391 atomic_uint_fast32_t total_errors;
392 } stats;
393 
394} ftp_server_context_t;
395 
396/*===========================================================================*
397 * COMPILE-TIME SIZE CHECKS
398 *===========================================================================*/
399 
400/* Session structure size will vary by platform and alignment */
401/* Typical size: 2-4KB per session */
402 
403/* Ensure reply codes fit in uint16_t */
404_Static_assert(FTP_REPLY_553_FILENAME_INVALID <= UINT16_MAX,
405 "Reply codes must fit in uint16_t");
406 
407#endif /* FTP_TYPES_H */
408