FTP服务器第三部分(最大连接数的限制以及20端口的绑定)

第三部分:FTP服务器最大连接数的限制

服务端的最大连接数

将当前的连接数保存在会话结构体成员中的max_clients,当有一个客户登录的时候对应的max_clients的值会加1。客户退出的时候,max_client的值会减少1个。
需要注意的是,当前的max_clients的值如果大于配置文件中的tunable_max_clients,那么客户端就无法登录。
连接的限制的函数是位于主函数miniftp.c中。
代码如下所示:

while(1)
{
	sockConn = accept(listenfd, (struct sockaddr*)&addrCli, &addrlen);
	if(sockConn < 0)
	{
		perror("accept");
		continue;
	}
	//最大连接数
	s_children_nums++;
	sess.max_clients = s_children_nums;
	pid_t pid = fork();
	// 进程创建失败的话,此时的连接数需要减少1.
	if(pid == -1)
	{
		s_children_nums--;
		ERR_EXIT("fork");
	}

	if(pid == 0)
	{
		//Child Process
		close(listenfd);
		sess.ctrl_fd = sockConn;
		//连接限制
		check_limit(&sess);
		begin_session(&sess);
		exit(EXIT_SUCCESS);
	}
	else
	{
		close(sockConn);
	}
}
	

handle_limit函数如下所示:

static void check_limit(session_t *sess)
{
	if(tunable_max_clients!=0 && sess->max_clients>tunable_max_clients)
	{
		//421 There are too many connected users, please try later.
		ftp_reply(sess, FTP_TOO_MANY_USERS, "There are too many connected users, please try later.");
		exit(EXIT_FAILURE);
	}
}

客户端退出时,对应的max_client需要减少。要实现这个功能就需要对子进程进行回收,即nobody进程退出之后,会向主进程发送SIGCHID信号,为该信号定义自定义处理函数sig_child,在该函数内部将client的数值减去1即可。

服务端的每ip连接数

服务器的每ip连接数是使用hash表来实现的。hash表的具体实现详见下面这篇博客。本文不再赘述原理部分,直接使用函数接口开发相应的功能。
在这里插入图片描述
这里需要注意的是:

  1. 必须使用两个hash表来维护,因为进程退出之后,对应的count值需要减一,我们可以很容易的建立ip到count的正向关系。一旦子进程退出之后,父进程只能通过waitpid函数获取到子进程对应的pid号,我们可以建立子进程的PID和IP的对应关系,继而再通过ip和count的关系实现对count的减少。
  2. 在获取客户端的ip地址时,需要对addrlen进行初始化,因为自己写代码的时候没有对socklen_t addrlen进行初始化,导致一开始的客户端ip地址获取错误。
  3. 添加pid和ip的映射关系是在父进程中完成滴。
  4. 增加ip和count的映射关系定义了handle_ip_count函数。
  5. 删除ip和count的映射关系定义了drop_ip_count函数

代码如下所示:

//申请hash表
s_ip_count_hash = hash_alloc(MAX_BUCKET_SIZE, hash_func);
s_pid_ip_hash = hash_alloc(MAX_BUCKET_SIZE, hash_func);
while(1)
	{
		sockConn = accept(listenfd, (struct sockaddr*)&addrCli, &addrlen);
		if(sockConn < 0)
		{
			perror("accept");
			continue;
		}
		//最大连接数
		s_children_nums++;
		sess.max_clients = s_children_nums;
		
		//每IP连接数
		unsigned int ip = addrCli.sin_addr.s_addr;
		sess.max_per_ip = handle_ip_count(&ip);
		pid_t pid = fork();
		// 进程创建失败的话,此时的连接数需要减少1.
		if(pid == -1)
		{
			s_children_nums--;
			ERR_EXIT("fork");
		}
		if(pid == 0)
		{
			//Child Process
			close(listenfd);
			sess.ctrl_fd = sockConn;

			//连接限制
			check_limit(&sess);

			begin_session(&sess);
			exit(EXIT_SUCCESS);
		}
		//父进程
		else
		{
			//Parent Process
			//增加pid跟ip的映射
			hash_add_entry(s_pid_ip_hash, &pid, sizeof(pid), &ip, sizeof(ip));

			close(sockConn);
		}

信号处理函数如下所示:

static void handle_sigchld(int sig)
{
	//减少最大连接数
	s_children_nums--;
	//减少每ip的连接数
	pid_t pid;
	while((pid = waitpid(-1, NULL, WNOHANG)) > 0)
	{
		unsigned int *ip = (unsigned int *)hash_lookup_entry(s_pid_ip_hash, &pid, sizeof(pid));
		if(ip == NULL)
			continue;
		drop_ip_count(ip);
		// 由于当前的子进程已经退出咯,因此删除对应的pid和ip的哈希结点。
		hash_free_entry(s_pid_ip_hash, &pid, sizeof(pid));
	}
}
unsigned int hash_func(unsigned int buket_size, void *key)
{
	return (*(unsigned int*)key) % buket_size;
}

unsigned int handle_ip_count(void *ip)
{
	unsigned int *p_count = (unsigned int *)hash_lookup_entry(s_ip_count_hash, ip, sizeof(unsigned int));
	if(p_count == NULL)
	{
		unsigned int count = 1;
		hash_add_entry(s_ip_count_hash, ip, sizeof(unsigned int), &count, sizeof(unsigned int));
		return count;
	}

	(*p_count)++;
	return *p_count;
}

void drop_ip_count(unsigned int *ip)
{
	unsigned int *p_count = (unsigned int*)hash_lookup_entry(s_ip_count_hash, ip, sizeof(unsigned int));
	if(p_count == NULL)
		return;
	//查找到对应的ip地址,对应的count要减一。
	(*p_count)--;
	// 当前ip用户减少为0,所以删除对应的hash表。
	if(*p_count == 0)
		hash_free_entry(s_ip_count_hash, ip, sizeof(unsigned int));
}

绑定20端口

需要注意的是,nobody进程没有权限去绑定20端口的,需要提升权限到root之后,才可以绑定20端口。
使用capset这个函数,对进程进行能力的限制。

int capset(cap_user_header_t hdrp, const cap_user_data_t datap)
{	
	//通过系统调用函数
	return syscall(__NR_capset, hdrp, datap);
}
static void minimize_privilege()
{
	//更改nobody进程
	struct passwd *pwd = getpwnam("nobody"); //lyx
	if(pwd == NULL)
		ERR_EXIT("getpwnam");
	if(setegid(pwd->pw_gid) < 0)
		ERR_EXIT("setegid");
	if(seteuid(pwd->pw_uid) < 0)
		ERR_EXIT("seteuid");

	struct __user_cap_header_struct cap_header;
	struct __user_cap_data_struct   cap_data;
	memset(&cap_header, 0, sizeof(cap_header));
	memset(&cap_data,   0, sizeof(cap_data));

	//设置头结构
	cap_header.version = _LINUX_CAPABILITY_VERSION_2;
	cap_header.pid = 0; //提升为root用户
	//设置数据结构
	unsigned int cap_mask = 0;
	// 提升相应的权限
	cap_mask |= (1<<CAP_NET_BIND_SERVICE); // 0000 0000 0000 0000 1000 0000 0000 0000 
	cap_data.effective = cap_data.permitted = cap_mask;
	cap_data.inheritable = 0;
	//设置特殊能力
	capset(&cap_header, &cap_data);
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FTP服务器的架设 在我们的实际网络生活中,特别是宽带网接入之后,FTP服务器作为文件的传输和共享工具得到广泛应用。FTP服务器在文件的传输上性能稳定,占用系统资源小,而且传输速度快,现在网上已经有很多的FTP服务器可供使用,而自己架设一个FTP服务器也很容易,下面介绍两种主流的FTP架构方式。 1.利用微软公司的IIS 微软的IIS功能非常强大,它除了提供WWW服务之外,还提供FTP的服务,利用它一样很容易就能架设一个功能卓越的FTP服务器。 IIS的安装前面已经讲解过,下面我们一起来看看通过设置IIS来架设FTP服务器的几个步骤。 第一步:启动IIS,并启动IIS上的FTP服务。在默认的情况下,此时你的FTP服务器已经搭建好,并且可以立即登录,但是该FTP中没有任何文件。 第二步:鼠标右击IIS中的"默认FTP站点"项,选择"属性"菜单,即可出现如图19的对话框。 第三步:选择"主目录"的标签,在FTP站点目录的"本地路径"处填上你要设置的共享文件路径。默认情况下,此处的文件夹位置为"C:\Inetpub\Ftproot",你如果临时想改变共享目录,随时都可在此处修改(如图20),以后别人登录你的FTP服务器时显示的文件列表就是在这个目录中。 第三步:在"主目录"的标签处,你还可设置FTP服务器的文件访问权限,分别有读取、写入和日志访问,安全起见,这里的写入权限一般不选,保证匿名用户不能随意对你文件进行操作。 第四步:设置登录的用户。如果你愿意提供"匿名"的访问权限,还需在"安全标签"处选择上"允许匿名连接"(如图21)。此外,你还可从Windows系统帐号中选择FTP服务器的特殊帐号,当然也可以自己任意设置用户名和密码。 第五步:在"消息"标签处,有"欢迎"、"退出"和"最大连接数"3个输入框,分别代表别人在登录、退出时FTP服务器上给出的提示信息,你可根据自己的需要设置。此外,最大连接数是设置同时连接本地FTP最大主机台数(如图22)。 第六步:在"FTP站点"的标签处设置FTP标识,包括说明、IP地址和端口,这里一般不需要改动,按照默认选择即可(如图23)。此外,在"C:\Winnt\System32\Logfiles"目录中你还可以看到连接上你FTP的IP、时间等日志信息。 此时,利用IE或者任何一款FTP的客户端软件即可登录你架设好的FTP站点。 2.利用Serv-U 在FTP服务器的搭建中,Serv-U是目前使用比较多的工具之一。它设置简单,功能强大,而且非常稳定,总体上来说上它比IIS附带的FTP服务器略胜一筹。它适用于所有的Windows版本,是一款共享软件,可以让用户免费使用一个月。 第一步:Serv-U的下载和安装。 目前Serv-U的最高版本为4.0,文件大小为2.9MB,它的下载站点之一为"http://www.download.com.cn/show.phtml?action=detail&id=484"。 点击下载的可执行文件即可开始安装了,安装过程很简单,所有设置保持默认值就可以,按"Next"完成每一步(如图24)。安装完毕,在Windows的桌面上就出现Serv-U的图标,双击Serv-U图标,出现Serv-U主窗口,点击主界面右边窗口的"Start server"即可启动FTP服务器 。 第二步:Serv-U的配置。 在Serv-U的安装完成之后即可出现配置向导,可以通过这个向导来对它进行配置,主要步骤如下。 A.安装程序首先启动一个设置向导帮助你设置FTP服务器,点击"Next"继续。 B.系统弹出输入IP的对话框,此项需要填入你准备为此FTP服务器绑定的IP地址。除非你的计算机有多个固定的IP地址,并且你只想其中一个被FTP服务器所使用,否则,建议不管你是否有固定的IP地址,都将此项保留为空(即让系统自动侦测),点击"Next"(如图25)。 C.输入Domain name(域名)。此处填入你FTP服务器的域名。但域名由DNS解析而不是由这里决定,因此实际上你可以填入任意内容,比如像"我的个人FTP服务器"这种对此FTP进行说明的文字。 D.输入FTP访问的端口号,一般保持默认的21即可。 E.匿名用户的创建和访问目录的设定。首先向导会提示你是否要创建匿名帐号,这里选择是,如果选择不,则用户需要用户名和密码才能访问FTP服务器。接下来安装向导提示输入匿名用户的主目录(Anonymous home directory),此处按照需要来设定匿名用户访问硬盘的位置。 F.选择匿名用户是否将其限制在主目录里,如果选择是,则用户只能访问其主目录及以下的目录树;如果选择否,则可以访问其主目录的同级或更高级的目录树。从安全角度考虑,一般建议选是。 G.创建新用户和访问目录。按照向导的提示一步一步
FTP服务器配置 VSFTP主配置文件路径:/etc/vsftpd/vsftpd.conf,重要参数: anonymous_enable=yes/no 是否允许匿名用户访问 anon_upload_enable=yes/no 是否允许匿名用户上传文件 anon_mkdir_write_enable=yes/no 是否允许匿名用户创建目录 anon_other_write_enable=yes/no 匿名用户和虚拟用户是否拥有删除权限 local_enable=yes/no 是否允许本地用户登陆 write_enable=yes/no 设置全局是否可写 anon_root=/var 指定匿名用户目录 chroot_local_user=yes 锁定所有用户到用户主目录 chroot_list_enable=yes/no 锁定列表中的用户到主目录,需要配合下一参数使用 chroot_list_file=/etc/vsftpd/chroot_list 指定存储被锁定用户的列表文件位置 chown_uploads=yes/no 匿名用户上传所有者指定功能,需要与下一参数配合使用 chown_username=用户名 指定匿名用户上传文件的所有者 max_clients=300 最大客户端连接数为300 anon_max_rate=30000 匿名用户和虚拟用户限速为30K/S local_max_rate=30000 本地用户限速为30K/S max_per_ip=10 每个IP最大连接数 listen_port=22 更改监听端口 实现如下要求:允许匿名用户登陆,匿名用户限速为60K/S,只允许下载。监听端口为22,最大连接数为10。新建用户ftp1,限速为200K/S,允许上传下载删除新建文件夹。进入目录/etc/vsftpd,用vi编辑器打开vsftpd.conf主配置文件: 直接添加以下选项: [root@LidadeFedora vsftpd]# service vsftpd restart 添加用户ftp1,设置登录脚本为 /sbin/nologin: vsftp默认目录为:/var/ftp,为方便测试,在/var/ftp下新建一个文件"testLocal",在"/var/ftp/pub"新建一个文件"testAnon"。 由于使用root用户新建文件,文件的所有者为root,所以需要把文件的权限设置为644其他用户才能读取该文件 设置/var/ftp/pub权限为777,表示所有用户均有读写权限. 由于端口22被ssh服务器占用,所以需要关闭ssh服务并重启vsftp服务。 客户端用Flashfxp测试:新建站点"VsftpTest",输入Vsftp服务器的IP地址,端口填22,用户名填ftp1,密码填你设置的密码,然后点击"连接"按钮: 由于没有使用选项:local_root,登陆本地用户时自动跳转到该用户的主目录.没有使用chroot_local_user=yes,所以本地用户可以浏览整个文件系统中他有权限读取的文件和文件夹: 切换到目录"/var/ftp/pub",该目录的权限为777,上传一个10M以上的文件,测试ftp1的上传速度: 新建文件夹和删除权限测试省略。下面测试匿名用户权限:勾选"匿名"选项并连接: 匿名用户登陆成功: 进入pub目录,尝试删除testAnon文件失败,说明这里是配置文件中的anon_other_write_enable=no生效了。最终权限等于配置文件中的权限和linux文件系统权限相或的结果。比如vsftp配置文件中允许匿名用户下载,但光这样是不够的,还需要被下载的文件或文件夹的能够被其他用户读取。 尝试上传文件失败,符合匿名用户只允许下载的要求: 最后测试匿名用户的下载速度: vsftp的配置

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值