Python에는 Django를 비롯하여 Flask, Bottle 등 다양하고 많은 양의 Web Framework tool 이 존재한다. 그들을 사용하면 쉽게 파이썬을 이용하여 서버와 클라이언트의 서버 환경을 구축 가능하다. 그러나 이 모든 tool 들이 핵심적으로 이용하는 하나의 모듈이 있으니, 바로 socket 모듈이다. socket 모듈을 이용하면 네트워크에 관련된 필요한 대부분의 기능들, 가령 TCP나 UDP의 클라이언트나 서버를 빠른 시간 내에 작성한다거나 등의 일을 할 수 있다. 이 글에서는 간단한 클라이언트와 서버를 작성하는 방법을 살펴보자.

TCP Client

침투 테스팅을 하면 TCP Client를 만들어야 하는 수많은 경우가 생긴다. 가령 서비스 (윈도우서의 백그라운드 프로세스)를 테스트 하는 경우나, 랜덤 데이터를 보내는 경우나, 퍼징(fuzz: 랜덤 데이터를 반복적으로 소프트웨어에 입력함으로써 에러를 발생시키어 취약점을 발견해내는 기법)을 하거나 등등의 일이 그 경우이다. 만약 대기업이나 그와 유사한 상황에서 일을 하는 경우라면 보안상의 이유로 아마 네트워크에 관련된 멋진 도구들, 예를 들자면 nc와 nmap 등을 사용할 수 없을 것이다. 이런 경우 TCP Client를 사용하면 굉장히 편리해진다. 다음은 간단한 TCP Client의 소스코드이다.

import socket

t_host = "www.google.com"
t_port = 80

# 소켓 객체 생성
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 클라이언트 연결
client.connect((t_host, t_port))

# HTTP 규약에 따른 데이터 보내기
client.send("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")

response = client.recv(4096)

print response

 먼저 AF_INET과 SOCK_STREAM 인자를 주어 소켓 객체를 생성한다. AF_INET은 IPv4을 사용하겠다는 의미이며, SOCK_STREAM은 TCP 연결을 하겠다는 의미이다. 아마 C언어에서 Socket Programming을 해보신 분이라면 쉽게 이해할 수 있을 것이다. 그리고는 Client를 Server에 연결하여, 데이터를 보낸다. 이후 보낸 데이터에 대해 받은 응답을 출력한다. 굉장히 간단한 형태이지만, 아마 자주 사용하게 될 것이다(HTTP 자체에 대한 이해도가 높아진다면).

 위의 코드에서 주의할 점은, 위의 코드는 세 가지 가정을 하고 있다는 점이ë. 1첫번째로는, 연결이 무조건 성공할 경우를 가정한다는 것이다. 2두번째로는, 서버가 항상 우리가 데이터를 먼저 보내리라는 것을 가정한다는 것이다(가령 예를 들면 서버가 먼저 우리에게 데이터를 보내고 응답을 기다리는 경우도 있을 것이다). 3마지막으로 서버가 항상 응답을 빠르게 하리라는 가정을 한다는 것이다. 왠만한 대부분의 경우에는 위의 가정이 맞기에 간단한 형태로 소스코드를 작성하였지만, 실제의 상황에서는 더욱 다양한 경우가 있을 수 있기에 해당하는 상황에 알맞는 예외처리 등을 해주어야 한다.

UDP Client

UDP Client는 TCP Client에서 약간의 변화만 주면 된다.

import socket
t_host = "127.0.0.1"
t_port = 80

# 소켓 객체 생성
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 데이터 보내기
client.sendto("THIS IS APPLE", (t_host,t_port))

data, addr = client.recvfrom(4096)

print data

 UDP 연결을 위하여 SOCK_STREAM을 SOCK_DGRAM 으로 변경하였다. 그리고 UDP의 특성 상 초기에 연결이 필요 없으므로 그저 데이터를 보내면, 받는다. 훨씬 간단하다.


TCP Server

Python에서 TCP Server를 만드는 일은 TCP Client를 만드는 일처럼 쉽다. 간단한 멀티 쓰레딩이 되는 TCP Server를 작성해보자.

import socket
import threading

bind_ip    = "0.0.0.0"
bind_port  = 8080

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server.bind((bind_ip, bind_port))

server.listen(5)

print "[*] %s:%d 에서 입력 대기 중" % (bind_ip, bind_port)

# 여러 클라이언트를 처리하는 쓰레드
def handle_client(client_socket):
	# 클라이언트가 보낸 데이터 출력
	request = client_socket.recv(1024)

	print "[*] Received: %s" % request

	# 응답 신호 보내기
	client_socket.send("ACK!")

	client_socket.close()

	while True:
		client, addr = server.accept()

		print "[*] %s:%d 로부터 연결 수립" % (addr[0], addr[1])

		client_handler = threading.Thread(target=handle_client, args=(client, ))
		client_handler.start()