- PySkyWiFi는 결제 전에도 열리는 항공 마일리지 계정 프로필 필드를 임시 저장소로 써서, 장거리 비행 중 HTTP 요청과 응답을 아주 느리게 왕복시키는 실험임
- 초기 프로토타입은 계정의 이름 필드를 갱신·폴링해 채팅을 만들고, 이어 지상 데몬이
STOCKPRICE: APPL같은 요청을 읽어 결과를 되돌려주는 방식으로 확장됨 - 최종 구조는 비행기 안의 sky proxy와 인터넷에 연결된 ground daemon으로 나뉘며, 로컬
curl요청의X-PySkyWiFi헤더로 대상 URL을 전달함 - 전송은 HTTP 요청을 작은 청크로 쪼갠 뒤
DATA,ACK,END세그먼트와 6자리 시퀀스 번호로 순서를 맞추는 방식임 - 실제 항공사 계정에 자동 요청을 계속 보내는 방식은 피했고, 최종 테스트는 GitHub Gist로 블로그 홈페이지를
curl호출해 몇 분 뒤 HTML 응답을 받는 수준에 머무름
결제 전 Wi-Fi 포털에서 발견한 우회 통로
- 비행기 Wi-Fi 로그인 페이지는 결제를 요구했지만, 결제 전에도 항공 마일리지 계정 로그인은 무료로 허용함
- 프로필 편집 화면의 이름 필드를 보고, 이 값을 외부와 주고받는 작은 통신 채널처럼 사용할 수 있다고 봄
- 구현 언어는 처음에 Go를 생각했지만, Python을 쓰면 도구 이름을 PySkyWiFi로 붙일 수 있어 Python을 선택함
- 소스 코드는 PySkyWiFi에 공개됨
프로토타입 1: 이름 필드로 만든 인스턴트 메시징
- 같은 항공 마일리지 계정에 두 클라이언트가 로그인한 뒤, 이름 필드를 번갈아 갱신하고 읽는 방식으로 메시지를 주고받음
- 한쪽이 이름을
Hello how are you로 바꾸면 다른 쪽이 이를 읽음 - 다시
Im fine how are you로 바꾸면 첫 번째 쪽이 답장을 확인함
- 한쪽이 이름을
- UI에서 직접 이름을 바꾸는 방식은 번거로워 커맨드라인 도구로 자동화함
- 도구는 웹사이트에 로그인해 테스트 계정의 이름 필드를 메시지로 갱신함
- 몇 초마다 이름 필드를 폴링하고, 값이 바뀌면 상대의 답장으로 출력함
- 실제 항공 마일리지 계정에는 더 이상 자동 데이터를 보내지 않기로 하고, 이름을 빠르게 약 10번 갱신해 모두 성공하는지만 확인함
- 이 결과는 해당 계정이 요청 속도나 개수에 대해 제한을 걸지 않았을 가능성을 보여줌
- 이후 개발은 GitHub Gists와 로컬 파일로 같은 원리를 재현해 진행함
- 항공 마일리지 계정보다 반복 개발이 빠르고 쉬웠기 때문임
프로토타입 2: 지상 데몬으로 외부 정보 가져오기
- 다음 단계에서는 지상 또는 클라우드의 인터넷 연결 컴퓨터에서 데몬을 실행함
- 데몬은 항공 마일리지 계정의 이름 필드를 계속 폴링하며, 비행기에서 보낸 구조화된 요청을 찾음
- 예:
STOCKPRICE: APPL - 예:
SCORE: MANUNITED
- 예:
- 새 요청을 발견하면 데몬이 이를 파싱하고 관련 API로 정보를 가져온 뒤, 항공 마일리지 계정을 통해 비행기 쪽으로 되돌려줌
- 이 단계부터 단순 채팅을 넘어 주가나 스포츠 점수 같은 실시간 정보 조회가 가능해짐
sky proxy와 ground daemon으로 구성한 전체 구조
- PySkyWiFi는 HTTP 요청 전체를 항공 마일리지 계정으로 밀어 넣고, 지상 컴퓨터가 실제 HTTP 요청을 수행한 뒤 응답을 같은 방식으로 올려보내는 TCP/IP 유사 프로토콜임
- 구성 요소는 두 개임
- sky proxy: 비행기 안 노트북에서 실행되는 프록시
- ground daemon: 집이나 클라우드의 인터넷 연결 컴퓨터에서 실행되는 데몬
- 사용 전 지상 데몬을 먼저 실행하고, 비행기에서 Wi-Fi에 연결한 뒤 노트북에서 sky proxy를 실행함
- 사용자는
curl같은 도구로 로컬 프록시에 HTTP 요청을 보냄- 예:
curl localhost:1234 -H "X-PySkyWiFi: example.com" - 실제 대상 URL은
X-PySkyWiFi커스텀 헤더에 넣음
- 예:
- 지상 데몬은
X-PySkyWiFi헤더를 제거하고 이를 대상 웹사이트 라우팅에 사용함- 요청 본문과 다른 헤더는 그대로 전달함
- 모든 과정이 끝나면 일반 인터넷 요청처럼 HTTP 응답을 받지만, 항공기 네트워크 상태에 따라 요청이 몇 분 동안 멈춰 있고 속도도 초당 몇 바이트 수준일 수 있음
청크, ACK, 두 번째 계정으로 HTTP 왕복하기
- sky proxy는
curl요청을 받아 전체 HTTP 요청을 청크로 나눔- 항공 마일리지 계정의 이름 필드에 한 번에 담을 수 있는 양이 작기 때문임
- 각 청크는 첫 번째 항공 마일리지 계정의 이름 필드에 순서대로 기록됨
- ground daemon은 계정을 폴링하다가 새 청크를 발견하면 읽고, 다음 청크를 보내도 된다는 수신 확인을 남김
- 전체 HTTP 요청이 재조립되면 ground daemon이 인터넷으로 실제 요청을 보냄
- HTTP 응답은 반대 방향으로 같은 절차를 거침
- ground daemon이 응답을 청크로 나눠 보냄
- 구현을 단순하게 하기 위해 응답 청크는 두 번째 항공 마일리지 계정을 사용함
- sky proxy가 청크를 읽어 HTTP 응답을 재조립하고 원래
curl호출에 반환함
전송 계층과 네트워크 계층의 역할 분리
- PySkyWiFi의 통신 로직은 전송 계층과 네트워크 계층으로 나뉨
- 전송 계층은 어떤 데이터를 어떤 순서로 보낼지 결정함
- 긴 메시지를 작은 청크로 나누는 방식
- 수신자가 다음 청크를 받을 준비가 됐음을 알리는 방식
- TCP와 아주 느슨하게 비슷한 역할
- 네트워크 계층은 전송 계층이 정한 데이터를 실제 저장소에 쓰고 읽음
- 항공 마일리지 계정, 로컬 파일, Discord 프로필 같은 저장소가 될 수 있음
- 이런 분리는 새 항공사 마일리지 플랫폼을 붙이기 쉽게 만듦
- 새 네트워크 계층만 구현하면 전송 계층은 그대로 유지됨
- 테스트용 네트워크 계층은 항공 마일리지 계정 대신 로컬 파일을 읽고 쓸 수 있음
DATA, ACK, END로 맞추는 전송 규칙
- PySkyWiFi 전송 연결은 두 개의 파이프로 구성됨
- 각 클라이언트는 쓰기용 SEND 파이프와 읽기용 RECV 파이프를 가짐
- 항공 마일리지 계정을 파이프로 사용할 수 있음
- PSWF 클라이언트는 특정 순간에 보내기 또는 받기 중 하나만 가능함
- 보내기 모드에서는 상대가 보낸 데이터를 보지 못함
- 받기 모드에서는 데이터를 보내도 상대가 보지 못함
- TCP처럼 언제든 양방향 송수신하는 방식은 아님
- 긴 메시지는
DATA,ACK,END세그먼트로 전송함DATA: 문자D, 6자리로 패딩된 시퀀스 번호, 실제 청크 데이터로 구성됨- 예:
D000451adline": "Mudslide in Wigan causes m ACK: 문자A와 확인 대상 6자리 시퀀스 번호로 구성됨- 예:
A000451 END: 문자E하나로 메시지 종료를 표시함
- 송신자는
DATA를 보낸 뒤 해당 시퀀스 번호의ACK를 받을 때까지 기다림A000451을 받으면000451청크가 처리됐다고 보고000452청크를 보냄
- 수신자가
END를 보면 메시지가 끝난 것으로 처리하고, 송신자와 수신자가 역할을 바꿈
네트워크 계층 구현 인터페이스
- 네트워크 계층은 데이터를 저장소에 쓰고 읽는 단순한 인터페이스를 제공함
- 필수 연산은 두 개임
send(msg: str):msg를 저장소에 기록함recv() -> str: 저장소에서 메시지를 읽음
- 항공 마일리지 기반 구현에서는 두 연산이 계정의 이름 필드를 쓰고 읽는 동작이 됨
- 필수 속성도 두 개임
sleep_for: RECV 파이프를 폴링하는 사이에 전송 계층이 대기할 초 단위 시간segment_data_size: 한 세그먼트에 담을 문자 수
sleep_for는 파일 같은 테스트 구현에서는 낮게 둘 수 있지만, 항공 마일리지 계정 구현에서는 원격 서버에 너무 많은 요청을 보내지 않도록 최소 몇 초로 둬야 함segment_data_size는 사용하는 항공 마일리지 계정 필드의 최대 크기와 같아야 하며, 예시는 약 20자임- 선택 훅은 두 개임
connect_send(): SEND 파이프 초기화 시 호출되며, 항공 마일리지 구현에서는 사용자명과 비밀번호로 로그인해 쿠키를 얻는 데 사용될 수 있음connect_recv(): RECV 파이프 초기화 시 호출됨
base26 인코딩과 대역폭 개선 아이디어
- 항공 마일리지 HTML 폼은 이름에 알파벳이 아닌 문자를 허용하지 않을 수 있음
Stephen은 허용될 수 있음GET /data?id=5는 거부될 수 있음
- 이를 피하려면 네트워크 계층이 세그먼트를 항공 마일리지 계정에 쓰기 전에 base26으로 인코딩해야 함
- base26은 문자열을
A부터Z까지의 문자만 사용해 표현함 - 바이트 문자열을 큰 숫자로 바꾼 뒤, 26진법 숫자처럼
A~Z문자로 표현함 - 예:
b26_encode("Hello world")는CZEZINADXFFTZEIDPKM를 반환함
- base26은 문자열을
- 전송 계층은 이 인코딩을 알 필요가 없음
- 네트워크 계층이 바이트를 base26으로 바꿔 기록하고, 읽을 때 다시 바이트로 복원함
- base26은 문자열을 상당히 길게 만들어 대역폭을 줄임
- 대문자와 소문자를 모두 쓰는 base52를 사용하면 더 짧아질 수 있지만, 이는 버전 2 개선사항으로 남겨둠
- 대역폭은 세그먼트 크기를 키워 늘릴 수 있음
- 필드 1개가 20자만 저장할 수 있어도, 20자 필드 5개를 동시에 쓰면 100자 세그먼트를 한 요청으로 보낼 수 있음
- 수신자는 5개 필드를 한 요청으로 읽어 다시 이어 붙일 수 있음
HTTP CONNECT가 남긴 개선 여지와 최종 테스트
- PySkyWiFi는 수동으로 HTTP 요청을 주고받는 대신 HTTP CONNECT requests를 사용하면 더 나은 터널을 만들 수 있음
CONNECT는 대부분의 HTTP 프록시가 사용하는 방식이며, 이를 쓰면 PySkyWiFi가 시스템 수준 프록시처럼 동작해 브라우저 요청도 처리할 수 있음CONNECT를 쓰면 PySkyWiFi가 대상 웹사이트와 직접 TLS 연결을 협상하므로, 항공 마일리지 계정을 통과하는 트래픽이 암호화됨- 단점은 구현 작업이 훨씬 많아진다는 점임
- 최종 테스트에서는 GitHub Gist를 통해 데이터를 터널링해
curl로 블로그 홈페이지를 불러왔고, 몇 분 뒤 HTML 응답을 받음