ftp의 passive모드에 대한 이해와 포트에 대한 오해가 많아 프로젝트에서 개발한 ftp 라이브러리가 회사 서버에 접속하지 못하는 문제를 네트웍관리자, 프로그래머도 해결하지 못하는 경우가 발생하여 ftp에 대해 잠시 분석을 해봤다.
ftp에 대한 기본적인 이해는 이미 알고 있었으나 passive모드에 대한 깨알같은 이해를 이번기회에 확실하게 정리를 하였다.
ftp란
ftp는 당연히 file transfer protocol의 약자로 파일 전송을 위한 기본적인 tcp/ip 프로토콜이다. 초기에 많은 오해가 있었는 부분이 “ftp는 udp 전송을 한다”라는 오해였는데 최근에는 더이상 udp와 tcp를 혼돈하는 경우는 없다.
ftp는 두개의 session을 사용하는데 기본적으로 명령어를 서로 주고 받기 위한 control session과 데이터를 송수신하기 위한 data session이 그것이다.
control session
control session은 흔히 알려진 대로 client쪽에서 server쪽으로 접속 요청을 하고 id와 password를 입력하여 명령을 내릴 준비단계가 된 session이다. 포트는 21번 포트를 사용하며 sftp 등을 사용할 때는 20번 포트 또는 다른 포트로 변경을 하여 사용한다. 그러나 ftp의 공식 포트번호는 21번 포트이다.
일단 접속을 하면 GET, CWD, RETR, LIST 등의 명령을 보내는 통로가 되며 server에서는 이에 대한 응답을 보낸다.
data session
data session은 서버에서 데이터를 보내기 위한 session으로 기본적으로 20번 포트를 사용한다. 20번 포트는 server측의 포트로 server에서 client로 접속하기 위한 기본 포트이다. 이 때 개념적인 server, client와 물리적인 server, client 때문에 많은 혼동이 생길 수 있는데 그 개념은 접어두고 일단 control을 위한 session과 data를 위한 session은 분리되어 있다는 것을 명심해야 한다.
acive모드와 passive모드
Active Mode는 data session을 맺을 때 server(client가 접속한 server)측에서 20번 포트로 client가 지정한 port로 접속을 요청한다 ( server port : 20, client port : client가 통보해주는 랜덤).
그리고 client측에서 control session을 통해 LIST, RETR 등의 명령을 내리면 그 결과를 data session으로 전송해 준다.
Passive Mode는 data session을 server에서 접속하는 것이 아니라 passive mode로 진입하겠다고 client가 통보하면 server에서 접속 가능한 data port 번호를 알려준다. 이 때 이 port 번호로 client가 다시 접속 시도를 해서 접속이 되면 이 data session을 통해 데이터를 받게 된다.
다음의 그림을 보면 명확하게 이해가 될 것이다.
(그림출처 : taeho’s life logger )
이 때 firewall의 정책이 각각의 ftp server 운영 정책에 따라 달라지는데 active mode의 경우 data session 20번 port의 outbound target을 ANY(*.*.*.*;*)로 open 해 주어야 하고 passive mode의 경우 server의 1024번 이상 포트 inbound ANY(*.*.*.*;*)를 열어주어야 한다.
passive mode port 번호 추출
passive mode로 진입하기 위해 서버에 명령을 전송하면 서버에서는
227 Entering Passive Mode ( 111,222,333,444,123,456)
와 같은 메시지가 전송되어 온다. 이 때 111,222,333,444는 서버의 주소이므로 현재 접속한 서버와 동일할 것이다. 문제는 뒤의 123, 456 위치에 있는 숫자인데 이 숫자가 서버에서 대기하고 있는 data session용 포트번호이다.
ftp를 구현하여 데이터 수신 프로그램을 작성하는 경우 client에서 이 포트번호를 추출하기 위해서는 다음과 같은 코딩을 하여 추출할 수 있다.
char ip1[10], ip2[10], ip3[10], ip4[10]; int port1, port2; ... 중략 ... if ( atoi(szBuffer) == 227 ) { /* passive mode ok */ sscanf(szBuffer, "%[^','],%[^','],%[^','],%[^','],%d,%d", ip1, ip2, ip3, ip4, &port1, &port2); nRetCode = port1 * 256 + port2; }
server의 port번호는 123 * 256 + 456 이다.
마지막줄에 오타난것 같습니다.
“server의 port번호는 123 * 254 + 456 이다.”
=> 123 * 256 + 456
감사합니다. 오타 수정하였습니다.
[…] + 더 읽기 […]