网络安全传输系统(6)-其它改进

1、改进

需要改进的地方如下:

1、输入密码时最好不回显,用*代替

2、目前数据库中保存的是明文密码,这显然是不行的,容易被窃取,可以保存密码对应的MD5摘要

3、在服务器中为每个用户创建一个文件夹,用来保存其上传的文件

4、用户选择下载文件时,最好能够列出其能够下载的所有文件

改进后的代码在:https://github.com/dayL-W/File-Transfer-with-SSL.git

1.1密码不回显

方法一:

在windows中可以使用getch函数来获取字符,且不会显,如果在Linux中使用getch()需要解压curses.h头文件的帮助,同时在编译的时候加上-lcurses.但是这个方法在我的程序中总是出现各种各样的问题,尤为麻烦,算了,放弃好了。

 

方法二:

Linux中的unistd.h的文件中有一个函数getpass();专门用来输入密码用的,用法是:

char *password;
password = getpass("Input Password");

缺点是不能在输入密码时显示*

 

1.2使用OpenSSL的MD5摘要算法

头文件:

#include<openssl/md5.h>

用法:

char password_md5[33]={'\0'};
char md5_tmp[33]={'\0'};
MD5(password,strlen(password),md);
for(i=0; i<16; i++)
{
	sprintf(md5_tmp,"%2.2x",md[i]);
	strcat(password_md5,md5_tmp);
}
password_md5[33] = '\0';

 

由于MD5参数中的md返回的是16位的无符号字符型数据,一般都会把它保存成16进制的字符串类型,所以对md5进行格式化,一个unsigned char映射成2个16进制的数字,然后拼接成加密后的密码即可,最后给字符串接上结尾的符号。

1.3为用户创建文件夹

如果是新用户则需要为用户创建一个文件夹,同时线程需要保存当前的用户名,如果现在用户名已存在,则注册失败,不创建用户名。

 

头文件:

#include <sys/stat.h> 

 

实现方法:

getcwd(pwd,sizeof(pwd));
strcat(pwd,"/");
strcat(pwd,now_username);
strcat(pwd,"/");
strcat(pwd,filename);

先获取当前路径,然后把用户名和需要操作的文件名加上,构成一条路径,然后下载和上传文件都在这个目录下进行,当然也可以改变当前工作目录,但是感觉不太稳妥。

 

1.4列举可以下载的文件

先统计文件个数,因为这里的文件夹没有子文件,可以直接用opendir统计,

头文件:

#include <dirent.h>

统计个数及发送文件名:

int file_cnt=0;
DIR *dir;
struct dirent *ptr;
dir = opendir(pwd);
while( (ptr = readdir(dir))!=NULL)
{
  	file_cnt++;
}
//发送文件个数
SSL_write(ssl, &file_cnt, 4);
//逐个发送文件名
dir = opendir(pwd);
while( (ptr = readdir(dir))!=NULL)
{
	SSL_write(ssl, ptr->d_name, 20);
}	SSL_write(ssl, ptr->d_name, 20);
}

 

2、实例代码

经过这个多的改进,这里贴出所以代码

 

客户机:

#include <stdio.h>
#include <string.h>
#include <curses.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>


#include <openssl/err.h>
#include <openssl/ssl.h>
#include <openssl/md5.h>

#define port 3333

char ipaddr[15];
int sockfd;
struct sockaddr_in sockaddr;
//声明SSL套接字
SSL_CTX *ctx;
SSL *ssl;

int login()
{
	char username[20];
	char *password;
	char ch;
	int  cnt_ch=0;
	int login_or_create;
	char temp[100];
	char buf[10];
	int  i=0,login_flag=0;
	unsigned char md[15];
	char password_md5[33]={'\0'};
	char md5_tmp[33]={'\0'};

	printf("please input your selection: 1 to login 2 to create\n");
	printf("selection:");
	scanf("%d",&login_or_create);
	if(login_or_create != 1 && login_or_create != 2)
	{
		return 0;
	}
	printf("username:");
	scanf("%s",username);
	password = getpass("password:");
	MD5(password,strlen(password),md);
	for(i=0; i<16; i++)
	{
		sprintf(md5_tmp,"%2.2x",md[i]);
		strcat(password_md5,md5_tmp);
	}
	password_md5[33] = '\0';
	printf("password:%s\n",password);
	printf("md5:%s\n",password_md5);
	sprintf(temp,"log:%d username:%s password:%s",login_or_create,username,password_md5);

	SSL_write(ssl,temp,100);
	SSL_read(ssl,buf,10);
	sscanf(buf,"login:%d",&login_flag);

	if(login_flag == 0)
	{
		printf("-----wrong username or password!-------\n");
	}
	else if(login_or_create == 1)
	{
		printf("----------login successufl!------------\n");
	}
	else
	{
		printf("----------create successufl!------------\n");
	}
	//0失败1成功
	return login_flag;
}
int linkS()
{
	//创建socket
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
	{
		perror("socket\n");
		_exit(0);
	}
	//连接
	memset(&sockaddr,0, sizeof(sockaddr));
	sockaddr.sin_family = AF_INET;
	sockaddr.sin_port = htons(port);
	sockaddr.sin_addr.s_addr = inet_addr(ipaddr);
	if(connect(sockfd,(struct sockaddr *)&sockaddr,sizeof(sockaddr)) == -1)
	{
		perror("connect\n");
		_exit(0);
	}
	
	ssl = SSL_new(ctx);
	SSL_set_fd(ssl,sockfd);
	if(SSL_connect(ssl) == -1)
	{
		printf("SSL connect error!\n");	
		_exit(0);
	}

	return login();
}

void upload_file(char *filename)
{
	int fd;
	char cmd = 'U';
	int FileNameSize = strlen(filename);
	char buf[1024];
	int count=0;
	struct stat fstat;
	
	//打开文件
	fd = open(filename,O_RDONLY);
	//发送命令
	SSL_write(ssl,&cmd,1);
	
	//发送文件名
	SSL_write(ssl,(void *)&FileNameSize,4);
	SSL_write(ssl,filename, FileNameSize);
	//发送文件长度
	if((stat(filename,&fstat)) == -1)
		return;
	SSL_write(ssl,(void *)&fstat.st_size,4);
	
	//发送文件数据
	while((count = read(fd,(void *)buf,1024)) > 0)
	{
		SSL_write(ssl,buf,count);	
	}
	//关闭文件
	close(fd);
}

void download_file()
{
	int fd;
	char cmd = 'D';
	char buf[1024];
	int FileNameSize;
	int filesize=0,count=0,totalrecv=0;
	int file_num=0;
	char file_name_temp[20];
	char file_d[20];
	char c;

	//发送命令
	SSL_write(ssl,&cmd,1);
	
	//接收文件总个数和文件名
	SSL_read(ssl,&file_num,4);
	printf("Total file:%d\n",file_num);
	while(file_num--)
	{
		SSL_read(ssl,file_name_temp,20);
		printf("%s\n",file_name_temp);		
	}
	printf("Download Files:");
	//输入文件名
	while((c = getchar()) != '\n' && c != EOF);
	fgets(file_d, 30, stdin);
	file_d[strlen(file_d)-1] = '\0';


	//发送文件名
	FileNameSize = strlen(file_d);
	SSL_write(ssl,(void *)&FileNameSize,4);
	SSL_write(ssl,file_d,FileNameSize);
	
	//打开并创建文件
	if((fd = open(file_d,O_RDWR|O_CREAT)) == -1)
	{
		perror("open:");
		_exit(0);	
	}
	
	//接收数据
	SSL_read(ssl,&filesize,4);
	while((count = SSL_read(ssl,(void *)buf,1024)) > 0)
	{
		write(fd,buf,count);
		totalrecv += count;
		if(totalrecv == filesize)
			break;	
	}
	
	//关闭文件
	close(fd);
}

void quit()
{
	char cmd = 'Q';
	//发送命令
	SSL_write(ssl,(void *)&cmd,1);
	//关闭及释放SSL连接
	SSL_shutdown(ssl);
	SSL_free(ssl);
	//退出
	_exit(0);
}
void menu()
{
	char cmd;
	char c;
	char file_u[30];
	char file_d[30];
	while(1)
	{
		printf("----------1.Upload Files---------------\n");
		printf("----------2.Download Files-------------\n");
		printf("----------3.Exit-----------------------\n");
		printf("Please input the Client command:");
		scanf("%c",&cmd);	
		
		switch(cmd)
		{
			case '1':
			{
				printf("Upload Files:");
				//输入文件名
				while((c = getchar()) != '\n' && c != EOF);
				fgets(file_u, 30, stdin);
				file_u[strlen(file_u)-1] = '\0';
				//上传文件
				upload_file(file_u);
			}
			break;	
			case '2':
			{
				//下载文件
				download_file();
			}
			break;	
			case '3':
			{
				//退出
				quit();
				break;
			}
			break;	
			default:
			{
				printf("Please input right command!\n");	
			}
			break;	
		}
	}	
}
int main(int argc, char *args[])
{
	if(argc != 2)
	{
		printf("format error: you mast enter ipaddr like this : client 192.168.0.6\n");
	    	_exit(0);	
	}
	strcpy(ipaddr,args[1]);
	
	//初始化SSl
	SSL_library_init();
	OpenSSL_add_all_algorithms();
	SSL_load_error_strings();
	//创建SSL套接字,参数表明支持版本和客户机
	ctx = SSL_CTX_new(SSLv23_client_method());
	if(ctx == NULL)
	{
		printf("Creat CTX error!!!");	
	}
	
	//建立连接
	if(linkS() == 0)
	{
		printf("user name or password error!\n");
		//关闭及释放SSL连接
		SSL_shutdown(ssl);
		SSL_free(ssl);
		//清屏
		system("clear");
		_exit(0);
	}
	//打印菜单
	menu();
	//结尾操作
	close(sockfd);
	//释放CTX
	SSL_CTX_free(ctx);
	return 0;	
}



服务器:

 

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>

#include <openssl/err.h>
#include <openssl/ssl.h>
#include <pthread.h> 
#include <sqlite3.h> 

#define port 3333

typedef struct task 
{ 
    void *(*process) (int newfd, char *now_username); 
    int newfd;
    char *now_username;
    struct task *next; 
} Cthread_task; 


/*线程池结构*/ 
typedef struct 
{ 
    pthread_mutex_t queue_lock; 
    pthread_cond_t queue_ready; 

    /*链表结构,线程池中所有等待任务*/ 
    Cthread_task *queue_head; 

    /*是否销毁线程池*/ 
    int shutdown; 
    pthread_t *threadid; 
    
    /*线程池中线程数目*/ 
    int max_thread_num; 
    
    /*当前等待的任务数*/ 
    int cur_task_size; 

} Cthread_pool; 

static Cthread_pool *pool = NULL; 

void *thread_routine (void *arg); 


int sockfd;
struct sockaddr_in sockaddr;
struct sockaddr_in client_addr;
int sin_size;
char passwd_d[33];

SSL_CTX *ctx;
sqlite3 *db; 
char now_username[20]; //存放当前线程执行任务的用户名

void pool_init (int max_thread_num) 
{ 
    int i = 0;
    
    pool = (Cthread_pool *) malloc (sizeof (Cthread_pool)); 

    pthread_mutex_init (&(pool->queue_lock), NULL); 
    /*初始化条件变量*/
    pthread_cond_init (&(pool->queue_ready), NULL); 

    pool->queue_head = NULL; 

    pool->max_thread_num = max_thread_num; 
    pool->cur_task_size = 0; 

    pool->shutdown = 0; 

    pool->threadid = (pthread_t *) malloc (max_thread_num * sizeof (pthread_t)); 

    for (i = 0; i < max_thread_num; i++) 
    {  
        pthread_create (&(pool->threadid[i]), NULL, thread_routine, NULL); 
    } 
} 

void * thread_routine (void *arg) 
{ 
    printf ("starting thread 0x%x\n", pthread_self ()); 
    while (1) 
    { 
        pthread_mutex_lock (&(pool->queue_lock)); 

        while (pool->cur_task_size == 0 && !pool->shutdown) 
        { 
            printf ("thread 0x%x is waiting\n", pthread_self ()); 
            pthread_cond_wait (&(pool->queue_ready), &(pool->queue_lock)); 
        } 

        /*线程池要销毁了*/ 
        if (pool->shutdown) 
        { 
            /*遇到break,continue,return等跳转语句,千万不要忘记先解锁*/ 
            pthread_mutex_unlock (&(pool->queue_lock)); 
            printf ("thread 0x%x will exit\n", pthread_self ()); 
            pthread_exit (NULL); 
        } 

        printf ("thread 0x%x is starting to work\n", pthread_self ()); 

         
        /*待处理任务减1,并取出链表中的头元素*/ 
        pool->cur_task_size--; 
        Cthread_task *task = pool->queue_head; 
        pool->queue_head = task->next; 
        pthread_mutex_unlock (&(pool->queue_lock)); 

        /*调用回调函数,执行任务*/ 
        (*(task->process)) (task->newfd,task->now_username); 
        free (task); 
        task = NULL; 
    } 
    /*这一句应该是不可达的*/ 
    pthread_exit (NULL); 
}

/*向线程池中加入任务*/ 
int pool_add_task (void *(*process) (int newfd, char *now_username), int newfd, char *now_username) 
{ 
    /*构造一个新任务*/ 
    Cthread_task *task = (Cthread_task *) malloc (sizeof (Cthread_task)); 
    task->process = process; 
    task->newfd = newfd;
    task->now_username = now_username; 
    task->next = NULL;

    pthread_mutex_lock (&(pool->queue_lock)); 
    /*将任务加入到等待队列中*/ 
    Cthread_task *member = pool->queue_head; 
    if (member != NULL) 
    { 
        while (member->next != NULL) 
            member = member->next; 
        member->next = task; 
    } 
    else 
    { 
        pool->queue_head = task; 
    } 

    pool->cur_task_size++; 
    pthread_mutex_unlock (&(pool->queue_lock)); 
    //唤醒一个线程
    //加入
    pthread_cond_signal (&(pool->queue_ready)); 
    
    return 0; 
} 

void handle(char cmd,SSL *ssl,char *now_username)
{
	char filename[30]={0};
	int FileNameSize=0;
	int fd;
	int filesize=0;
	int count=0,totalrecv=0;
	char pwd[100];
	char buf[1024];
	struct stat fstat;
	int file_cnt=0;
	DIR *dir;
	struct dirent *ptr;
  

	switch(cmd)
	{
		case 'U':
		{
			//接收文件名
			SSL_read(ssl, &FileNameSize, 4);
			SSL_read(ssl, (void *)filename, FileNameSize);
			filename[FileNameSize]='\0';

			//strcat(pwd,"./");
			getcwd(pwd,sizeof(pwd));
			strcat(pwd,"/");
			strcat(pwd,now_username);
			strcat(pwd,"/");
			strcat(pwd,filename);
			
			//创建文件
			if((fd = open(pwd,O_RDWR|O_CREAT)) == -1)
			{
				perror("creat:");
				_exit(0);	
			}
			//接收文件长度
			SSL_read(ssl, &filesize, 4);
			
			//接收文件
			while((count = SSL_read(ssl,(void *)buf,1024)) > 0)
			{
				write(fd,&buf,count);
				totalrecv += count;
				if(totalrecv == filesize)
					break;	
			}			
			//关闭文件
			close(fd);
		}
		break;
		
		case 'D':
		{
			//统计用户文件夹的个数
			getcwd(pwd,sizeof(pwd));
			strcat(pwd,"/");
			strcat(pwd,now_username);
			dir = opendir(pwd);
			while( (ptr = readdir(dir))!=NULL)
			{
				file_cnt++;
			}
			//发送文件个数
			SSL_write(ssl, &file_cnt, 4);
			//逐个发送文件名
			dir = opendir(pwd);
			while( (ptr = readdir(dir))!=NULL)
			{
				SSL_write(ssl, ptr->d_name, 20);
			}

			//接收文件名
			SSL_read(ssl, &FileNameSize, 4);
			SSL_read(ssl, filename, FileNameSize);
			filename[FileNameSize]='\0';

			//strcat(pwd,"./");
			strcat(pwd,"/");
			strcat(pwd,filename);
			
			//打开文件
			if((fd = open(pwd,O_RDONLY)) == -1)
			{
				perror("open:");
				_exit(0);	
			}
			//发送文件长度和文件名
			if((stat(pwd,&fstat)) == -1)
				return;
			SSL_write(ssl,&fstat.st_size,4);
			
			while((count = read(fd,(void *)buf,1024)) > 0)
			{
				SSL_write(ssl,&buf,count);	
			}
			close(fd);
		}
		break;	

		default:
		break;
	}
}

static int callback(void *NotUsed, int argc, char **argv, char **azColName) 
{ 
     int i; 
     for(i=0; i<argc; i++) 
     {          
     	strcpy(passwd_d,argv[i]);
     } 
    return 0;
} 

int login(SSL *ssl)
{
	char username[20];
	char password[33];
	int login_or_create;
	char temp[100];
	char buf[10];
	int  login_flag=0;
	char sql[100];
	int rc;
	char pwd[100];

	sqlite3_open("user.db",&db);
	SSL_read(ssl,temp,100);
	sscanf(temp,"log:%d username:%s password:%s",&login_or_create,username,password);
	strcpy(now_username,username);
	//注册
	if(login_or_create == 2)
	{
		sprintf(sql,"insert into stu(username,password) values('%s','%s');",username,password);
		printf("username:%s password:%s\n",username,password);
		rc = sqlite3_exec(db, sql, callback, 0, NULL);
		strcat(pwd,"./");
		strcat(pwd,now_username);
		mkdir(pwd,S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
		if(rc == SQLITE_OK)
		{ 
			login_flag = 1;
		} 
		else
		{
			login_flag = 0;
			printf("insert to database error!\n");
		}
	}
	//登录
	else
	{
		sprintf(sql, "select password from stu where username='%s';",username); 
		sqlite3_exec(db, sql, callback, 0, NULL);
		printf("username:%s password:%s\n",username,passwd_d);
		if(strcmp(password,passwd_d) == 0)
		{
			login_flag = 1;
		}
		else
		{
			login_flag = 0;
			return 0;
		}
	}
	sqlite3_close(db);
	sprintf(buf,"login:%d",login_flag);
	SSL_write(ssl,buf,10);
	
	return login_flag;

}
void *myprocess(int newfd, char *now_username)
{
	SSL *ssl;
	int tmp_fd = newfd;
	char cmd;
	int first=1;
	//产生新的SSL
	ssl = SSL_new(ctx);
	SSL_set_fd(ssl,tmp_fd);
	SSL_accept(ssl);
	//处理事件
	while(1)
	{
		if(first == 1 && login(ssl) == 0 )
		{
			SSL_shutdown(ssl);
			SSL_free(ssl);
			close(tmp_fd);
			break;	
		}
		first = 0;
		SSL_read(ssl,&cmd,1);
		
		if(cmd == 'Q')
		{
			SSL_shutdown(ssl);
			SSL_free(ssl);
			close(tmp_fd);
			break;	
		}
		else
		{
			handle(cmd,ssl,now_username);	
		}
	}
	return NULL;
}


int main()
{
	int newfd;
	char sql[100];
	int rc;
	//初始化线程池
	pool_init(5);

	//创建数据库
	rc = sqlite3_open("user.db",&db);
	if(rc)
	{
		printf("Can't open database: %s\n", sqlite3_errmsg(db)); 
        sqlite3_close(db); 
	}
	sprintf(sql,"create table stu(username char(20), password char(40));");
	rc = sqlite3_exec(db, sql, callback, 0, NULL); 
	if(rc != SQLITE_OK)
	{
		printf("create tables error!\n");
	}
	//建立连接
	
	//SSL连接
	SSL_library_init();
	OpenSSL_add_all_algorithms();
	SSL_load_error_strings();
	ctx = SSL_CTX_new(SSLv23_server_method());
	//载入数字证书
	SSL_CTX_use_certificate_file(ctx,"./cacert.pem",SSL_FILETYPE_PEM);
	//载入私钥
	SSL_CTX_use_PrivateKey_file(ctx,"./privkey.pem",SSL_FILETYPE_PEM);
	SSL_CTX_check_private_key(ctx);
	//创建socket
	if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
	{
		perror("socket:");
		_exit(0);	
	}
	
	memset(&sockaddr,0, sizeof(sockaddr));
	sockaddr.sin_family = AF_INET;
	sockaddr.sin_port = htons(port);
	sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	//绑定地址
	if(bind(sockfd,(struct sockaddr *)&sockaddr,sizeof(sockaddr)) == -1)
	{
		perror("bind:");
		_exit(0);	
	}
	//监听
	if(listen(sockfd,10) == -1)
	{
		perror("listen");	
	}
	
	while(1)
	{
		//连接
		if((newfd = accept(sockfd, (struct sockaddr *)(&client_addr),&sin_size)) == -1)
		{
			perror("accept:");	
			_exit(0);
		}
		//给线程池添加任务
		pool_add_task(myprocess,newfd,now_username);
	}	
	close(sockfd);
	SSL_CTX_free(ctx);
	return 0;
}

更多Linux资料及视频教程点击这里

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值