Zmodem 프로토콜의 구조와 동작 원리

개요

Zmodem은 1980년대 후반에 개발된 효율적인 파일 전송 프로토콜로, 직렬 통신 환경에서 신뢰성 있고 빠른 데이터 전송을 목표로 합니다. 이 프로토콜은 양방향 제어 신호를 사용하지만 실제 데이터는 단방향으로 흐르며, 오류 복구, 재전송, 스트리밍 최적화 기능을 제공합니다.

기본적인 전송 구조는 다음과 같습니다:

  [보내는 측] ----(데이터 채널)----> [받는 측]
             <----(제어 채널)-----
모든 통신은 **프레임**(frame) 단위로 구성되며, 각 프레임은 헤더와 선택적으로 데이터 조각(subpacket)으로 이루어집니다.

프레임 형식

Zmodem은 세 가지 형태의 프레임 인코딩을 지원합니다:
  • HEX (16진수): ASCII 문자만 사용. 주로 제어 채널에서 사용.
  • BIN16: 바이너리 데이터 + 16비트 CRC 보호.
  • BIN32: 바이너리 데이터 + 32비트 CRC 보호 (높은 신뢰성).

HEX 프레임 형식

ZPAD ZPAD ZDLE ZHEX [타입] [정보x4] [CRC1] [CRC2] CR LF [XON]
- 모든 값은 두 자리 16진수 문자열로 인코딩됨. - 대문자 허용 안 됨. - XON은 ZACK/ZFIN 외 모든 프레임에 포함되어 흐름 제어를 보장함.

BIN16 / BIN32 프레임 형식

ZPAD ZDLE [스타일] [타입] [정보x4] [CRC...]
- BIN16: 스타일 = 'A' (0x41) - BIN32: 스타일 = 'C' (0x43) - 정보 필드 이후 데이터 조각이 올 수 있음.

헤더 구조

모든 프레임은 공통 헤더를 포함하며, 다음 요소로 구성됩니다:
  1. 프레임 타입: 1바이트, 동작 유형 식별.
  2. 정보 필드: 4바이트 (P0~P3), 오프셋 또는 플래그 포함.
  3. CRC: 무결성 검사를 위한 체크섬 (16비트 또는 32비트).
예를 들어, ZRINIT 프레임은 수신기가 초기화되었음을 알리고, 지원하는 기능들을 비트 플래그로 전달합니다.

주요 프레임 타입

타입방향용도
ZRQINIT0x00S→R수신기 초기화 요청
ZRINIT0x01R→S수신기 기능 보고
ZFILE0x04S→R파일 메타데이터 전송
ZDATA0x0AS→R실제 파일 데이터 전송 시작
ZEOF0x0BS→R파일 전송 종료 알림
ZRPOS0x09R→S특정 위치에서 재전송 요청
ZFIN0x08S→R세션 종료 요청

데이터 조각 (Subpacket)

ZDATA와 같은 프레임은 하나 이상의 데이터 조각을 포함할 수 있습니다. 각 조각은 다음과 같은 구조를 가집니다:
[데이터...] ZDLE [종류] [CRC]
여기서 ZDLE는 0x18로, 특수 제어 시퀀스의 시작을 나타냅니다. 실제 데이터 내에 0x18이 포함될 경우, ZDLEE(0x58)로 이스케이프 처리됩니다.

주요 데이터 조각 종류

  • ZCRCW: "Wait" — 수신 측이 응답(ZACK)을 보내야 함. 버퍼 정리를 위해 사용.
  • ZCRCE: "End" — 오류 발생 시에만 응답.
  • ZCRCQ: "Query" — 비동기 응답 요구 (전이중 모드에서 사용).
  • ZCRCG: "Go" — 오류 없으면 계속 전송.
최대 데이터 크기는 일반적으로 1024바이트이며, 환경에 따라 조정 가능. 일부 구현에서는 8KB까지 확장 지원.

이스케이프 인코딩 (ZDLE)

통신 중 제어 문자와 데이터 충돌을 방지하기 위해 Zmodem은 **ZDLE 인코딩**을 사용합니다. 특정 바이트 값은 전송 전 변환되며, 수신 측에서 복원됩니다.

다음은 대표적인 이스케이프 시퀀스입니다:

0x18 → 0x18 0x58     // ZDLE itself
0x10 → 0x18 0x50     // DLE
0x11 → 0x18 0x51     // XON
0x13 → 0x18 0x53     // XOFF
0x7F → 0x18 0x6C     // RUBOUT (DEL)
0xFF → 0x18 0x6D     // RUBOUT (extended)
또한, Telenet과 같은 오래된 네트워크 호환성을 위해 CR @ CR 시퀀스도 보호됩니다.

세션 흐름

초기화

  1. 송신자는 ZRQINIT 전송.
  2. 수신자는 ZRINIT 응답, 기능 플래그 포함.
  3. 필요 시 ZSINIT으로 추가 설정 교환.
  4. 수신자는 ZACK으로 확인.

파일 전송

  1. 송신자는 ZFILE + 파일 메타데이터 전송.
  2. 수신자는 필요 시 ZSKIP으로 건너뛸 수 있음.
  3. 수신자는 ZRPOS로 시작 오프셋 지정 (재시작 지원).
  4. 송신자는 ZDATA 프레임으로 데이터 전송 시작.
  5. 수신자는 정기적으로 ZACK 또는 ZRPOS로 상태 피드백.
  6. 파일 종료 시 ZEOF 전송.

세션 종료

  1. 송신자는 ZFIN 전송.
  2. 수신자는 ZFIN 응답.
  3. 송신자는 "OO" 문자열 전송 후 종료.
  4. 양측 연결 종료.

파일 메타데이터 (ZFILE)

ZFILE 프레임 다음에는 NUL 문자로 구분된 문자열 조각이 옵니다:
[경로명]\0[크기]\0[수정일]\0[모드]\0[남은 파일 수]\0\0
- 경로명: 절대 경로 금지, 디렉터리 구분자는 '/' 사용. - 크기: 10진수 문자열. - 수정일: UNIX 타임스탬프 (1970년 기준 초 단위). - 모드: 일반적으로 Unix 권한 (8진수), 비유닉스 시 0. - 남은 파일 수: 배치 전송 시 예상 갯수. 메타데이터 전체 길이는 1024바이트를 초과할 수 없음.

오류 및 복구

- 손상된 프레임: 수신자는 ZNAK 전송. - 위치 불일치: 수신자는 ZRPOS로 올바른 오프셋 요청. - 재시작 가능: 이전에 중단된 전송은 ZRPOS를 통해 이어서 진행 가능. - 연결 끊김: 5회 연속 CAN(0x18) 수신 시 강제 종료.

보안 고려사항

ZCHALLENGE는 간단한 난수 검증 메커니즘을 제공하지만, 암호학적으로 안전하지 않음. 현재 환경에서는 의존하지 않는 것이 좋습니다. 대신, 전송 전 인증이나 암호화 계층(예: SSH)을 사용해야 합니다.

상수 정의

#define ZPAD    0x2A        // 프레임 시작 패딩
#define ZDLE    0x18        // 이스케이프 문자
#define ZBIN    0x41        // BIN16 프레임
#define ZHEX    0x42        // HEX 프레임
#define ZBIN32  0x43        // BIN32 프레임

#define ZRQINIT 0x00
#define ZRINIT  0x01
#define ZFILE   0x04
#define ZDATA   0x0A
#define ZEOF    0x0B
#define ZFIN    0x08
#define ZRPOS   0x09

왜 이스케이프가 필요한가?

역사적으로, 많은 통신 장비는 제어 문자(예: XON/XOFF)를 데이터 스트림에서 해석하여 의도치 않게 전송을 중단하거나 조절하는 문제가 있었습니다. ZDLE 인코딩은 이러한 충돌을 방지하고, 바이너리 데이터도 안전하게 전송할 수 있도록 보장합니다. 이는 모뎀, 직렬 포트, 터미널 서버 등 오래된 인프라와의 호환성을 유지하기 위한 필수 설계입니다.

태그: Zmodem 파일 전송 프로토콜 통신 프로토콜 이스케이프 인코딩 CRC

6월 7일 19:44에 게시됨