Lines 44-49
Link Here
|
44 |
#define ADDRPORT_STRLEN 58 |
44 |
#define ADDRPORT_STRLEN 58 |
45 |
|
45 |
|
46 |
/* prototypes */ |
46 |
/* prototypes */ |
|
|
47 |
static int alloc_ipv4(ftp_session_t *f); |
47 |
static int invariant(const ftp_session_t *f); |
48 |
static int invariant(const ftp_session_t *f); |
48 |
static void reply(ftp_session_t *f, int code, const char *fmt, ...); |
49 |
static void reply(ftp_session_t *f, int code, const char *fmt, ...); |
49 |
static void change_dir(ftp_session_t *f, const char *new_dir); |
50 |
static void change_dir(ftp_session_t *f, const char *new_dir); |
Lines 137-175
int ftp_session_init(ftp_session_t *f,
Link Here
|
137 |
daemon_assert(strlen(dir) <= PATH_MAX); |
138 |
daemon_assert(strlen(dir) <= PATH_MAX); |
138 |
daemon_assert(err != NULL); |
139 |
daemon_assert(err != NULL); |
139 |
|
140 |
|
140 |
#ifdef INET6 |
141 |
memset(&f->server_ipv4_addr, 0, sizeof(f->server_ipv4_addr)); |
141 |
/* if the control connection is on IPv6, we need to get an IPv4 address */ |
|
|
142 |
/* to bind the socket to */ |
143 |
if (SSFAM(server_addr) == AF_INET6) { |
144 |
struct addrinfo hints; |
145 |
struct addrinfo *res; |
146 |
int errcode; |
147 |
|
148 |
/* getaddrinfo() does the job nicely */ |
149 |
memset(&hints, 0, sizeof(struct addrinfo)); |
150 |
hints.ai_family = AF_INET; |
151 |
hints.ai_flags = AI_PASSIVE; |
152 |
if (getaddrinfo(NULL, "ftp", &hints, &res) != 0) { |
153 |
error_init(err, 0, "unable to determing IPv4 address; %s", |
154 |
gai_strerror(errcode)); |
155 |
return 0; |
156 |
} |
157 |
|
158 |
/* let's sanity check */ |
159 |
daemon_assert(res != NULL); |
160 |
daemon_assert(sizeof(f->server_ipv4_addr) >= res->ai_addrlen); |
161 |
daemon_assert(SSFAM(host_port) == AF_INET); |
162 |
|
163 |
/* copy the result and free memory as necessary */ |
164 |
memcpy(&f->server_ipv4_addr, res->ai_addr, res->ai_addrlen); |
165 |
freeaddrinfo(res); |
166 |
} else { |
167 |
daemon_assert(SSFAM(host_port) == AF_INET); |
168 |
f->server_ipv4_addr = *server_addr; |
169 |
} |
170 |
#else |
171 |
f->server_ipv4_addr = *server_addr; |
172 |
#endif |
173 |
|
142 |
|
174 |
f->session_active = 1; |
143 |
f->session_active = 1; |
175 |
f->command_number = 0; |
144 |
f->command_number = 0; |
Lines 291-296
void ftp_session_destroy(ftp_session_t *
Link Here
|
291 |
} |
260 |
} |
292 |
} |
261 |
} |
293 |
|
262 |
|
|
|
263 |
#ifdef INET6 |
264 |
static int alloc_ipv4(ftp_session_t *f) |
265 |
{ |
266 |
/* if the control connection is on IPv6, we need to get an IPv4 address */ |
267 |
/* to bind the socket to */ |
268 |
if (SSFAM(&f->server_addr) == AF_INET6) { |
269 |
struct addrinfo hints; |
270 |
struct addrinfo *res; |
271 |
int errcode; |
272 |
|
273 |
/* getaddrinfo() does the job nicely */ |
274 |
memset(&hints, 0, sizeof(struct addrinfo)); |
275 |
hints.ai_family = AF_INET; |
276 |
hints.ai_flags = AI_PASSIVE; |
277 |
if ((errcode = getaddrinfo(NULL, "ftp", &hints, &res)) != 0) { |
278 |
syslog(LOG_ERR, "Unable to determing IPv4 address: %s", gai_strerror(errcode)); |
279 |
return 0; |
280 |
} |
281 |
|
282 |
/* let's sanity check */ |
283 |
daemon_assert(res != NULL); |
284 |
daemon_assert(sizeof(f->server_ipv4_addr) >= res->ai_addrlen); |
285 |
daemon_assert(SSFAM(&f->server_ipv4_addr) == AF_INET); |
286 |
|
287 |
/* copy the result and free memory as necessary */ |
288 |
memcpy(&f->server_ipv4_addr, res->ai_addr, res->ai_addrlen); |
289 |
freeaddrinfo(res); |
290 |
} else { |
291 |
daemon_assert(SSFAM(&f->server_addr) == AF_INET); |
292 |
memcpy(&f->server_ipv4_addr, &f->server_addr, sizeof(f->server_ipv4_addr)); |
293 |
} |
294 |
return 1; |
295 |
} |
296 |
#else |
297 |
inline static int alloc_ipv4(ftp_session_t *f) |
298 |
{ |
299 |
return 0; |
300 |
} |
301 |
#endif |
302 |
|
294 |
#ifndef NDEBUG |
303 |
#ifndef NDEBUG |
295 |
static int invariant(const ftp_session_t *f) |
304 |
static int invariant(const ftp_session_t *f) |
296 |
{ |
305 |
{ |
Lines 333-339
static int invariant(const ftp_session_t
Link Here
|
333 |
return 0; |
342 |
return 0; |
334 |
} |
343 |
} |
335 |
break; |
344 |
break; |
336 |
case DATA_PASSIVE: |
345 |
case DATA_PASSIVE_4: |
|
|
346 |
case DATA_PASSIVE_6: |
337 |
if (f->server_fd < 0) { |
347 |
if (f->server_fd < 0) { |
338 |
return 0; |
348 |
return 0; |
339 |
} |
349 |
} |
Lines 424-439
static void get_addr_str(const sockaddr_
Link Here
|
424 |
/* buf must be able to contain (at least) a string representing an |
434 |
/* buf must be able to contain (at least) a string representing an |
425 |
* ipv4 addr, followed by the string " port " (6 chars) and the port |
435 |
* ipv4 addr, followed by the string " port " (6 chars) and the port |
426 |
* number (which is 5 chars max), plus the '\0' character. */ |
436 |
* number (which is 5 chars max), plus the '\0' character. */ |
427 |
daemon_assert(bufsiz >= (INET_ADDRSTRLEN + 12)); |
437 |
daemon_assert(bufsiz >= (INET6_ADDRSTRLEN + 12)); |
428 |
|
438 |
|
429 |
error = getnameinfo(client_addr, sizeof(sockaddr_storage_t), buf, |
439 |
error = getnameinfo((struct sockaddr *)s, sizeof(sockaddr_storage_t), buf, |
430 |
bufsiz, NULL, 0, NI_NUMERICHOST); |
440 |
bufsiz, NULL, 0, NI_NUMERICHOST); |
431 |
/* getnameinfo() should never fail when called with NI_NUMERICHOST */ |
441 |
/* getnameinfo() should never fail when called with NI_NUMERICHOST */ |
432 |
daemon_assert(error == 0); |
442 |
daemon_assert(error == 0); |
433 |
|
443 |
|
434 |
len = strlen(buf); |
444 |
len = strlen(buf); |
435 |
daemon_assert(bufsiz >= len+12); |
445 |
daemon_assert(bufsiz >= len+12); |
436 |
snprintf(buf+len, bufsiz-len, " port %d", ntohs(SINPORT(&f->client_addr))); |
446 |
snprintf(buf+len, bufsiz-len, " port %d", ntohs(SINPORT(s))); |
437 |
} |
447 |
} |
438 |
#else |
448 |
#else |
439 |
static void get_addr_str(const sockaddr_storage_t *s, char *buf, int bufsiz) |
449 |
static void get_addr_str(const sockaddr_storage_t *s, char *buf, int bufsiz) |
Lines 639-645
static void set_port(ftp_session_t *f, c
Link Here
|
639 |
reply(f, 500, "Port may not be less than 1024, which is reserved."); |
649 |
reply(f, 500, "Port may not be less than 1024, which is reserved."); |
640 |
} else { |
650 |
} else { |
641 |
/* close any outstanding PASSIVE port */ |
651 |
/* close any outstanding PASSIVE port */ |
642 |
if (f->data_channel == DATA_PASSIVE) { |
652 |
if (f->data_channel == DATA_PASSIVE_4 || f->data_channel == DATA_PASSIVE_6) { |
643 |
close(f->server_fd); |
653 |
close(f->server_fd); |
644 |
f->server_fd = -1; |
654 |
f->server_fd = -1; |
645 |
} |
655 |
} |
Lines 733-744
static int set_pasv(ftp_session_t *f, so
Link Here
|
733 |
|
743 |
|
734 |
for (;;) { |
744 |
for (;;) { |
735 |
port = get_passive_port(); |
745 |
port = get_passive_port(); |
736 |
SINPORT(bind_addr) = htons(port); |
746 |
#ifdef INET6 |
|
|
747 |
if (SAFAM(bind_addr) == AF_INET6) |
748 |
SIN6PORT(bind_addr) = htons(port); |
749 |
else |
750 |
SIN4PORT(bind_addr) = htons(port); |
737 |
if (bind(socket_fd, (struct sockaddr *)bind_addr, |
751 |
if (bind(socket_fd, (struct sockaddr *)bind_addr, |
738 |
sizeof(struct sockaddr)) == 0) |
752 |
SAFAM(bind_addr) == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == 0) |
739 |
{ |
|
|
740 |
break; |
753 |
break; |
741 |
} |
754 |
#else |
|
|
755 |
SIN4PORT(bind_addr) = htons(port); |
756 |
if (bind(socket_fd, (struct sockaddr *)bind_addr, sizeof(struct sockaddr_in)) == 0) |
757 |
break; |
758 |
#endif |
742 |
if (errno != EADDRINUSE) { |
759 |
if (errno != EADDRINUSE) { |
743 |
reply(f, 500, "Error binding server port; %s.", strerror(errno)); |
760 |
reply(f, 500, "Error binding server port; %s.", strerror(errno)); |
744 |
close(socket_fd); |
761 |
close(socket_fd); |
Lines 771-784
static void do_pasv(ftp_session_t *f, co
Link Here
|
771 |
goto exit_pasv; |
788 |
goto exit_pasv; |
772 |
} |
789 |
} |
773 |
|
790 |
|
774 |
socket_fd = set_pasv(f, &f->server_ipv4_addr); |
791 |
if (SAFAM(&f->server_addr) == AF_INET) |
|
|
792 |
socket_fd = set_pasv(f, (sockaddr_storage_t *)&f->server_addr); |
793 |
else if (SAFAM(&f->server_ipv4_addr) == AF_UNSPEC) { |
794 |
if (!alloc_ipv4(f)) { |
795 |
reply(f, 522, "PASV not possible, use EPSV"); |
796 |
goto exit_pasv; |
797 |
} |
798 |
socket_fd = set_pasv(f, (sockaddr_storage_t *)&f->server_ipv4_addr); |
799 |
} else |
800 |
socket_fd = set_pasv(f, (sockaddr_storage_t *)&f->server_ipv4_addr); |
775 |
if (socket_fd == -1) { |
801 |
if (socket_fd == -1) { |
776 |
goto exit_pasv; |
802 |
goto exit_pasv; |
777 |
} |
803 |
} |
778 |
|
804 |
|
779 |
/* report port to client */ |
805 |
/* report port to client */ |
780 |
addr = ntohl(f->server_ipv4_addr.sin_addr.s_addr); |
806 |
if (SAFAM(&f->server_addr) == AF_INET) { |
781 |
port = ntohs(f->server_ipv4_addr.sin_port); |
807 |
addr = ntohl(SIN4ADDR(&f->server_addr).s_addr); |
|
|
808 |
port = ntohs(SIN4PORT(&f->server_addr)); |
809 |
} else { |
810 |
addr = ntohl(SIN4ADDR(&f->server_ipv4_addr).s_addr); |
811 |
port = ntohs(SIN4PORT(&f->server_ipv4_addr)); |
812 |
} |
782 |
reply(f, 227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d).", |
813 |
reply(f, 227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d).", |
783 |
addr >> 24, |
814 |
addr >> 24, |
784 |
(addr >> 16) & 0xff, |
815 |
(addr >> 16) & 0xff, |
Lines 788-797
static void do_pasv(ftp_session_t *f, co
Link Here
|
788 |
port & 0xff); |
819 |
port & 0xff); |
789 |
|
820 |
|
790 |
/* close any outstanding PASSIVE port */ |
821 |
/* close any outstanding PASSIVE port */ |
791 |
if (f->data_channel == DATA_PASSIVE) { |
822 |
if (f->data_channel == DATA_PASSIVE_4 || f->data_channel == DATA_PASSIVE_6) { |
792 |
close(f->server_fd); |
823 |
close(f->server_fd); |
793 |
} |
824 |
} |
794 |
f->data_channel = DATA_PASSIVE; |
825 |
f->data_channel = DATA_PASSIVE_4; |
795 |
f->server_fd = socket_fd; |
826 |
f->server_fd = socket_fd; |
796 |
|
827 |
|
797 |
exit_pasv: |
828 |
exit_pasv: |
Lines 841-850
static void do_lpsv(ftp_session_t *f, co
Link Here
|
841 |
reply(f, 228, "Entering Long Passive Mode %s", addr); |
872 |
reply(f, 228, "Entering Long Passive Mode %s", addr); |
842 |
|
873 |
|
843 |
/* close any outstanding PASSIVE port */ |
874 |
/* close any outstanding PASSIVE port */ |
844 |
if (f->data_channel == DATA_PASSIVE) { |
875 |
if (f->data_channel == DATA_PASSIVE_4 || f->data_channel == DATA_PASSIVE_6) { |
845 |
close(f->server_fd); |
876 |
close(f->server_fd); |
846 |
} |
877 |
} |
847 |
f->data_channel = DATA_PASSIVE; |
878 |
#ifdef INET6 |
|
|
879 |
f->data_channel = SSFAM(&f->server_addr) == AF_INET6 ? DATA_PASSIVE_6 : DATA_PASSIVE_4; |
880 |
#else |
881 |
f->data_channel = DATA_PASSIVE_4; |
882 |
#endif |
848 |
f->server_fd = socket_fd; |
883 |
f->server_fd = socket_fd; |
849 |
|
884 |
|
850 |
exit_lpsv: |
885 |
exit_lpsv: |
Lines 875-885
static void do_epsv(ftp_session_t *f, co
Link Here
|
875 |
reply(f, 200, "EPSV ALL command successful."); |
910 |
reply(f, 200, "EPSV ALL command successful."); |
876 |
goto exit_epsv; |
911 |
goto exit_epsv; |
877 |
case 1: |
912 |
case 1: |
878 |
addr = (sockaddr_storage_t *)&f->server_ipv4_addr; |
913 |
if (SAFAM(&f->server_addr) == AF_INET) |
|
|
914 |
addr = &f->server_addr; |
915 |
else if (SAFAM(&f->server_ipv4_addr) == AF_UNSPEC) { |
916 |
if (!alloc_ipv4(f)) { |
917 |
reply(f, 522, "Only IPv6 supported, use (2)"); |
918 |
goto exit_epsv; |
919 |
} |
920 |
addr = &f->server_ipv4_addr; |
921 |
} else |
922 |
addr = &f->server_ipv4_addr; |
879 |
break; |
923 |
break; |
880 |
#ifdef INET6 |
924 |
#ifdef INET6 |
881 |
case 2: |
925 |
case 2: |
882 |
addr = &f->server_addr; |
926 |
if (SAFAM(&f->server_addr) == AF_INET) { |
|
|
927 |
reply(f, 522, "Only IPv4 supported, use (1)"); |
928 |
goto exit_epsv; |
929 |
} |
930 |
addr = &f->server_addr; |
883 |
break; |
931 |
break; |
884 |
default: |
932 |
default: |
885 |
reply(f, 522, "Only IPv4 and IPv6 supported, use (1,2)"); |
933 |
reply(f, 522, "Only IPv4 and IPv6 supported, use (1,2)"); |
Lines 903-912
static void do_epsv(ftp_session_t *f, co
Link Here
|
903 |
ntohs(SINPORT(&f->server_addr))); |
951 |
ntohs(SINPORT(&f->server_addr))); |
904 |
|
952 |
|
905 |
/* close any outstanding PASSIVE port */ |
953 |
/* close any outstanding PASSIVE port */ |
906 |
if (f->data_channel == DATA_PASSIVE) { |
954 |
if (f->data_channel == DATA_PASSIVE_4 || f->data_channel == DATA_PASSIVE_6) { |
907 |
close(f->server_fd); |
955 |
close(f->server_fd); |
908 |
} |
956 |
} |
909 |
f->data_channel = DATA_PASSIVE; |
957 |
f->data_port = *addr; |
|
|
958 |
#ifdef INET6 |
959 |
if (cmd->num_arg == 0) |
960 |
f->data_channel = SAFAM(&f->server_addr) == AF_INET6 ? DATA_PASSIVE_6 : DATA_PASSIVE_4; |
961 |
else |
962 |
#endif |
963 |
f->data_channel = cmd->arg[0].num == 2 ? DATA_PASSIVE_6 : DATA_PASSIVE_4; |
910 |
f->server_fd = socket_fd; |
964 |
f->server_fd = socket_fd; |
911 |
|
965 |
|
912 |
exit_epsv: |
966 |
exit_epsv: |
Lines 1278-1288
static void do_stor(ftp_session_t *f, co
Link Here
|
1278 |
static int open_connection(ftp_session_t *f) |
1332 |
static int open_connection(ftp_session_t *f) |
1279 |
{ |
1333 |
{ |
1280 |
int socket_fd; |
1334 |
int socket_fd; |
1281 |
struct sockaddr_in addr; |
1335 |
sockaddr_storage_t addr; |
1282 |
unsigned addr_len; |
1336 |
unsigned addr_len; |
1283 |
|
1337 |
|
1284 |
daemon_assert((f->data_channel == DATA_PORT) || |
1338 |
daemon_assert((f->data_channel == DATA_PORT) || |
1285 |
(f->data_channel == DATA_PASSIVE)); |
1339 |
(f->data_channel == DATA_PASSIVE_4) || |
|
|
1340 |
(f->data_channel == DATA_PASSIVE_6)); |
1286 |
|
1341 |
|
1287 |
if (f->data_channel == DATA_PORT) { |
1342 |
if (f->data_channel == DATA_PORT) { |
1288 |
socket_fd = socket(SSFAM(&f->data_port), SOCK_STREAM, 0); |
1343 |
socket_fd = socket(SSFAM(&f->data_port), SOCK_STREAM, 0); |
Lines 1298-1306
static int open_connection(ftp_session_t
Link Here
|
1298 |
return -1; |
1353 |
return -1; |
1299 |
} |
1354 |
} |
1300 |
} else { |
1355 |
} else { |
1301 |
daemon_assert(f->data_channel == DATA_PASSIVE); |
1356 |
daemon_assert(f->data_channel == DATA_PASSIVE_4 || f->data_channel == DATA_PASSIVE_6); |
1302 |
|
1357 |
|
1303 |
addr_len = sizeof(struct sockaddr_in); |
1358 |
addr_len = sizeof(addr); |
1304 |
socket_fd = accept(f->server_fd, (struct sockaddr *)&addr, &addr_len); |
1359 |
socket_fd = accept(f->server_fd, (struct sockaddr *)&addr, &addr_len); |
1305 |
if (socket_fd == -1) { |
1360 |
if (socket_fd == -1) { |
1306 |
reply(f, 425, "Error accepting connection; %s.", strerror(errno)); |
1361 |
reply(f, 425, "Error accepting connection; %s.", strerror(errno)); |
Lines 1310-1333
static int open_connection(ftp_session_t
Link Here
|
1310 |
/* in IPv6, the client can connect to a channel using a different */ |
1365 |
/* in IPv6, the client can connect to a channel using a different */ |
1311 |
/* protocol - in that case, we'll just blindly let the connection */ |
1366 |
/* protocol - in that case, we'll just blindly let the connection */ |
1312 |
/* through, otherwise verify addresses match */ |
1367 |
/* through, otherwise verify addresses match */ |
1313 |
if (SAFAM(addr) == SSFAM(&f->client_addr)) { |
1368 |
if (SAFAM(&addr) == SSFAM(&f->client_addr)) { |
1314 |
if (memcmp(&SINADDR(&f->client_addr), &SINADDR(&addr), |
1369 |
if (SAFAM(&addr) == AF_INET6) { |
1315 |
sizeof(SINADDR(&addr)))) |
1370 |
if (memcmp(&SIN6ADDR(&f->client_addr), &SIN6ADDR(&addr), |
1316 |
{ |
1371 |
sizeof(struct in6_addr))) { |
1317 |
reply(f, 425, |
1372 |
reply(f, 425, "Error accepting connection; " |
1318 |
"Error accepting connection; connection from invalid IP."); |
1373 |
"connection from invalid IP."); |
1319 |
close(socket_fd); |
1374 |
close(socket_fd); |
1320 |
return -1; |
1375 |
return -1; |
1321 |
} |
1376 |
} |
1322 |
} |
1377 |
} else { |
1323 |
#else |
1378 |
#endif |
1324 |
if (memcmp(&f->client_addr.sin_addr, |
1379 |
if (memcmp(&SIN4ADDR(&f->client_addr), &SIN4ADDR(&addr), |
1325 |
&addr.sin_addr, sizeof(struct in_addr))) |
1380 |
sizeof(struct in_addr))) { |
1326 |
{ |
1381 |
reply(f, 425, "Error accepting connection; " |
1327 |
reply(f, 425, |
1382 |
"connection from invalid IP."); |
1328 |
"Error accepting connection; connection from invalid IP."); |
1383 |
close(socket_fd); |
1329 |
close(socket_fd); |
1384 |
return -1; |
1330 |
return -1; |
1385 |
} |
|
|
1386 |
#ifdef INET6 |
1387 |
} |
1331 |
} |
1388 |
} |
1332 |
#endif |
1389 |
#endif |
1333 |
} |
1390 |
} |
Lines 1822-1830
static int ip_equal(const sockaddr_stora
Link Here
|
1822 |
if (SSFAM(a) != SSFAM(b)) { |
1879 |
if (SSFAM(a) != SSFAM(b)) { |
1823 |
return 0; |
1880 |
return 0; |
1824 |
} |
1881 |
} |
1825 |
if (memcmp(&SINADDR(a), &SINADDR(b), sizeof(SINADDR(a))) != 0) { |
1882 |
#ifdef INET6 |
1826 |
return 0; |
1883 |
if (SSFAM(a) == AF_INET6) { |
|
|
1884 |
if (memcmp(&SIN6ADDR(a), &SIN6ADDR(b), sizeof(struct in6_addr)) != 0) |
1885 |
return 0; |
1886 |
} else { |
1887 |
#endif |
1888 |
if (memcmp(&SIN4ADDR(a), &SIN4ADDR(b), sizeof(struct in_addr)) != 0) |
1889 |
return 0; |
1890 |
#ifdef INET6 |
1827 |
} |
1891 |
} |
|
|
1892 |
#endif |
1828 |
return 1; |
1893 |
return 1; |
1829 |
} |
1894 |
} |
1830 |
|
1895 |
|