File size: 4,044 Bytes
3c8ff75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// tcpUtils.js
/**
 * 解析TCP头部信息
 * @param {Buffer} packet - IP数据包(包含TCP负载)
 * @param {number} ipHeaderLength - IP头部长度
 * @returns {Object} TCP头部信息
 */
function parseTCPHeader(packet, ipHeaderLength) {
  if (packet.length < ipHeaderLength + 20) {
    throw new Error('TCP数据包太短,无法解析TCP头部');
  }

  const tcpStart = ipHeaderLength;
  const sourcePort = (packet[tcpStart] << 8) + packet[tcpStart + 1];
  const destPort = (packet[tcpStart + 2] << 8) + packet[tcpStart + 3];
  const seqNum = (
    (packet[tcpStart + 4] << 24) +
    (packet[tcpStart + 5] << 16) +
    (packet[tcpStart + 6] << 8) +
    packet[tcpStart + 7]
  ) >>> 0; // 使用无符号右移确保得到正数
  const ackNum = (
    (packet[tcpStart + 8] << 24) +
    (packet[tcpStart + 9] << 16) +
    (packet[tcpStart + 10] << 8) +
    packet[tcpStart + 11]
  ) >>> 0; // 使用无符号右移确保得到正数
  
  const dataOffset = (packet[tcpStart + 12] >> 4) & 0x0F; // TCP头部长度(以4字节为单位)
  const tcpHeaderLength = dataOffset * 4;
  
  // 控制位
  const flags = packet[tcpStart + 13];
  const isURG = (flags & 0x20) !== 0;
  const isACK = (flags & 0x10) !== 0;
  const isPSH = (flags & 0x08) !== 0;
  const isRST = (flags & 0x04) !== 0;
  const isSYN = (flags & 0x02) !== 0;
  const isFIN = (flags & 0x01) !== 0;
  
  const windowSize = (packet[tcpStart + 14] << 8) + packet[tcpStart + 15];
  const checksum = (packet[tcpStart + 16] << 8) + packet[tcpStart + 17];
  const urgentPointer = (packet[tcpStart + 18] << 8) + packet[tcpStart + 19];
  
  // 计算数据偏移量
  const dataOffsetBytes = tcpHeaderLength;
  
  return {
    sourcePort,
    destPort,
    seqNum,
    ackNum,
    tcpHeaderLength,
    isURG,
    isACK,
    isPSH,
    isRST,
    isSYN,
    isFIN,
    windowSize,
    checksum,
    urgentPointer,
    dataOffsetBytes,
    flags
  };
}

/**
 * 构造TCP数据包
 * @param {Object} options - TCP数据包选项
 * @returns {Buffer} 构造的TCP数据包
 */
function constructTCPPacket(options) {
  const {
    sourcePort,
    destPort,
    seqNum,
    ackNum,
    isACK = false,
    isSYN = false,
    isFIN = false,
    isRST = false,
    windowSize = 65535,
    data = Buffer.alloc(0)
  } = options;
  
  // TCP头部最小长度为20字节
  const tcpHeaderLength = 20;
  const packetLength = tcpHeaderLength + data.length;
  const packet = Buffer.alloc(packetLength);
  
  // 设置端口
  packet.writeUInt16BE(sourcePort, 0);
  packet.writeUInt16BE(destPort, 2);
  
  // 设置序列号和确认号
  packet.writeUInt32BE(seqNum, 4);
  packet.writeUInt32BE(ackNum, 8);
  
  // 设置数据偏移和保留位(数据偏移为5,表示20字节头部)
  packet[12] = (5 << 4) | 0; // 数据偏移=5,保留位=0
  
  // 设置标志位
  let flags = 0;
  if (isACK) flags |= 0x10;
  if (isSYN) flags |= 0x02;
  if (isFIN) flags |= 0x01;
  if (isRST) flags |= 0x04;
  packet[13] = flags;
  
  // 设置窗口大小
  packet.writeUInt16BE(windowSize, 14);
  
  // 校验和和紧急指针设为0(简化处理)
  packet.writeUInt16BE(0, 16); // 校验和
  packet.writeUInt16BE(0, 18); // 紧急指针
  
  // 添加数据
  if (data.length > 0) {
    data.copy(packet, tcpHeaderLength);
  }
  
  return packet;
}

/**
 * 检查是否为TCP SYN包(连接请求)
 * @param {Object} tcpHeader - TCP头部信息
 * @returns {boolean} 是否为SYN包
 */
function isSYNPacket(tcpHeader) {
  return tcpHeader.isSYN && !tcpHeader.isACK;
}

/**
 * 检查是否为TCP FIN包(连接终止)
 * @param {Object} tcpHeader - TCP头部信息
 * @returns {boolean} 是否为FIN包
 */
function isFINPacket(tcpHeader) {
  return tcpHeader.isFIN;
}

/**
 * 检查是否为TCP RST包(连接重置)
 * @param {Object} tcpHeader - TCP头部信息
 * @returns {boolean} 是否为RST包
 */
function isRSTPacket(tcpHeader) {
  return tcpHeader.isRST;
}

module.exports = {
  parseTCPHeader,
  constructTCPPacket,
  isSYNPacket,
  isFINPacket,
  isRSTPacket
};