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; }