Node.js - 解码的 WinPcap websocket 框架被切断
Node.js - Decoded WinPcap websocket frame is cut off
我正在尝试在 Windows 7 上使用 cap 模块和 Node.js (4.5.0) 来监听 websocket 流量。我能够 parse/decode正在捕获的载荷,但当载荷超过一定长度时,部分载荷被截断。
我正在使用 this example 解码 websocket 帧(wsDecoded 函数),我认为问题在于我从 cap 模块获得的响应在单个缓冲区中返回了整个响应,但是这个例子(以及我看过的其他一些 websocket 库)似乎期望在有效负载超过一定大小时处理多个帧。
我试过从中取出缓冲区并将其分解成更小的部分,但是当我这样做时,有效负载看起来就像随机垃圾。
var Cap = require('cap').Cap;
var decoders = require('cap').decoders;
var PROTOCOL = decoders.PROTOCOL;
var c = new Cap();
var device = Cap.findDevice(LOCAL_IP);
var filter = 'port 8088';
var bufSize = 10 * 1024 * 1024;
var buffer = new Buffer(65535);
var linkType = c.open(device, filter, bufSize, buffer);
c.setMinBytes && c.setMinBytes(0);
c.on('packet', function(nbytes, trunc) {
if (linkType === 'ETHERNET') {
var ret = decoders.Ethernet(buffer);
if (ret.info.type === PROTOCOL.ETHERNET.IPV4) {
ret = decoders.IPV4(buffer, ret.offset);
if (ret.info.protocol === PROTOCOL.IP.TCP) {
var datalen = ret.info.totallen - ret.hdrlen;
ret = decoders.TCP(buffer, ret.offset);
datalen -= ret.hdrlen;
var payload = wsDecoded(buffer, ret.offset, ret.offset + datalen);
console.log(payload.toString());
}
}
}
});
function wsDecoded (data, start, end) {
var message = data.slice(start, end);
var FIN = (message[0] & 0x80);
var RSV1 = (message[0] & 0x40);
var RSV2 = (message[0] & 0x20);
var RSV3 = (message[0] & 0x10);
var Opcode = message[0] & 0x0F;
var mask = (message[1] & 0x80);
var length = (message[1] & 0x7F);
var nextByte = 2;
if (length === 126) {
// length = next 2 bytes
nextByte += 2;
} else if (length === 127){
// length = next 8 bytes
nextByte += 8;
}
var maskingKey = null;
if (mask){
maskingKey = message.slice(nextByte, nextByte + 4);
nextByte += 4;
}
var payload = message.slice(nextByte, nextByte + length);
if (maskingKey){
for (var i = 0; i < payload.length; i++){
payload[i] = payload[i] ^ maskingKey[i % 4];
}
}
return payload;
}
未截断的示例结果:
{"Command":"FoldCards","Table":"Ring Game #02","Type":"R","Ghost":"No"}
被截断的示例结果:
{"Command":"Buttons","Table":"Ring Game #02","Type":"R","Button1":"","Button2":"Ready","Button3":"","Preflop":"No","Call":0,"M
使用 buffer.toString('binary', ret.offset, datalen) 时的有效负载 ('packet') 回调。这非常接近我所需要的,但它不会揭开有效载荷并在其中留下一些随机字符(我认为这是帧的开始和停止但不是 100% 确定)
?'{"Command":"TablesSitting","Tables":[]}?'{"Command":"TablesWaiting","Tables":[]}?~☺({"Command":"RingGameLobby","Clear":"Yes","Count":2,"ID":["Ring Game #01","Ring Game #02"],"Game":["NL Hold'em","NL Hold'em"],"GameIndex":[2,2],"Seats":[10,10],"StakesLo":[10,10],"StakesHi":[20,20],"BuyinMin":[400,400],"BuyinMax":[2000,2000],"Players":[0,0],"Waiting":[0,0],"Password":["No","No"]}?~☺{"Command":"TournamentLobby","Clear":"Yes","Count":0,"ID":[],"SnG":[],"Shootout":[],"Game":[],"GameIndex":[],"Buyin":[],"EntryFee":[],"Rebuy":[],"TS":[],"PreReg":[],"Reg":[],"Max":[],"Starts":[],"StartMin":[],"StartTime":[],"Running":[],"Tables":[],"Password":[]}?~ ?{"Command":"Logins","Clear":"Yes","Total":1,"Player":["harrythree"],"Name":[""],"Location":["Greenville"],"Login":["2016-08-27 13:26:45"]}
您收到的消息被截断了,因为您没有正确读取 payloadLength。
看一看:
var length = (message[1] & 0x7F);
var nextByte = 2;
if (length === 126) {
// in this case next 2 bytes store lengths. you have to read ones from the buffer
nextByte += 2;
} else if (length === 127){
// in this case next 8 bytes store lengths. you have to read ones from the buffer
nextByte += 8;
}
当消息大小超过 125 字节时,您必须像 RFC 所说的那样使用接下来的 2 或 8 个字节来读取正确的 payloadLength。在您的情况下,您只需跳过这些字节,以便您的 "length" 始终 <= 127,因此以下行会生成截断的有效负载数据:
var payload = message.slice(nextByte, nextByte + length);
^
always <= 127
我稍微修改了您的代码以调试问题。这是我正在谈论的实现示例:
var Cap = require('cap').Cap;
var decoders = require('cap').decoders;
var PROTOCOL = decoders.PROTOCOL;
var c = new Cap();
var device = Cap.findDevice('127.0.0.1');
var filter = 'port 3001';
var bufSize = 10 * 1024 * 1024;
var buffer = new Buffer(bufSize);
var linkType = c.open(device, filter, bufSize, buffer);
c.setMinBytes && c.setMinBytes(0);
c.on('packet', function(nbytes, trunc) {
console.log('************ packet: length ' + nbytes + ' bytes, truncated? '
+ (trunc ? 'yes' : 'no'));
if (linkType === 'ETHERNET') {
var ret = decoders.Ethernet(buffer);
if (ret.info.type === PROTOCOL.ETHERNET.IPV4) {
ret = decoders.IPV4(buffer, ret.offset);
if (ret.info.protocol === PROTOCOL.IP.TCP) {
var datalen = ret.info.totallen - ret.hdrlen;
ret = decoders.TCP(buffer, ret.offset);
datalen -= ret.hdrlen;
console.log('from: ' + ret.info.srcport + ' to ' + ret.info.dstport);
console.log("Data len: " + datalen);
var payload = wsDecoded(buffer, ret.offset, ret.offset + datalen);
if (payload) {
console.log("MSG:" + payload.toString());
} else {
console.log("Invalid message");
}
}
}
}
});
function wsDecoded (data, start, end) {
var message = data.slice(start, end);
var index = 0;
var FIN = (message[index] & 0x80);
var RSV1 = (message[index] & 0x40);
var RSV2 = (message[index] & 0x20);
var RSV3 = (message[index] & 0x10);
var Opcode = message[index] & 0x0F;
index++;
var masked = (message[index] & 0x80);
var payloadLength = message[index] & (~0x80);
index++;
if (payloadLength === 126){
// length = next 2 bytes
payloadLength = message.readUInt16BE(index);
index+=2;
} else if (payloadLength === 127) {
// read uint64
throw new Error("Implement me");
}
console.log("Opcode: " + Opcode + ", fin: " + FIN + ", masked: " + masked);
var maskingKey = null;
if (masked){
maskingKey = message.slice(index, index + 4);
index += 4;
}
console.log("Payload length: " + payloadLength);
var payload = message.slice(index, index + payloadLength);
if (payload.length != payloadLength) {
console.warn("Length mismatch");
return false;
}
if (maskingKey){
for (var i = 0; i < payload.length; i++){
payload[i] = payload[i] ^ maskingKey[i % 4];
}
}
return payload;
}
我正在尝试在 Windows 7 上使用 cap 模块和 Node.js (4.5.0) 来监听 websocket 流量。我能够 parse/decode正在捕获的载荷,但当载荷超过一定长度时,部分载荷被截断。
我正在使用 this example 解码 websocket 帧(wsDecoded 函数),我认为问题在于我从 cap 模块获得的响应在单个缓冲区中返回了整个响应,但是这个例子(以及我看过的其他一些 websocket 库)似乎期望在有效负载超过一定大小时处理多个帧。
我试过从中取出缓冲区并将其分解成更小的部分,但是当我这样做时,有效负载看起来就像随机垃圾。
var Cap = require('cap').Cap;
var decoders = require('cap').decoders;
var PROTOCOL = decoders.PROTOCOL;
var c = new Cap();
var device = Cap.findDevice(LOCAL_IP);
var filter = 'port 8088';
var bufSize = 10 * 1024 * 1024;
var buffer = new Buffer(65535);
var linkType = c.open(device, filter, bufSize, buffer);
c.setMinBytes && c.setMinBytes(0);
c.on('packet', function(nbytes, trunc) {
if (linkType === 'ETHERNET') {
var ret = decoders.Ethernet(buffer);
if (ret.info.type === PROTOCOL.ETHERNET.IPV4) {
ret = decoders.IPV4(buffer, ret.offset);
if (ret.info.protocol === PROTOCOL.IP.TCP) {
var datalen = ret.info.totallen - ret.hdrlen;
ret = decoders.TCP(buffer, ret.offset);
datalen -= ret.hdrlen;
var payload = wsDecoded(buffer, ret.offset, ret.offset + datalen);
console.log(payload.toString());
}
}
}
});
function wsDecoded (data, start, end) {
var message = data.slice(start, end);
var FIN = (message[0] & 0x80);
var RSV1 = (message[0] & 0x40);
var RSV2 = (message[0] & 0x20);
var RSV3 = (message[0] & 0x10);
var Opcode = message[0] & 0x0F;
var mask = (message[1] & 0x80);
var length = (message[1] & 0x7F);
var nextByte = 2;
if (length === 126) {
// length = next 2 bytes
nextByte += 2;
} else if (length === 127){
// length = next 8 bytes
nextByte += 8;
}
var maskingKey = null;
if (mask){
maskingKey = message.slice(nextByte, nextByte + 4);
nextByte += 4;
}
var payload = message.slice(nextByte, nextByte + length);
if (maskingKey){
for (var i = 0; i < payload.length; i++){
payload[i] = payload[i] ^ maskingKey[i % 4];
}
}
return payload;
}
未截断的示例结果:
{"Command":"FoldCards","Table":"Ring Game #02","Type":"R","Ghost":"No"}
被截断的示例结果:
{"Command":"Buttons","Table":"Ring Game #02","Type":"R","Button1":"","Button2":"Ready","Button3":"","Preflop":"No","Call":0,"M
使用 buffer.toString('binary', ret.offset, datalen) 时的有效负载 ('packet') 回调。这非常接近我所需要的,但它不会揭开有效载荷并在其中留下一些随机字符(我认为这是帧的开始和停止但不是 100% 确定)
?'{"Command":"TablesSitting","Tables":[]}?'{"Command":"TablesWaiting","Tables":[]}?~☺({"Command":"RingGameLobby","Clear":"Yes","Count":2,"ID":["Ring Game #01","Ring Game #02"],"Game":["NL Hold'em","NL Hold'em"],"GameIndex":[2,2],"Seats":[10,10],"StakesLo":[10,10],"StakesHi":[20,20],"BuyinMin":[400,400],"BuyinMax":[2000,2000],"Players":[0,0],"Waiting":[0,0],"Password":["No","No"]}?~☺{"Command":"TournamentLobby","Clear":"Yes","Count":0,"ID":[],"SnG":[],"Shootout":[],"Game":[],"GameIndex":[],"Buyin":[],"EntryFee":[],"Rebuy":[],"TS":[],"PreReg":[],"Reg":[],"Max":[],"Starts":[],"StartMin":[],"StartTime":[],"Running":[],"Tables":[],"Password":[]}?~ ?{"Command":"Logins","Clear":"Yes","Total":1,"Player":["harrythree"],"Name":[""],"Location":["Greenville"],"Login":["2016-08-27 13:26:45"]}
您收到的消息被截断了,因为您没有正确读取 payloadLength。 看一看:
var length = (message[1] & 0x7F);
var nextByte = 2;
if (length === 126) {
// in this case next 2 bytes store lengths. you have to read ones from the buffer
nextByte += 2;
} else if (length === 127){
// in this case next 8 bytes store lengths. you have to read ones from the buffer
nextByte += 8;
}
当消息大小超过 125 字节时,您必须像 RFC 所说的那样使用接下来的 2 或 8 个字节来读取正确的 payloadLength。在您的情况下,您只需跳过这些字节,以便您的 "length" 始终 <= 127,因此以下行会生成截断的有效负载数据:
var payload = message.slice(nextByte, nextByte + length);
^
always <= 127
我稍微修改了您的代码以调试问题。这是我正在谈论的实现示例:
var Cap = require('cap').Cap;
var decoders = require('cap').decoders;
var PROTOCOL = decoders.PROTOCOL;
var c = new Cap();
var device = Cap.findDevice('127.0.0.1');
var filter = 'port 3001';
var bufSize = 10 * 1024 * 1024;
var buffer = new Buffer(bufSize);
var linkType = c.open(device, filter, bufSize, buffer);
c.setMinBytes && c.setMinBytes(0);
c.on('packet', function(nbytes, trunc) {
console.log('************ packet: length ' + nbytes + ' bytes, truncated? '
+ (trunc ? 'yes' : 'no'));
if (linkType === 'ETHERNET') {
var ret = decoders.Ethernet(buffer);
if (ret.info.type === PROTOCOL.ETHERNET.IPV4) {
ret = decoders.IPV4(buffer, ret.offset);
if (ret.info.protocol === PROTOCOL.IP.TCP) {
var datalen = ret.info.totallen - ret.hdrlen;
ret = decoders.TCP(buffer, ret.offset);
datalen -= ret.hdrlen;
console.log('from: ' + ret.info.srcport + ' to ' + ret.info.dstport);
console.log("Data len: " + datalen);
var payload = wsDecoded(buffer, ret.offset, ret.offset + datalen);
if (payload) {
console.log("MSG:" + payload.toString());
} else {
console.log("Invalid message");
}
}
}
}
});
function wsDecoded (data, start, end) {
var message = data.slice(start, end);
var index = 0;
var FIN = (message[index] & 0x80);
var RSV1 = (message[index] & 0x40);
var RSV2 = (message[index] & 0x20);
var RSV3 = (message[index] & 0x10);
var Opcode = message[index] & 0x0F;
index++;
var masked = (message[index] & 0x80);
var payloadLength = message[index] & (~0x80);
index++;
if (payloadLength === 126){
// length = next 2 bytes
payloadLength = message.readUInt16BE(index);
index+=2;
} else if (payloadLength === 127) {
// read uint64
throw new Error("Implement me");
}
console.log("Opcode: " + Opcode + ", fin: " + FIN + ", masked: " + masked);
var maskingKey = null;
if (masked){
maskingKey = message.slice(index, index + 4);
index += 4;
}
console.log("Payload length: " + payloadLength);
var payload = message.slice(index, index + payloadLength);
if (payload.length != payloadLength) {
console.warn("Length mismatch");
return false;
}
if (maskingKey){
for (var i = 0; i < payload.length; i++){
payload[i] = payload[i] ^ maskingKey[i % 4];
}
}
return payload;
}