검색결과 리스트
글
[FTP] (2) 클라이언트 구현하기
네트워크로그
2011. 3. 9. 02:22
다음은 c언어로 구현한 open, get, put, ls, cd, pwd, quit/bye, hash 명령을 구현한 간단한 FTP 클라이언트 예제이다. 에러처리와 소켓의 recv, send 함수의 동작방식을 정확히 고려하지 않았기 때문에, 정확히 동작하지 않을 수 있다. 데이터 수신시 하나의 명령에 대한 메시지의 끝을 구분해 줄 수 있는 방법이 추가로 필요하다. (메시지 끝을 의미하는 \r\n 로 구분) : Ftp 커맨드 및 상수를 정의한 헤더파일
#define CMD_OPEN "open"
#define CMD_LIST "ls"
#define CMD_GET "get"
#define CMD_PUT "put"
#define CMD_PWD "pwd"
#define CMD_CD "cd"
#define CMD_QUIT "quit"
#define CMD_BYE "bye"
#define CMD_HASH "hash"
#define CMD_SHELL "!"
#define MODE_DEBUG 1
#define MODE_NORMAL 0
#define FTP_PORT 21
extern int mode;
void debug(char *msg) {
if (mode == MODE_DEBUG) {
printf("[debug] : %s \n", msg);
}
}
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define TEMP_BUFFER_SIZE 1024
int connectServer(char *serverIp, short port) {
int sock;
struct sockaddr_in servAddr;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("sock failed");
exit(1);
}
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr(serverIp);
servAddr.sin_port = htons(port);
if (connect(sock, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1) {
perror("connect failed");
exit(1);
}
return sock;
}
void sendProtocol(int sock, char *protocol) {
if (send(sock, protocol, strlen(protocol), 0) != strlen(protocol)) {
perror("send failed");
exit(1);
}
if (MODE_DEBUG == mode) {
printf("send: %s", protocol);
}
}
void recvProtocol(int sock, char *recvBuffer, int bufferSize) {
int recvLen;
if ((recvLen = recv(sock, recvBuffer, bufferSize-1, 0)) <= 0) {
perror("recv failed");
exit(1);
}
recvBuffer[recvLen] = '\0';
if (MODE_DEBUG == mode) {
printf("recv: %s", recvBuffer);
}
}
unsigned int downloadFile(int sock, char *filePath, unsigned int fileSize, int hashFlag) {
char readBuffer[TEMP_BUFFER_SIZE];
unsigned int readBytes, totalBytes, numHash;
int fd = open(filePath, O_WRONLY | O_CREAT, 0744);
totalBytes = numHash = 0;
while (totalBytes < fileSize) {
if ((readBytes = read(sock, readBuffer, TEMP_BUFFER_SIZE)) <= 0) {
close(fd);
return totalBytes;
}
write(fd, readBuffer, readBytes);
totalBytes += readBytes;
if (hashFlag) {
if ((totalBytes/TEMP_BUFFER_SIZE) > numHash) {
numHash++;
printf("#");
}
}
}
close(fd);
printf("\n");
return totalBytes;
}
unsigned int uploadFile(int sock, char *filePath, int hashFlag) {
char readBuffer[TEMP_BUFFER_SIZE];
unsigned int readBytes, totalBytes, numHash;
int fd = open(filePath, O_RDONLY);
totalBytes = numHash = 0;
while ((readBytes = read(fd, readBuffer, TEMP_BUFFER_SIZE)) > 0) {
write(sock, readBuffer, readBytes);
totalBytes += readBytes;
if (hashFlag) {
if ((totalBytes/TEMP_BUFFER_SIZE) > numHash) {
numHash++;
printf("#");
}
}
}
close(fd);
printf("\n");
return totalBytes;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "FtpCommand.h"
#include "ClientSocket.h"
#define COMMAND_MAX_SIZE 1024
#define BUFFER_SIZE 1024
#define FILENAME_SIZE 256
#define END_OF_PROTOCOL "\r\n"
void initializeFtpClient();
void startFtpClient(char *ip, char *port);
void commandHandle(char *cmd);
void defaultHandler(char *cmd);
int modeCheck(const char *option);
void printMessage(char *msg);
void openCon(char *cmd);
void list(char *listCmd);
void get(char *getCmd);
void put(char *putCmd);
void cd(char *cdCmd);
void quit(char *quitCmd);
void pwd(char *pwdCmd);
void bye(char *byeCmd);
void hash(char *hashCmd);
void passiveMode(char *ip, int *port);
void shellEscape(char *shellCmd);
unsigned int downloadFile(int sock, char *filePath, unsigned int fileSize, int hashFlag);
unsigned int uploadFile(int sock, char *filePath, int hashFlag);
typedef struct _FtpCmdHandler {
char cmd[5];
void (*handler)(char* arg);
} FtpCmdHandler;
// Map Ftp Command to Handler
FtpCmdHandler ftpCmdHandler[] = {
{ CMD_OPEN, openCon },
{ CMD_LIST, list },
{ CMD_PUT, put },
{ CMD_GET, get },
{ CMD_CD, cd },
{ CMD_PWD, pwd },
{ CMD_HASH, hash },
{ CMD_QUIT, quit },
{ CMD_BYE, bye },
{ CMD_SHELL, shellEscape },
};
// sock - PI socket , dtpSock - DTP socket
int sock, dtpSock;
int state;
int mode;
int hashFlag;
int main (int argc, char const *argv[])
{
if (argc == 3) {
// argv[1] == ip, argv[2] == port
startFtpClient(argv[1], argv[2]);
} else if (argc == 4 && modeCheck(argv[1]) == MODE_DEBUG) {
// argv[1] == -d, argv[2] = ip, argv[3] == port
startFtpClient(argv[2], argv[3]);
} else {
fprintf(stderr, "Usage: %s [-d] \n", argv[0]);
}
return 0;
}
int modeCheck(const char *option) {
if (!strcmp(option, "-d")) {
// debug mode
mode = MODE_DEBUG;
} else {
mode = MODE_NORMAL;
}
return mode;
}
// initialize ftp client
void initializeFtpClient() {
hashFlag = 1;
state = INITIAL_STATE;
debug("initialized");
}
// ftp client start
void startFtpClient(char *ip, char *port) {
char cmd[COMMAND_MAX_SIZE];
initializeFtpClient();
while (1) {
// input user command
if (ip == 0 && port == 0) {
printMessage("ftp>");
fgets(cmd, COMMAND_MAX_SIZE, stdin);
} else {
sprintf(cmd, "open %s %s", ip, port);
startCmd = 0;
}
// call handler
commandHandle(cmd);
}
}
// map command to handler
void commandHandle(char *cmd) {
int i;
int numCmd = sizeof(ftpCmdHandler)/sizeof(FtpCmdHandler);
for (i = 0; i < numCmd; i++) {
if (!strncmp(cmd, ftpCmdHandler[i].cmd, strlen(ftpCmdHandler[i].cmd))) {
(*(ftpCmdHandler[i].handler))(cmd);
break;
}
}
}
void defaultHandler(char *cmd) {
printf("default handler: %s\n", cmd);
}
// ftp server connect
void openCon(char *openCmd) {
char serverIp[16], serverPort[16];
char cmd[BUFFER_SIZE];
char sendBuffer[BUFFER_SIZE];
char recvBuffer[BUFFER_SIZE];
sscanf(openCmd,"%*s %s %s%*c", serverIp, serverPort);
debug(serverIp);
// connect to server
sock = connectServer(serverIp, atoi(serverPort));
recvProtocol(sock, recvBuffer, BUFFER_SIZE-1);
// send user name
printf("Name: ");
fgets(cmd, COMMAND_MAX_SIZE, stdin);
sprintf(sendBuffer, "User %s", cmd);
sendProtocol(sock, sendBuffer);
recvProtocol(sock, recvBuffer, BUFFER_SIZE-1);
printMessage(recvBuffer);
// send password
printf("Password: ");
fgets(cmd, COMMAND_MAX_SIZE, stdin);
sprintf(sendBuffer, "PASS %s", cmd);
sendProtocol(sock, sendBuffer);
recvProtocol(sock, recvBuffer, BUFFER_SIZE-1);
printMessage(recvBuffer);
// get server os information
sprintf(sendBuffer, "SYST%s", END_OF_PROTOCOL);
sendProtocol(sock, sendBuffer);
recvProtocol(sock, recvBuffer, BUFFER_SIZE-1);
printMessage(recvBuffer);
}
// send EPSV or PASS to Server
void passiveMode(char *ip, int *port) {
char sendBuffer[BUFFER_SIZE];
char recvBuffer[BUFFER_SIZE];
int host0, host1, host2, host3;
int port0, port1;
sprintf(sendBuffer, "PASV%s", END_OF_PROTOCOL);
sendProtocol(sock, sendBuffer);
recvProtocol(sock, recvBuffer, BUFFER_SIZE-1);
printMessage(recvBuffer);
sscanf(strchr(recvBuffer, '(')+1, "%d,%d,%d,%d,%d,%d", &host0, &host1, &host2, &host3, &port0, &port1);
sprintf(ip, "%d.%d.%d.%d", host0, host1, host2, host3);
*port = port0*256 + port1;
debug(ip);
printf("dtp port : %d\n", *port);
}
// get remote working directory file list
void list(char *listCmd) {
int port;
char ip[16];
char sendBuffer[BUFFER_SIZE];
char recvBuffer[BUFFER_SIZE*8];
debug("list");
// recv server response and parsing
passiveMode(ip, &port);
// connect to DTP
dtpSock = connectServer(ip, port);
// send LIST command to PI server
sprintf(sendBuffer, "LIST%s", END_OF_PROTOCOL);
sendProtocol(sock, sendBuffer);
recvProtocol(sock, recvBuffer, BUFFER_SIZE);
printMessage(recvBuffer);
// recv file list from DTP
recvProtocol(dtpSock, recvBuffer, BUFFER_SIZE*8);
printMessage(recvBuffer);
// recv complete message from PI server
recvProtocol(sock, recvBuffer, BUFFER_SIZE);
printMessage(recvBuffer);
close(dtpSock);
}
// file download
void get(char *getCmd) {
int port;
unsigned int fileSize;
char ip[16], filePath[FILENAME_SIZE], fileName[50];
char sendBuffer[BUFFER_SIZE];
char recvBuffer[BUFFER_SIZE];
// get local current working directory
getcwd(filePath, FILENAME_SIZE);
sscanf(getCmd, "%*s %s%*c", fileName);
sprintf(filePath, "%s/%s", filePath, fileName);
debug("get");
printf("fileName: %s\n", fileName);
printf("filePath: %s\n", filePath);
passiveMode(ip, &port);
// connect to DTP
dtpSock = connectServer(ip, port);
// request server for transfer start - RETR fileName
sprintf(sendBuffer, "RETR %s%s", fileName, END_OF_PROTOCOL);
sendProtocol(sock, sendBuffer);
recvProtocol(sock, recvBuffer, BUFFER_SIZE);
printMessage(recvBuffer);
// extract fileSize
sscanf(strchr(recvBuffer, '(')+1, "%u", &fileSize);
printf("fileSize: %u\n", fileSize);
// download file from DTP
downloadFile(dtpSock, filePath, fileSize, hashFlag);
// recv complete message from PI server
recvProtocol(sock, recvBuffer, BUFFER_SIZE);
printMessage(recvBuffer);
close(dtpSock);
}
// file upload
void put(char *putCmd) {
int port;
unsigned int fileSize;
char ip[16], filePath[FILENAME_SIZE], fileName[50];
char sendBuffer[BUFFER_SIZE];
char recvBuffer[BUFFER_SIZE];
sscanf(putCmd, "%*s %s%*c", fileName);
// get local current working directory
getcwd(filePath, FILENAME_SIZE);
sscanf(putCmd, "%*s %s%*c", fileName);
sprintf(filePath, "%s/%s", filePath, fileName);
debug("put");
debug(filePath);
passiveMode(ip, &port);
// connect to DTP
dtpSock = connectServer(ip, port);
// request server for transfer start - STOR fileName
sprintf(sendBuffer, "STOR %s%s", fileName, END_OF_PROTOCOL);
sendProtocol(sock, sendBuffer);
recvProtocol(sock, recvBuffer, BUFFER_SIZE);
printMessage(recvBuffer);
// file upload to DTP
fileSize = uploadFile(dtpSock, filePath, hashFlag);
close(dtpSock);
/// recv complete message from PI server
recvProtocol(sock, recvBuffer, BUFFER_SIZE);
printMessage(recvBuffer);
}
// change remote working directory
void cd(char *cdCmd) {
char sendBuffer[BUFFER_SIZE];
char recvBuffer[BUFFER_SIZE];
debug("cd");
sscanf(cdCmd, "%*s %s%*c", recvBuffer);
debug(recvBuffer);
sprintf(sendBuffer, "CWD %s%s", recvBuffer, END_OF_PROTOCOL);
sendProtocol(sock, sendBuffer);
recvProtocol(sock, recvBuffer, BUFFER_SIZE);
printMessage(recvBuffer);
}
// ftp client exit
void quit(char *quitCmd) {
char sendBuffer[BUFFER_SIZE];
char recvBuffer[BUFFER_SIZE];
debug("quit");
sprintf(sendBuffer, "QUIT%s", END_OF_PROTOCOL);
sendProtocol(sock, sendBuffer);
recvProtocol(sock, recvBuffer, BUFFER_SIZE);
printMessage(recvBuffer);
close(sock);
exit(0);
}
// same quit
void bye(char *byeCmd) {
quit(0);
}
// get remote working directory
void pwd(char *pwdCmd) {
char sendBuffer[BUFFER_SIZE];
char recvBuffer[BUFFER_SIZE];
debug("pwd");
sprintf(sendBuffer, "PWD%s", END_OF_PROTOCOL);
sendProtocol(sock, sendBuffer);
recvProtocol(sock, recvBuffer, BUFFER_SIZE);
printMessage(recvBuffer);
}
// hash option on/off
void hash(char *hashCmd) {
debug("hash");
hashFlag = !hashFlag;
if (hashFlag == 0) {
printMessage("hash off");
} else {
printMessage("hash on");
}
}
// shell command - not implemented
void shellEscape(char *shellCmd) {
printMessage("not implemented");
}
void printMessage(char *msg) {
printf("%s", msg);
}
'네트워크로그' 카테고리의 다른 글
| [FTP] (2) 클라이언트 구현하기 (0) | 2011.03.09 |
|---|---|
| [FTP] (1) FTP(File Transmission Protocol) 프로토콜 이해 (0) | 2011.03.09 |
| 바이트 오더링(byte ordering) (0) | 2011.03.08 |
| OAuth의 세부사항 (0) | 2011.01.01 |
| OAuth의 개념과 대략적인 흐름 (0) | 2010.12.31 |
FtpCommand.h