FTP(File Transfer Protocol, FTP)是TCP/IP网络上两台计算机传送文件的协议,FTP是在TCP/IP网络和INTERNET上最早使用的协议之一,它属于网络协议组的应用层。FTP客户机可以给服务器发出命令来下载文件,上载文件,创建或改变服务器上的目录。
FTP是应用层的协议,它基于传输层,为用户服务,它们负责进行文件的传输。FTP是一个8位的客户端-服务器协议,能操作任何类型的文件而不需要进一步处理,就像MIME或Unencode一样。但是,FTP有着极高的延时,这意味着,从开始请求到第一次接收需求数据之间的时间会非常长,并且不时的必需执行一些冗长的登陆进程。 FTP服务一般运行在20和21两个端口。端口20用于在客户端和服务器之间传输数据流,而端口21用于传输控制流,并且是命令通向ftp服务器的进口。当数据通过数据流传输时,控制流处于空闲状态。而当控制流空闲很长时间后,客户端的防火墙会将其会话置为超时,这样当大量数据通过防火墙时,会产生一些问题。此时,虽然文件可以成功的传输,但因为控制会话会被防火墙断开,传输会产生一些错误。
FTP实现的目标
优点:
1.促进文件的共享(计算机程序或数据)
2.鼓励间接或者隐式的使用远程计算机
3.向用户屏蔽不同主机中各种文件存储系统的细节
4.可靠和高效的传输数据
缺点:
1.密码和文件内容都使用明文传输,可能产生不希望发生的窃听。
2.因为必须开放一个随机的端口以建立连接,当防火墙存在时,客户端很难过滤处于主动模式下的FTP流量。这个问题通过使用被动模式的FTP得到了很大解决。
3.服务器可能会被告知连接一个第三方计算机的保留端口。
FTP虽然可以被终端用户直接使用,但是它是设计成被FTP客户端程序所控制。
运行FTP服务的许多站点都开放匿名服务,在这种设置下,用户不需要帐号就可以登录服务器,默认情况下,匿名用户的用户名是:“anonymous”。这个帐号不需要密码,虽然通常要求输入用户的邮件地址作为认证密码,但这只是一些细节或者此邮件地址根本不被确定,而是依赖于FTP服务器的配置情况。
主动和被动模式
FTP有两种使用模式:主动和被动。主动模式要求客户端和服务器端同时打开并且监听一个端口以建立连接。在这种情况下,客户端由于安装了防火墙会产生一些问题。所以,创立了被动模式。被动模式只要求服务器端产生一个监听相应端口的进程,这样就可以绕过客户端安装了防火墙的问题。
一个主动模式的FTP连接建立要遵循以下步骤:
1.客户端打开一个随机的端口(端口号大于1024,在这里,我们称它为x),同时一个FTP进程连接至服务器的21号命令端口。此时,源端口为随机端口x,在客户端,远程端口为21,在服务器。
2.客户端开始监听端口(x+1),同时向服务器发送一个端口命令(通过服务器的21号命令端口),此命令告诉服务器客户端正在监听的端口号并且已准备好从此端口接收数据。这个端口就是我们所知的数据端口。
3.服务器打开20号源端口并且建立和客户端数据端口的连接。此时,源端口为20,远程数据端口为(x+1)。
4.客户端通过本地的数据端口建立一个和服务器20号端口的连接,然后向服务器发送一个应答,告诉服务器它已经建立好了一个连接。
被动模式FTP:
为了解决服务器发起到客户的连接的问题,人们开发了一种不同的FTP连接方式。这就是所谓的被动方式,或者叫做PASV,当客户端通知服务器它处于被动模式时才启用。
在被动方式FTP中,命令连接和数据连接都由客户端发起,这样就可以解决从服务器到客户端的数据端口的入方向连接被防火墙过滤掉的问题。
当开启一个 FTP连接时,客户端打开两个任意的非特权本地端口(N > 1024和N+1)。第一个端口连接服务器的21端口,但与主动方式的FTP不同,客户端不会提交PORT命令并允许服务器来回连它的数据端口,而是提交 PASV命令。这样做的结果是服务器会开启一个任意的非特权端口(P > 1024),并发送PORT P命令给客户端。然后客户端发起从本地端口N+1到服务器的端口P的连接用来传送数据。
对于服务器端的防火墙来说,必须允许下面的通讯才能支持被动方式的FTP:
1. 从任何大于1024的端口到服务器的21端口 (客户端的初始化连接)
2. 服务器的21端口到任何大于1024的端口 (服务器响应到客户端的控制端口的连接)
3. 从任何大于1024端口到服务器的大于1024端口 (客户端初始化数据连接到服务器指定的任意端口)
4. 服务器的大于1024端口到远程的大于1024的端口(服务器发送ACK响应和数据到客户端的数据端口)
FTP和网页浏览器
大多数最新的网页浏览器和文件管理器都能和FTP服务器建立连接。这使得在FTP上通过一个接口就可以操控远程文件,如同操控本地文件一样。这个功能通过给定一个FTP的URL实现,形如ftp://<服务器地址>。是否提供密码是可选择的,如果有密码,则形如ftp://
网络协议
应用层DNS, FTP, ENRP,HTTP, IMAP, IIRC, NNTP, POP3, SIP, SMTP, SNMP, SSH, Telnet, BitTorrent, DHCP...
传输层DCCP, SCTP, TCP, RTP, UDP, IL, RUDP, ...
网络层IPv4, IPv6...
数据链路层以太网, Wi-Fi, 令牌环, MPLS, PPP...
物理层RS-232, EIA-422, RS-449, EIA-485, 10BASE2, 10BASE-T...
FTP和网站
我们都知道,当我们需要往网站空间上放网站文件的时候,我们可以采用WEB和FTP两种方法。在这里,我们建议直接使用FTP进行数据交换,因为不管是安全性还是快捷性来说,ftp都是很不错的。
那么我们怎么往空间上传送网站的数据文件呢,这时,我们就需要一个软件FlashFXP或者其他FTP客户端。这里我们以FlashFXP为例,我们去网上下载这个软件包并解压出来,双击FlashFXP.exe这个文件,进入页面之后,有一个闪电符号的按钮,这是连接。单击或者直接按F8,这时会出来一个对话框,
我们只需要输入网站的URL或者IP,然后再输入用户名和密码就行,这时,我们就可以进行网站数据文件的传输了。
FTP用户授权
(1)用户授权
要连上 FTP 服务器(即“登陆”),必须要有该 FTP 服务器授权的帐号,也就是说你只有在有了一个用户标识和一个口令后才能登陆FTP服务器,享受FTP服务器提供的服务。
(2)FTP地址格式
FTP地址如下: ftp://用户名:密码@FTP服务器IP或域名:FTP命令端口/路径/文件名
上面的参数除FTP服务器IP或域名为必要项外,其他都不是必须的。
#include "nids.h"
char ascii_string[10000];
char *char_to_ascii(char ch)
{
char *string;
ascii_string[0] = 0;
string = ascii_string;
if (isgraph(ch))
*string++ = ch;
else if (ch == ' ')
*string++ = ch;
else if (ch == '\n' || ch == '\r')
*string++ = ch;
else
*string++ = '.';
*string = 0;
return ascii_string;
}
/*
=======================================================================================================================
下面是分析FTP协议的回调函数
=======================================================================================================================
*/
void ftp_protocol_callback(struct tcp_stream *ftp_connection, void **arg)
{
int i;
char address_string[1024];
char content[65535];
char content_urgent[65535];
struct tuple4 ip_and_port = ftp_connection->addr;
strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);
strcat(address_string, " <---> ");
strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);
strcat(address_string, "\n");
switch (ftp_connection->nids_state)
{
case NIDS_JUST_EST:
if ((ftp_connection->addr.dest == 21) || (ftp_connection->addr.source == 20))
{
/* FTP客户端和FTP服务器端建立连接 */
ftp_connection->client.collect++;
/* FTP客户端接收数据 */
ftp_connection->server.collect++;
/* FTP服务器接收数据 */
ftp_connection->server.collect_urg++;
/* FTP服务器接收紧急数据 */
ftp_connection->client.collect_urg++;
/* FTP客户端接收紧急数据 */
if (ftp_connection->addr.dest == 21)
printf("%s FTP客户端与FTP服务器建立控制连接\n", address_string);
if (ftp_connection->addr.source == 20)
printf("%s FTP服务器与FTP客户端建立数据连接\n", address_string);
}
return ;
case NIDS_CLOSE:
/* FTP客户端与FTP服务器端连接正常关闭 */
printf("--------------------------------\n");
if (ftp_connection->addr.dest == 21)
printf("%sFTP客户端与FTP服务器的控制连接正常关闭\n", address_string);
if (ftp_connection->addr.source == 20)
printf("%sFTP服务器与FTP客户端的数据连接正常关闭\n", address_string);
return ;
case NIDS_RESET:
/* FTP客户端与FTP服务器端连接被RST关闭 */
printf("--------------------------------\n");
if (ftp_connection->addr.source == 20)
printf("%sFTP服务器与FTP客户端的数据连接被RESET关闭\n", address_string);
if (ftp_connection->addr.dest == 21)
printf("%sFTP客户端与FTP服务器的控制连接被REST关闭\n", address_string);
return ;
case NIDS_DATA:
{
/* FTP协议有新的数据达到 */
struct half_stream *hlf;
if (ftp_connection->server.count_new_urg)
{
/* FTP服务器接收到新的紧急数据 */
printf("--------------------------------\n");
strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);
strcat(address_string, " urgent---> ");
strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);
strcat(address_string, "\n");
address_string[strlen(address_string) + 1] = 0;
address_string[strlen(address_string)] = ftp_connection->server.urgdata;
printf("%s", address_string);
return ;
}
if (ftp_connection->client.count_new_urg)
{
/* FTP客户端接收到新的紧急数据 */
printf("--------------------------------\n");
strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.source);
strcat(address_string, " <--- urgent ");
strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), " : %i", ip_and_port.dest);
strcat(address_string, "\n");
address_string[strlen(address_string) + 1] = 0;
address_string[strlen(address_string)] = ftp_connection->client.urgdata;
printf("%s", address_string);
return ;
}
if (ftp_connection->client.count_new)
{
/* FTP客户端接收到新的数据 */
hlf = &ftp_connection->client;
strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), ":%i", ip_and_port.source);
strcat(address_string, " <--- ");
strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), ":%i", ip_and_port.dest);
strcat(address_string, "\n");
printf("--------------------------------\n");
printf("%s", address_string);
/* 输出FTP客户端接收到的新的数据 */
memcpy(content, hlf->data, hlf->count_new);
content[hlf->count_new] = '\0';
if (ftp_connection->addr.source == 20)
{
printf("传输的数据为:\n");
for (i = 0; i < hlf->count_new; i++)
{
printf("%s", char_to_ascii(content[i]));
}
printf("\n");
}
else
{
if (content[0] == '1' || content[0] == '2' || content[0] == '3' || content[0] == '4' || content[0] == '5')
printf("FTP服务器响应状态代码为:%c%c%c\n", content[0], content[1], content[2]);
if (strncmp(content, "220", 3) == 0)
printf("新连接的用户的服务已经准备就绪\n");
if (strncmp(content, "110", 3) == 0)
printf("启动标记应答\n");
if (strncmp(content, "120", 3) == 0)
printf("表示 服务在nnn分钟内可用\n");
if (strncmp(content, "125", 3) == 0)
printf("表示数据连接已打开,准备传送\n");
if (strncmp(content, "150", 3) == 0)
printf("表示文件状态正确,正在打开数据连接\n");
if (strncmp(content, "200", 3) == 0)
printf("表示命令正常执行\n");
if (strncmp(content, "202", 3) == 0)
printf("表示命令未被执行,此站点不支持此命令\n");
if (strncmp(content, "211", 3) == 0)
printf("表示系统状态或系统帮助响应\n");
if (strncmp(content, "212", 3) == 0)
printf("表示目录状态信息\n");
if (strncmp(content, "213", 3) == 0)
printf("表示文件状态信息\n");
if (strncmp(content, "214", 3) == 0)
printf("表示帮助信息\n");
if (strncmp(content, "215", 3) == 0)
printf("表示名字系统类型\n");
if (strncmp(content, "221", 3) == 0)
printf("表示控制连接已经被关闭\n");
if (strncmp(content, "225", 3) == 0)
printf("表示数据连接已经打开,没有数据传输\n");
if (strncmp(content, "226", 3) == 0)
printf("表示数据连接已经关闭,请求文件操作成功完成\n");
if (strncmp(content, "227", 3) == 0)
printf("表示进入被动模\n");
if (strncmp(content, "230", 3) == 0)
printf("表示用户已经登录\n");
if (strncmp(content, "250", 3) == 0)
printf("表示请求文件操作已经成功执行\n");
if (strncmp(content, "257", 3) == 0)
printf("表示创建路径名字\n");
if (strncmp(content, "331", 3) == 0)
printf("表示用户名正确,需要输入密码\n");
if (strncmp(content, "332", 3) == 0)
printf("表示 登录时需要帐户信息\n");
if (strncmp(content, "350", 3) == 0)
printf("表示对请求的文件操作需要更多的指令\n");
if (strncmp(content, "421", 3) == 0)
printf("表示服务不可用,关闭控制连接\n");
if (strncmp(content, "425", 3) == 0)
printf("表示打开数据连接操作失败\n");
if (strncmp(content, "426", 3) == 0)
printf("表示关闭连接,中止传输\n");
if (strncmp(content, "450", 3) == 0)
printf("表示请求的文件操作未被执行\n");
if (strncmp(content, "451", 3) == 0)
printf("表示请求操作中止,有本地错误发生\n");
if (strncmp(content, "452", 3) == 0)
printf("表示未执行请求的操作,系统存储空间不足 ,文件不可用\n");
if (strncmp(content, "500", 3) == 0)
printf("表示语法错误,命令错误\n");
if (strncmp(content, "501", 3) == 0)
printf("表示参数的语法错误\n");
if (strncmp(content, "502", 3) == 0)
printf("表示命令未被执行\n");
if (strncmp(content, "503", 3) == 0)
printf("表示命令顺序发生错误\n");
if (strncmp(content, "504", 3) == 0)
printf("表示由于参数而发生错误命令\n");
if (strncmp(content, "530", 3) == 0)
printf("表示未登录\n");
if (strncmp(content, "532", 3) == 0)
printf("表示存储文件需要帐户信息\n");
if (strncmp(content, "550", 3) == 0)
printf("表示未执行请求的操作,文件不可用\n");
if (strncmp(content, "551", 3) == 0)
printf("表示请求操作中止,页面类型未知\n");
if (strncmp(content, "552", 3) == 0)
printf("表示请求的文件操作中止,超出存储分配空间\n");
if (strncmp(content, "553", 3) == 0)
printf("表示未执行请求的操作,文件名不合法\n");
for (i = 0; i < hlf->count_new; i++)
{
printf("%s", char_to_ascii(content[i]));
}
printf("\n");
}
}
else
{
/* FTP服务器接收到新的数据 */
hlf = &ftp_connection->server;
strcpy(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.saddr))));
sprintf(address_string + strlen(address_string), ":%i", ip_and_port.source);
strcat(address_string, " ---> ");
strcat(address_string, inet_ntoa(*((struct in_addr*) &(ip_and_port.daddr))));
sprintf(address_string + strlen(address_string), ":%i", ip_and_port.dest);
strcat(address_string, "\n");
printf("--------------------------------\n");
printf("%s", address_string);
/* 输出FTP服务器端接收到的新的数据 */
memcpy(content, hlf->data, hlf->count_new);
content[hlf->count_new] = '\0';
if (ftp_connection->addr.source == 20)
printf("FTP服务器向FTP客户端发送数据\n");
else
{
if (strstr(content, "USER"))
printf("用户名字为(USER):%s\n", content + strlen("USER"));
else if (strstr(content, "PASS"))
printf("用户密码为(PASS):%s\n", content + strlen("PASS"));
else if (strstr(content, "PORT"))
printf("端口参数为(PORT):%s\n", content + strlen("PORT"));
else if (strstr(content, "LIST"))
printf("显示文件列表(LIST):%s\n", content + strlen("LIST"));
else if (strstr(content, "CWD"))
printf("改变工作目录为(CWD):%s\n", content + strlen("CWD"));
else if (strstr(content, "TYPE"))
printf("类型为(TYPE):%s\n", content + strlen("TYPE"));
else if (strstr(content, "RETR"))
printf("获取文件为(RETR):%s\n", content + strlen("RETR"));
else if (strstr(content, "STOR"))
printf("保存文件为(STOR):%s\n", content + strlen("STOR"));
else if (strstr(content, "XRMD"))
printf("删除目录(XRMD):%s\n", content + strlen("XRMD"));
else if (strstr(content, "QUIT"))
printf("退出登陆(QUIT):%s\n", content + strlen("QUIT"));
else
printf("FTP客户端使用的命令为 %c%c%c%c\n", content[0], content[1], content[2], content[3]);
}
for (i = 0; i < hlf->count_new; i++)
{
printf("%s", char_to_ascii(content[i]));
}
printf("\n");
}
}
default:
break;
}
return ;
}
/*
=======================================================================================================================
主函数
=======================================================================================================================
*/
void main()
{
if (!nids_init())
/* Libnids初始化 */
{
printf("出现错误:%s\n", nids_errbuf);
exit(1);
}
nids_register_tcp(ftp_protocol_callback);
/* 注册回调函数 */
nids_run();
/* 进入循环捕获数据包状态 */
}