213 lines
4.9 KiB
Plaintext
213 lines
4.9 KiB
Plaintext
unit ObexParserUnit;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils, System.Classes, System.Generics.Collections,
|
|
Winapi.WinSock2; // ntohs, ntohl 함수 사용
|
|
|
|
// OBEX 헤더 ID 상수
|
|
const
|
|
OBEX_HID_NAME = $01;
|
|
OBEX_HID_BODY = $48;
|
|
OBEX_HID_BODY_FINAL = $49;
|
|
OBEX_HID_LENGTH = $C3;
|
|
|
|
type
|
|
|
|
|
|
{ TObexHeader: 파싱된 개별 헤더 (ID, Value)를 저장 }
|
|
TObexHeader = class
|
|
public
|
|
HeaderID: Byte;
|
|
Value: TBytes;
|
|
end;
|
|
|
|
{ TObexPacket: OBEX 패킷 전체를 파싱하고 데이터를 보관 }
|
|
TObexPacket = class
|
|
private
|
|
FHeaders: TList<TObexHeader>;
|
|
FOpcode: Byte;
|
|
FPacketLength: Word;
|
|
|
|
function GetHeader(HeaderID: Byte): TObexHeader;
|
|
function GetBody: TBytes;
|
|
function GetName: string;
|
|
function GetTotalLength: Cardinal;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
|
|
// 이 함수가 사용자가 요청한 "캐스팅 함수"입니다.
|
|
procedure Parse(Buffer: PByte; Size: Integer);
|
|
|
|
// 주요 속성
|
|
property Opcode: Byte read FOpcode;
|
|
property PacketLength: Word read FPacketLength;
|
|
property Name: string read GetName; // 0x01 헤더
|
|
property TotalLength: Cardinal read GetTotalLength; // 0xC3 헤더
|
|
property Body: TBytes read GetBody; // 0x48 또는 0x49 헤더
|
|
end;
|
|
|
|
implementation
|
|
|
|
{ TObexPacket }
|
|
|
|
constructor TObexPacket.Create;
|
|
begin
|
|
inherited;
|
|
FHeaders := TList<TObexHeader>.Create;
|
|
end;
|
|
|
|
destructor TObexPacket.Destroy;
|
|
var
|
|
Header: TObexHeader;
|
|
begin
|
|
for Header in FHeaders do
|
|
Header.Free;
|
|
FHeaders.Free;
|
|
inherited;
|
|
end;
|
|
|
|
function TObexPacket.GetHeader(HeaderID: Byte): TObexHeader;
|
|
var
|
|
Header: TObexHeader;
|
|
begin
|
|
Result := nil;
|
|
for Header in FHeaders do
|
|
begin
|
|
if Header.HeaderID = HeaderID then
|
|
Exit(Header);
|
|
end;
|
|
end;
|
|
|
|
function TObexPacket.GetBody: TBytes;
|
|
var
|
|
Header: TObexHeader;
|
|
begin
|
|
// Body-Final (0x49)을 먼저 찾고, 없으면 Body (0x48)를 찾음
|
|
Header := GetHeader(OBEX_HID_BODY_FINAL);
|
|
if Header = nil then
|
|
Header := GetHeader(OBEX_HID_BODY);
|
|
|
|
if Header <> nil then
|
|
Result := Header.Value
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TObexPacket.GetName: string;
|
|
var
|
|
Header: TObexHeader;
|
|
begin
|
|
Result := '';
|
|
Header := GetHeader(OBEX_HID_NAME);
|
|
if (Header <> nil) and (Length(Header.Value) > 0) then
|
|
begin
|
|
// OBEX 이름은 UTF-16 Big Endian, 마지막 2바이트는 널 종결
|
|
Result := TEncoding.BigEndianUnicode.GetString(Header.Value, 0, Length(Header.Value) - 2);
|
|
end;
|
|
end;
|
|
|
|
function TObexPacket.GetTotalLength: Cardinal;
|
|
var
|
|
Header: TObexHeader;
|
|
begin
|
|
Result := 0;
|
|
Header := GetHeader(OBEX_HID_LENGTH);
|
|
if (Header <> nil) and (Length(Header.Value) = 4) then
|
|
begin
|
|
// 32비트 Big Endian (Network) 값을 Host (Little Endian)로 변환
|
|
Result := ntohl(PCardinal(Header.Value)^);
|
|
end;
|
|
end;
|
|
|
|
procedure TObexPacket.Parse(Buffer: PByte; Size: Integer);
|
|
var
|
|
CurrentOffset: Integer;
|
|
Header: TObexHeader;
|
|
HeaderID: Byte;
|
|
HeaderLen: Word;
|
|
ValueLen: Integer;
|
|
begin
|
|
// 기존 헤더 클리어
|
|
for Header in FHeaders do
|
|
Header.Free;
|
|
FHeaders.Clear;
|
|
|
|
if (Buffer = nil) or (Size < 3) then
|
|
Exit; // 최소 3바이트 (Opcode + PacketLength)
|
|
|
|
FOpcode := Buffer[0];
|
|
// 16비트 Big Endian (Network) 값을 Host (Little Endian)로 변환
|
|
FPacketLength := ntohs(PWord(@Buffer[1])^);
|
|
|
|
// 패킷 크기 검증
|
|
if FPacketLength > Size then
|
|
Exit; // 실제 버퍼 크기보다 패킷 길이가 더 큼 (잘못된 데이터)
|
|
|
|
CurrentOffset := 3; // 헤더 시작 위치
|
|
|
|
// 헤더 파싱 루프
|
|
while CurrentOffset < FPacketLength do
|
|
begin
|
|
Header := TObexHeader.Create;
|
|
HeaderID := Buffer[CurrentOffset];
|
|
Header.HeaderID := HeaderID;
|
|
Inc(CurrentOffset);
|
|
|
|
// 헤더 ID의 상위 2비트로 타입을 구분 (01=유니코드, 10=바이트시퀀스, 11=4바이트정수)
|
|
case (HeaderID and $C0) of
|
|
$00, // Name (0x01)
|
|
$40: // Body (0x48, 0x49)
|
|
begin
|
|
// 헤더 길이 (2바이트 Big Endian)
|
|
if (CurrentOffset + 2) > FPacketLength then
|
|
begin
|
|
Header.Free;
|
|
Break;
|
|
end;
|
|
|
|
HeaderLen := ntohs(PWord(@Buffer[CurrentOffset])^);
|
|
Inc(CurrentOffset, 2);
|
|
|
|
// [수정] 헤더 길이가 최소 3바이트인지 확인
|
|
if HeaderLen < 3 then
|
|
begin
|
|
Header.Free;
|
|
Break;
|
|
end;
|
|
|
|
// 실제 값 길이 (전체 헤더 길이 - ID(1) - Length(2))
|
|
ValueLen := HeaderLen - 3;
|
|
if (CurrentOffset + ValueLen) > FPacketLength then
|
|
begin
|
|
Header.Free;
|
|
Break; // 패킷이 거짓말을 하고 있음
|
|
end;
|
|
|
|
SetLength(Header.Value, ValueLen);
|
|
Move(Buffer[CurrentOffset], Header.Value[0], ValueLen);
|
|
Inc(CurrentOffset, ValueLen);
|
|
end;
|
|
|
|
$C0: // Length (0xC3)
|
|
begin
|
|
// 4바이트 정수 (값 자체가 4바이트)
|
|
ValueLen := 4;
|
|
SetLength(Header.Value, ValueLen);
|
|
Move(Buffer[CurrentOffset], Header.Value[0], ValueLen);
|
|
Inc(CurrentOffset, ValueLen);
|
|
end
|
|
else
|
|
// 알 수 없는 헤더 타입
|
|
Break;
|
|
end;
|
|
|
|
FHeaders.Add(Header);
|
|
end;
|
|
end;
|
|
|
|
|
|
end.
|