www.pudn.com > ftpproxy.rar > ftp_proxy.cpp


// ftp_proxy.cpp : Defines the entry point for the console application. 
// 
// ftp协议中存在两个信道,分别是命令和数据信道,命令信道 
// 使用命令/响应方式工作,其中USER命令和PORT命令操作需要 
// 代理服务器转义。命令信道控制数据信道的建立,数据信道工 
// 作于数据流方式。 
// 
 
#include "stdafx.h" 
#include "ftp_proxy.h" 
 
#include  
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
///////////////////////////////////////////////////////////////////////////// 
// The one and only application object 
 
CWinApp theApp; 
 
using namespace std; 
 
#define _SUCCESS		0 
#define _ERROR			-1 
#define _LINE_ERROR		-100 
#define _BUFF_OVERFLOW	-101 
 
#define FTP_DEFAULT_PORT 21 
 
typedef unsigned long	ulong; 
typedef unsigned short	ushort; 
typedef unsigned char	uchar; 
 
//辅助线程启动使用的参数 
typedef struct { 
	HANDLE		syncobj; 
	SOCKET		so_client; 
	SOCKET		so_remote; 
	SOCKET		so_listen; 
	sockaddr_in	sa_client; 
	sockaddr_in sa_remote; 
}friend_args_rec; 
 
typedef struct { 
	char		buff[1024]; 
	ulong		size; 
}ftp_buff_rec; 
 
typedef struct { 
	SOCKET		so_client; 
	SOCKET		so_remote; 
	sockaddr_in	sa_client; 
	sockaddr_in sa_remote; 
	char		pool[1024]; 
	char		host[80]; 
	ushort		port; 
	char		usrname[80]; 
	char		request[80]; 
	char		command[80]; 
	char		token[80]; 
	ulong		proxy_status; 
}ftp_request_rec; 
 
/* ftp command list 
USER    PORT    RETR    ALLO    DELE    SITE    XMKD    CDUP 
PASS    PASV    STOR    REST    CWD     STAT    RMD     XCUP 
ACCT    TYPE    APPE    RNFR    XCWD    HELP    XRMD    STOU 
REIN    STRU    SMNT    RNTO    LIST    NOOP    PWD     SIZE 
QUIT    MODE    SYST    ABOR    NLST    MKD     XPWD    MDTM 
*/ 
 
//错误日志 
int ftp_log_error(ftp_request_rec* r, const char* error_msg) { 
 
	return _SUCCESS; 
} 
 
 
//ftp proxy 辅助线程 
UINT ftp_proxy_friend(LPVOID lpargs) { 
	friend_args_rec r; 
	char pool[4096]; 
 
	memcpy(&r, lpargs, sizeof(r)); 
	//set thread startup success flag 
	if (r.syncobj != NULL) SetEvent(r.syncobj); 
 
	int len = sizeof(r.sa_client); 
	r.so_client = accept(r.so_listen, (sockaddr*)&r.sa_client, &len); 
 
	r.so_remote = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
	int ret = connect(r.so_remote, (sockaddr*)&r.sa_remote, sizeof(r.sa_remote)); 
	ret = WSAGetLastError(); 
 
	for (;;) { 
		fd_set rdfs; 
 
		FD_ZERO(&rdfs); 
		FD_SET(r.so_client, &rdfs); 
		FD_SET(r.so_remote, &rdfs); 
		 
		ret = select(2, &rdfs, NULL, NULL, NULL); 
 
		if (ret == SOCKET_ERROR) { 
			TRACE("friend thread: select failed\n"); 
			break; 
		} 
 
		if (ret > 0) { 
			if (FD_ISSET(r.so_client, &rdfs)) { 
				ret = recv(r.so_client, pool, sizeof(pool), 0); 
				 
				if (ret == 0) { 
					break; 
				} 
 
				if (ret == SOCKET_ERROR) { 
					TRACE("friend thread: receive data failed\n"); 
					break; 
				} 
 
				if (send(r.so_remote, pool, ret, 0) == SOCKET_ERROR) { 
					TRACE("friend thread: receive data failed\n"); 
					break; 
				} 
			} 
 
			if (FD_ISSET(r.so_remote, &rdfs)) { 
				ret = recv(r.so_remote, pool, sizeof(pool), 0); 
 
				if (ret == 0) { 
					break; 
				} 
 
				if (ret == SOCKET_ERROR) { 
					TRACE("friend thread: receive data failed\n"); 
					break; 
				} 
 
				if (send(r.so_client, pool, ret, 0) == SOCKET_ERROR) { 
					TRACE("friend thread: receive data failed\n"); 
					break; 
				} 
			 
			} 
		} 
	} 
	 
	closesocket(r.so_client); 
	closesocket(r.so_remote); 
 
	return _SUCCESS; 
} 
 
 
//取得一行输入数据 
int ftp_getline(ftp_request_rec* r) { 
	char* p = r->request; 
	ulong size = 0; 
	int ret = 0; 
	int i; 
 
	for (;;) { 
		ret = recv(r->so_client, p+size, sizeof(r->request)-size, 0); 
		if (ret == SOCKET_ERROR || ret == 0) { 
			return _ERROR; 
		} 
 
		for (i = size; i < size+ret; i++) { 
			if (p[i] == '\n' && p[i-1] == '\r') { 
				r->request[i-1] = '\0'; 
				return _SUCCESS; 
			} 
		} 
		size += ret; 
		if (size >= sizeof(r->request)) { 
			return _BUFF_OVERFLOW; 
		} 
	} 
 
	return _ERROR; 
} 
 
 
//解析请求 
int ftp_parse_request(ftp_request_rec* r) { 
	char *s,*p,*end; 
 
	strcpy(r->pool, r->request); 
 
	s = p = r->pool; 
	end = r->pool + strlen(r->pool); 
 
	memset(r->command, 0, sizeof(r->command)); 
	//parse command 
	if ((p = strchr(s, ' ')) != NULL) { 
		*p = '\0';	 
	} 
 
	strcpy(r->command, s); 
	strupr(r->command);		//to upper for compare 
	 
	//parse request token 
	memset(r->token, 0, sizeof(r->token)); 
	if (p != NULL) { 
		s = ++p; 
		if (s < end) { 
			strcpy(r->token, s); 
		} 
	} 
 
	return _SUCCESS; 
} 
 
 
int ftp_parse_response(ftp_request_rec* r, ftp_buff_rec* response) { 
	int ret = _ERROR; 
	ulong resp_code; 
	char *s, *p; 
 
	s = p = response->buff; 
 
	for (;;) { 
		p = strchr(s, ' '); 
		if (p != NULL && p-s == 3) { 
			resp_code = atoi(s); 
			if (resp_code >= 100 && resp_code <= 999) { 
				if (r->proxy_status == 0) { 
					*p = '-'; 
				} 
				ret = _SUCCESS; 
			} 
		} 
		 
		p = strchr(s, '\n'); 
		 
		if (p == NULL ) { 
			response->size = response->size + response->buff - s; 
			memcpy(response->buff, s, response->size); 
			ret = _ERROR; 
			break; 
		} 
		 
		++p; 
		//临时对策(dos ftp程序无法识别密码请求) 
		if(r->proxy_status != 0 ) { 
			if (send(r->so_client, s, p-s, 0) == SOCKET_ERROR) { 
				break; 
			} 
		}	 
		if (resp_code < 200) { 
			ret = _ERROR; 
		} 
 
		s = p; 
		if (s - response->buff >= response->size) { 
			response->size = 0; 
			break; 
		} 
 
		if (ret = _SUCCESS) break; 
	} 
 
	return ret; 
} 
 
int ftp_process_response(ftp_request_rec* r) { 
	int ret; 
	ftp_buff_rec pool; 
	 
	pool.size = 0; 
 
	for (;;) { 
		ret = recv(r->so_remote, pool.buff+pool.size, sizeof(pool.buff)-pool.size, 0); 
		 
		if(ret <= 0 )break; 
		 
		pool.size += ret; 
 
		if (ftp_parse_response(r, &pool) == _SUCCESS) { 
			break; 
		} 
	} 
 
	return _SUCCESS; 
} 
 
 
int exec_cmd_user(ftp_request_rec* r) { 
	char *s, *p, *end; 
 
	if (r->so_remote != SOCKET_ERROR) { 
		const char* err_msg =  
			"500 Already connected to remote host, use disconnect first.\r\n"; 
		send(r->so_client, err_msg, strlen(err_msg), 0);  
		return _ERROR; 
	} 
 
	strcpy(r->pool, r->token); 
	 
 
	s = p = r->pool; 
	end = r->pool + strlen(r->pool); 
 
	//parse user name 
	if ((p=strchr(s, '@')) == NULL) { 
		return _LINE_ERROR; 
	} 
	*p = '\0'; 
	strcpy(r->usrname, s); 
 
	//parse port 
	s = ++p; 
	if ((p=strchr(s, ':')) == NULL) { 
		p = end; 
		r->port = FTP_DEFAULT_PORT; 
	} 
	else { 
		r->port = atoi(p+1); 
	} 
	 
	//parse remote host name 
	if (s >= end) { 
		return _LINE_ERROR; 
	} 
	strcpy(r->host, s); 
	 
	//parse host name to sockaddr struct 
	r->sa_remote.sin_family = AF_INET; 
	r->sa_remote.sin_port = htons(r->port); 
	ulong* addr = &(r->sa_remote.sin_addr.s_addr); 
	*addr = INADDR_NONE; 
	if ((*addr = inet_addr(r->host)) == INADDR_NONE) { 
		HOSTENT* host = gethostbyname(r->host); 
		if (host != NULL) { 
			*addr = *((ulong*)host->h_addr); 
		} 
	} 
 
	if (*addr == INADDR_NONE) { 
		return _ERROR; 
	} 
 
	// create socket 
	r->so_remote = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
	if (r->so_remote == SOCKET_ERROR) { 
		return _ERROR; 
	} 
 
	//connect to remote host 
	if (connect(r->so_remote, (sockaddr*)&r->sa_remote, 
			sizeof(r->sa_remote)) == SOCKET_ERROR) { 
		return _ERROR; 
	} 
	 
	ftp_process_response(r); 
	 
	r->proxy_status = 1; 
 
	sprintf(r->pool, "USER %s\r\n", r->usrname); 
	if (send(r->so_remote, r->pool, strlen(r->pool), 0) == SOCKET_ERROR) { 
		return _ERROR; 
	} 
 
	ftp_process_response(r); 
 
	return _SUCCESS; 
} 
 
 
int exec_cmd_stor(ftp_request_rec* r) { 
	const char* err_msg = "500 method denied.\r\n";  
	send(r->so_client, err_msg, strlen(err_msg), 0); 
 
	return _SUCCESS; 
} 
 
 
int exec_cmd_port(ftp_request_rec* r) { 
 
	char buff[6]; 
	char *s, *p; 
	 
	memset(buff, 0, sizeof(buff)); 
	strcpy(r->pool, r->token); 
	s = p = r->pool; 
 
	for (int i=0; i < 6; i++) { 
		p = strchr(s, ','); 
		if(p == NULL) { 
			if(i == 5) buff[i] = atoi(s); 
			break; 
		} 
		*p = '\0'; 
		buff[i] = atoi(s); 
		s = ++p; 
	} 
 
	sockaddr_in sa_client; 
	sa_client.sin_family = AF_INET; 
	sa_client.sin_addr.s_addr = *((ulong*)buff); 
	sa_client.sin_port = *((ushort*)&buff[4]); 
 
	if (sa_client.sin_addr.s_addr == INADDR_ANY || 
			ntohs(sa_client.sin_port) == 0) { 
		const char* err_msg = "501 Syntax error in parameters or arguments.\r\n"; 
		send(r->so_client, err_msg, strlen(err_msg), 0); 
		return _SUCCESS; 
	} 
 
	sockaddr_in sa_local; 
	int len = sizeof(sockaddr_in); 
 
	int ret = getsockname(r->so_remote, (sockaddr*)&sa_local, &len); 
 
	SOCKET so_local = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
	sa_local.sin_port = htons(0); 
	ret = bind(so_local, (sockaddr*)&sa_local, sizeof(sockaddr_in)); 
	ret = listen(so_local, 1); 
	ret = getsockname(so_local, (sockaddr*)&sa_local, &len);  
 
	sprintf(r->pool, "PORT %d,%d,%d,%d,%d,%d\r\n",  
		sa_local.sin_addr.S_un.S_un_b.s_b1, 
		sa_local.sin_addr.S_un.S_un_b.s_b2, 
		sa_local.sin_addr.S_un.S_un_b.s_b3, 
		sa_local.sin_addr.S_un.S_un_b.s_b4, 
		(uchar)(sa_local.sin_port &0x00ff), 
		(uchar)(sa_local.sin_port >> 8)); 
 
	send(r->so_remote, r->pool, strlen(r->pool), 0); 
	ftp_process_response(r); 
 
	friend_args_rec args; 
	memset(&args, 0, sizeof(args)); 
 
	args.so_listen = so_local; 
	args.syncobj = CreateEvent(NULL, TRUE, FALSE, "ftp_proxy_sync_obj"); 
 
	memcpy(&args.sa_remote, &sa_client, sizeof(sockaddr_in)); 
 
	AfxBeginThread(ftp_proxy_friend, (LPVOID)&args); 
	::WaitForSingleObject(args.syncobj, INFINITE); 
	CloseHandle(args.syncobj); 
 
	return _SUCCESS; 
} 
 
 
int exec_cmd_default(ftp_request_rec* r) { 
	sprintf(r->pool, "%s\r\n", r->request); 
	if (send(r->so_remote, r->pool, 
			strlen(r->pool), 0) == SOCKET_ERROR) { 
		return _ERROR;  
	} 
	ftp_process_response(r); 
 
	return _SUCCESS; 
} 
 
 
int ftp_exec_command(ftp_request_rec* r) { 
	char* p = r->command; 
	int ret; 
 
	if (!strcmp(p, "USER")) { 
		ret = exec_cmd_user(r); 
	} 
	else if (!strcmp(p, "STOR")) { 
		ret = exec_cmd_stor(r); 
	} 
	else if (!strcmp(p, "PORT")) { 
		ret = exec_cmd_port(r); 
	} 
	else if (!strcmp(p, "PASV")) { 
		ret = ERROR; 
	} 
	else { 
		ret = exec_cmd_default(r); 
	} 
 
	return ret; 
} 
 
 
//ftp proxy 主线程 
UINT ftp_proxy_main(LPVOID lpargs) { 
	int ret; 
	ftp_request_rec r; 
	memset(&r, 0, sizeof(r)); 
 
	r.so_client = (SOCKET)lpargs; 
	r.so_remote = SOCKET_ERROR; 
 
	//send welcome message 
	const char* welcome_msg = "220 ftp proxy ready\r\n"; 
	if (send(r.so_client, welcome_msg, strlen(welcome_msg), 0) == SOCKET_ERROR) { 
		return _ERROR; 
	} 
 
	for (;;) { //wait for receive event 
		fd_set rdfs; 
		FD_ZERO(&rdfs); 
		FD_SET(r.so_client, &rdfs); 
 
		if ((ret = select(1, &rdfs, NULL, NULL, NULL)) <= 0) { 
			break; 
		} 
 
		//data receive or disconnect 
		if (FD_ISSET(r.so_client, &rdfs)) { 
			if (ftp_getline(&r) != _SUCCESS) { 
				TRACE("cannot get request line \n"); 
				ftp_log_error(&r, "cannot get request line"); 
				break; 
			} 
			if (ftp_parse_request(&r) != _SUCCESS) { 
				TRACE("cannot parse request \n"); 
				ftp_log_error(&r, "cannot parse request"); 
				break;; 
			} 
 
			if (ftp_exec_command(&r) != _SUCCESS) { 
				TRACE("cannot exec command \n"); 
				ftp_log_error(&r, "cannot exec command"); 
				break; 
			} 
		} 
	} 
 
	//close all socket 
	closesocket(r.so_client); 
	closesocket(r.so_remote); 
 
	return _SUCCESS;; 
} 
 
 
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) 
{ 
	// initialize MFC and print and error on failure 
	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0)) 
	{ 
		// TODO: change error code to suit your needs 
		cerr << _T("Fatal Error: MFC initialization failed") << endl; 
		return  1; 
	} 
 
	WSADATA wsd; 
	if (WSAStartup(MAKEWORD(1,1), &wsd) != 0) { 
		cout << "failed to startup winsock library" << endl; 
		return _ERROR; 
	} 
 
	// startup listen socket 
	SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
	if (s == SOCKET_ERROR) { 
		cout << "failed to create socket" << endl; 
		return _ERROR; 
	} 
 
	sockaddr_in sa_local; 
	sa_local.sin_family			= AF_INET; 
	sa_local.sin_port			= htons(21); 
	sa_local.sin_addr.s_addr	= INADDR_ANY; 
 
	if (bind(s, (sockaddr*)&sa_local, sizeof(sockaddr_in)) == SOCKET_ERROR) { 
		cout << "failed to bind socket" << endl; 
		return _ERROR; 
	} 
 
	if (listen(s, 5) == SOCKET_ERROR) { 
		cout << "failed to listen socket" << endl; 
		return _ERROR; 
	} 
	 
	cout << "ftp proxy is running..." << endl; 
 
	for(;;) { //wait for connect request 
		sockaddr_in sa_client; 
		int sa_len; 
		 
		sa_len = sizeof(sa_client); 
		SOCKET so_client = accept(s, (sockaddr*)&sa_client, &sa_len); 
 
		if (so_client == SOCKET_ERROR) { 
			cout << "accept is failed" << endl; 
			break; 
		} 
		else { //begin new thread 
			AfxBeginThread(ftp_proxy_main, (void*)so_client); 
		} 
	} 
 
	WSACleanup(); 
 
	return 0; 
}