Если вы достаточно давно читаете этот блог, то можете помнить о том, как я решил собрать в нем описание популярных (и не очень) сетевых протоколов. Зачем это мне нужно, можно прочитать в статье Достаточно полное описание протокола SMTP . Вот решил пополнить коллекцию протоколом FTP, повсеместно используемым для передачи файлов.
1. Заходим
По традиции, сразу начну с примера:
Trying 192.168.0.1…
Connected to example.ru.
Escape character is ‘^]’.
220-Welcome to Pure-FTPd
You are user number 5 of 100 allowed.
Local time is now 17:41. Server port: 21.
220 You will be disconnected after 15 minutes of inactivity.
USER afiskon
331 User afiskon OK. Password required
PASS lamepassword
230-User afiskon has group access to: coders
230 OK. Current restricted directory is /
FTP-сервер обычно работает на 21 порту. В приведенном примере строки, начинающиеся с цифр, посылаются сервером, остальные — клиентом. Запросы клиента всегда состоят из одной строки формата КОМАНДА [аргументы] , в то время как ответы сервера могут содержать несколько строк.
Первая и последняя строки начинается с трех цифр, представляющих собой код ответа, за которыми идет текстовое описание ответа, отделенное от кода либо пробелом, либо тире. Если в качестве разделителя используется пробел, значит строка является последней в ответе (и, возможно, единственной), иначе — мы получили первую строку многострочного ответа. Где-то мы это уже видели , не так ли?
Существует пять групп ответов сервера:
Код | Значение |
1xx | Команда принята и в настоящее время идет ее выполнение. |
2xx | Команда принята и успешно выполнена. |
3xx | Команда принята, требуется дополнительная команда. |
4xx | Команда не может быть принята в данный момент. |
5xx | Невозможно выполнить команду. |
Как видно из примера, все начинается с посылки сервером кода 220. Затем пользователь должен залогиниться с помощью команд USER и PASS. Если все сделано правильно, на первую сервер ответит кодом 331, а на вторую — 230. Для анонимного входа (если он разрешен настройками сервера), в качестве имени пользователя следует указать «anonymous», а в качестве пароля — свой e-mail. На практике обычно посылается либо пустой e-mail, либо что-то типа root@example.ru.
Как видно, пароль передается в открытом виде, потому крайне желательно шифровать FTP-соединение с помощью SSL (это называется FTPS — FTP плюс SSL), а еще лучше — передавать файлы по SSH с помощью утилит scp, sftp или WinSCP . Первые две есть в любой unix-системе и используют для передачи файлов одноименные протоколы, работающие поверх SSH. WinSCP написан для Windows и внешне напоминает Total Commander, умеет работать как с устаревшим SCP (Secure Copy), так и SFTP (SSH File Transfer Protocol), появившимся только в SSH-2.
2. Осматриваемся
Но что-то меня не в ту степь понесло. После прохождения аутентификации (надо же, больше не путаю с авторизацией ) FTP-сервер с радостью выполнит наши команды. Вот их список:
Команда | Ожидаемый код | Описание |
DELE | 250 | Удалить файл |
RMD | 250 | Удалить директорию |
CWD | 250 | Перейти в директорию |
MKD | 257 | Создать директорию |
PWD | 257 | Узнать текущую директорию |
QUIT | 221 | Закончить работу |
TYPE | 200 | Установить тип передачи |
PORT | 200 | Перейти в активный режим |
PASV | 227 | Перейти в пассивный режим |
LIST | 150, 226 | Получить содержимое каталога |
RETR | 150, 226 | Скачать файл |
STOR | 150, 226 | Залить файл |
ABOR | 426,226 | Отменить передачу |
RNFR | 350 | Выбрать файл для переименования |
RNTO | 250 | Переименовать файл |
Здесь я перечислил лишь основные команды, которых достаточно для написания полноценного FTP-клиента. Дело в том, что в реальных условиях FTP-серверы очень избирательно относятся к поддержке команд, описанных в RFC959 и RFC3659 . Так что, если мы хотим получить действительно работающее приложение, а не сферического коня в вакууме, придется ограничиться лишь командами из приведенного списка.
Самые простые команды — это QUIT, DELE, MKD, CWD и RMD . Просто командуем и проверяем код, возвращаемый сервером. Если он равен ожидаемому, значит все ОК, если нет — обрабатываем ошибку.
257 «ftp_test» : The directory was successfully created
CWD ftp_test
250 OK. Current directory is /ftp_test
CWD ..
250 OK. Current directory is /
RMD ftp_test
250 The directory was successfully removed
Если бы я писал FTP-клиент, то код, отвечающий за выполнение названных команд, выглядел бы примерно так:
char * dir ;
// …
if ( code = rawcmd ( 250 , «RMD %s r n » , dir ) )
printf ( «Error: %d n » , code ) ;
else
printf ( «All done! n » ) ;
Чуть сложнее с парсингом ответа сервера на команду PWD :
257 «/ftp_test» is your current location
Текущая директория передается в единственной (последней?) строке ответа сервера, заключенная в двойные кавычки. Если полное имя текущей директории содержит двойные кавычки, они заменяются на две кавычки:
257 «/ftp»»test» is your current location
Для переименования файлов используется пара команд — RNFR и RNTO :
350 Are you kidding?
RNTO new_file.zip
250 Done!
По всей видимости, это такая оптимизация, чтобы буфер, в который сервер читает команды клиента, был порядка максимально допустимой длины полного имени файла, а не в два раза больше. В 1971-м году, когда был создан протокол, это могло быть важно.
Команда TYPE позволяет установить режим передачи файлов. Пример:
200 TYPE is now EBCDIC
TYPE A
200 TYPE is now ASCII
TYPE I
200 TYPE is now 8-bit binary
Насколько я могу судить, сегодня эта команда уже устарела и все данные можно спокойно передавать в бинарном формате (TYPE I). Цитата из Википедии :
Первые компьютеры использовали формат, размером в байт, машинное слово, двойное машинное слово, не кратное 8. Обычно они были кратны шести. Восемь бит в байте было принято при разработке системы машинных команд для IBM System/360. Это стало международным стандартом и с начала 1970-х большинство компьютеров использует байты, состоящие из 8 бит, и машинные слова, кратные 8.
3. Действуем
Особенность протокола FTP — для выполнения команд и передачи файлов используются разные соединения. Это в общем-то нормальное проектное решение. Мы же не знаем, что там в этих файлах записано, а если передавать их вместе с командами, придется как-то кодировать содержимое файла, чтобы отличить его от команд. Зачем увеличивать объем трафика и усложнять протокол, когда можно просто открыть новое соединение и послать файл, как есть?
При установлении нового соединения кто-то должен собственно соединяться, а кто-то соединение принимать. Если клиент открывает порт, а сервер коннектится к нему, режим передачи файла называется активным. В обратном случае — пассивным. За счет того, что многие пользователи Интернета сегодня сидят за NAT, обычно используется пассивный режим. И это не очень хорошо, потому что число портов у сервера ограничено.
Что интересно — существует возможность передавать файлы с одного FTP-сервера на другой напрямую. Но поскольку такая возможность часто использовалась в DDoS-атаках, сейчас она практически везде отключена.
Для перехода в пассивный режим используется команда PASV , для перехода в активный — PORT :
200 PORT command successful
PASV
227 Entering Passive Mode (192,168,0,1,21,216)
Как несложно догадаться, с помощью цифр кодируется IP-адрес и порт для соединения. Допустим, мы находимся в пассивном режиме и хотим установить соединение для передачи данных:
Trying 192.168.0.1…
Connected to example.ru.
Escape character is ‘^]’.
После чего можем, например, просмотреть содержимое текущего каталога, заюзав команду LIST :
150 Accepted data connection
226-Options: -a -l
226 5 matches total
Смотрим на вывод telnet:
drwx—— 5 afiskon coders 512 Jul 7 11:35 ..
drwxr—r— 3 afiskon coders 512 Jun 6 14:30 remontka.com
drwxr-xr-x 2 afiskon coders 1024 Jul 7 00:16 logs
drwxr—r— 2 afiskon coders 512 Jun 6 14:30 tmp
Connection closed by foreign host.
Абсолютно аналогично происходит скачивание и аплоад файлов, только используются команды RETR {файл} и STOR {файл} соответственно. Команды RETR, STOR и LIST можно прервать в процессе выполнения с помощью команды ABOR , в ответ на которую сервер должен ответить 426 «передача прервана», а затем — 226 «отмена операции произошла успешно».
4. Заключение
На этом я, пожалуй, закончу свое повествование. Получилось 9 Кб текста против 130 Кб RFC959 . По этой статье вполне можно написать несложный FTP клиент или сервер, я проверял! Самое главное — это протестировать его на совместимость с максимально возможным количеством ПО, поскольку, как я уже отмечал, в мире FTP мало кто строго следует RFC.
Дополнение: Вас также может заинтересовать заметка Памятка по использованию WebDAV