Seregon/zftpd

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

C/11.0 KB/No license
tests/test_mlst_ascii.c
zftpd / tests / test_mlst_ascii.c
1#include "ftp_commands.h"
2#include "ftp_session.h"
3#include <arpa/inet.h>
4#include <errno.h>
5#include <fcntl.h>
6#include <stdatomic.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/socket.h>
11#include <sys/stat.h>
12#include <sys/types.h>
13#include <unistd.h>
14 
15static int read_available_text(int fd, char *buffer, size_t size) {
16 if ((buffer == NULL) || (size == 0U)) {
17 return -1;
18 }
19 
20 int flags = fcntl(fd, F_GETFL, 0);
21 if (flags < 0) {
22 return -1;
23 }
24 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
25 return -1;
26 }
27 
28 size_t total = 0U;
29 while ((total + 1U) < size) {
30 ssize_t n = recv(fd, buffer + total, size - total - 1U, 0);
31 if (n > 0) {
32 total += (size_t)n;
33 continue;
34 }
35 if (n == 0) {
36 break;
37 }
38 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
39 break;
40 }
41 (void)fcntl(fd, F_SETFL, flags);
42 return -1;
43 }
44 
45 buffer[total] = '\0';
46 (void)fcntl(fd, F_SETFL, flags);
47 return (int)total;
48}
49 
50static int buffer_contains(const char *buffer, const char *needle) {
51 return ((buffer != NULL) && (needle != NULL) && (strstr(buffer, needle) != NULL))
52 ? 1
53 : 0;
54}
55 
56static int init_authenticated_session(ftp_session_t *session, int ctrl_fd,
57 const char *root_path) {
58 struct sockaddr_in client_addr;
59 memset(&client_addr, 0, sizeof(client_addr));
60 client_addr.sin_family = AF_INET;
61 (void)inet_pton(AF_INET, "127.0.0.1", &client_addr.sin_addr);
62 client_addr.sin_port = htons(12021);
63 
64 ftp_error_t err =
65 ftp_session_init(session, ctrl_fd, &client_addr, 7U, root_path);
66 if (err != FTP_OK) {
67 return -1;
68 }
69 
70 session->authenticated = 1U;
71 session->user_ok = 1U;
72 atomic_store(&session->state, FTP_STATE_AUTHENTICATED);
73 return 0;
74}
75 
76static int test_mlst_reply(void) {
77 char root_template[] = "/tmp/zftpd-mlst-XXXXXX";
78 char *root_dir = mkdtemp(root_template);
79 if (root_dir == NULL) {
80 return 1;
81 }
82 
83 char file_path[FTP_PATH_MAX];
84 int n = snprintf(file_path, sizeof(file_path), "%s/sample.txt", root_dir);
85 if ((n < 0) || ((size_t)n >= sizeof(file_path))) {
86 rmdir(root_dir);
87 return 2;
88 }
89 
90 int file_fd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
91 if (file_fd < 0) {
92 rmdir(root_dir);
93 return 3;
94 }
95 if (write(file_fd, "hello", 5U) != 5) {
96 close(file_fd);
97 unlink(file_path);
98 rmdir(root_dir);
99 return 4;
100 }
101 close(file_fd);
102 
103 int ctrl_sv[2];
104 if (socketpair(AF_UNIX, SOCK_STREAM, 0, ctrl_sv) != 0) {
105 unlink(file_path);
106 rmdir(root_dir);
107 return 5;
108 }
109 
110 ftp_session_t session;
111 if (init_authenticated_session(&session, ctrl_sv[0], root_dir) != 0) {
112 close(ctrl_sv[0]);
113 close(ctrl_sv[1]);
114 unlink(file_path);
115 rmdir(root_dir);
116 return 6;
117 }
118 
119 ftp_error_t err = cmd_MLST(&session, "sample.txt");
120 if (err != FTP_OK) {
121 ftp_session_cleanup(&session);
122 close(ctrl_sv[1]);
123 unlink(file_path);
124 rmdir(root_dir);
125 return 7;
126 }
127 
128 char reply[1024];
129 if (read_available_text(ctrl_sv[1], reply, sizeof(reply)) <= 0) {
130 ftp_session_cleanup(&session);
131 close(ctrl_sv[1]);
132 unlink(file_path);
133 rmdir(root_dir);
134 return 8;
135 }
136 
137 if (buffer_contains(reply, "250-Listing /sample.txt\r\n") == 0 ||
138 buffer_contains(reply, " type=file;size=5;modify=") == 0 ||
139 buffer_contains(reply, "unix.mode=0644; /sample.txt\r\n") == 0 ||
140 buffer_contains(reply, "250 End\r\n") == 0) {
141 ftp_session_cleanup(&session);
142 close(ctrl_sv[1]);
143 unlink(file_path);
144 rmdir(root_dir);
145 return 9;
146 }
147 
148 ftp_session_cleanup(&session);
149 close(ctrl_sv[1]);
150 unlink(file_path);
151 rmdir(root_dir);
152 return 0;
153}
154 
155static int test_ascii_stor(void) {
156 char root_template[] = "/tmp/zftpd-stor-XXXXXX";
157 char *root_dir = mkdtemp(root_template);
158 if (root_dir == NULL) {
159 return 11;
160 }
161 
162 int ctrl_sv[2];
163 if (socketpair(AF_UNIX, SOCK_STREAM, 0, ctrl_sv) != 0) {
164 rmdir(root_dir);
165 return 12;
166 }
167 
168 ftp_session_t session;
169 if (init_authenticated_session(&session, ctrl_sv[0], root_dir) != 0) {
170 close(ctrl_sv[0]);
171 close(ctrl_sv[1]);
172 rmdir(root_dir);
173 return 13;
174 }
175 
176 ftp_error_t err = cmd_TYPE(&session, "A");
177 if (err != FTP_OK || session.transfer_type != FTP_TYPE_ASCII) {
178 ftp_session_cleanup(&session);
179 close(ctrl_sv[1]);
180 rmdir(root_dir);
181 return 14;
182 }
183 
184 char reply[1024];
185 if (read_available_text(ctrl_sv[1], reply, sizeof(reply)) <= 0 ||
186 buffer_contains(reply, "200 Type set.\r\n") == 0) {
187 ftp_session_cleanup(&session);
188 close(ctrl_sv[1]);
189 rmdir(root_dir);
190 return 15;
191 }
192 
193 err = cmd_STOR(&session, "ascii.txt");
194 if (err != FTP_OK) {
195 ftp_session_cleanup(&session);
196 close(ctrl_sv[1]);
197 rmdir(root_dir);
198 return 16;
199 }
200 
201 if (read_available_text(ctrl_sv[1], reply, sizeof(reply)) <= 0 ||
202 buffer_contains(reply,
203 "150 File status okay; about to open data connection.\r\n") ==
204 0 ||
205 buffer_contains(reply, "425 Can't open data connection.\r\n") ==
206 0) {
207 ftp_session_cleanup(&session);
208 close(ctrl_sv[1]);
209 rmdir(root_dir);
210 return 17;
211 }
212 
213 char stored_path[FTP_PATH_MAX];
214 int nn = snprintf(stored_path, sizeof(stored_path), "%s/ascii.txt", root_dir);
215 if ((nn < 0) || ((size_t)nn >= sizeof(stored_path))) {
216 ftp_session_cleanup(&session);
217 close(ctrl_sv[1]);
218 rmdir(root_dir);
219 return 18;
220 }
221 
222 if (access(stored_path, F_OK) == 0) {
223 ftp_session_cleanup(&session);
224 close(ctrl_sv[1]);
225 unlink(stored_path);
226 rmdir(root_dir);
227 return 19;
228 }
229 
230 ftp_session_cleanup(&session);
231 close(ctrl_sv[1]);
232 rmdir(root_dir);
233 return 0;
234}
235 
236int main(void) {
237 int rc = test_mlst_reply();
238 if (rc != 0) {
239 return rc;
240 }
241 
242 rc = test_ascii_stor();
243 if (rc != 0) {
244 return rc;
245 }
246 
247 return 0;
248}
249