├── .gitignore ├── README.md ├── docs ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md └── LICENSE.md ├── examples ├── index.js └── res │ └── myfile.pcapng ├── package.json ├── src ├── BlockConfig.js └── PCAPNGParser.js └── test ├── buffer ├── buffer0 ├── buffer1 └── buffer2 └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | .nyc_output 4 | sample_capture -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | PCAP-NG-Parser is a stream-based module to decode, print and analyze network traffic packets. With this module, you can read from an existing .pcapng file or connect it to an active stream. PCAP-NG-Parser is currently in active development. At this time, it supports only ethernet protocols from the output of [TCPDump v. 4.9.2](http://www.tcpdump.org/). 3 | 4 | # Why capture packets in JavaScript 5 | 6 | Excerpt from: 7 | 8 | 9 | ``` 10 | There are already many tools for capturing, decoding, and analyzing packets. Many of them are thoroughly 11 | tested and very fast. Why would anybody want to do such low level things like packet capture and analysis 12 | in JavaScript? A few reasons: 13 | 14 | * JavaScript makes writing event-based programs very natural. Each packet that is captured generates an 15 | event, and as higher level protocols are decoded, they might generate events as well. Writing code to handle 16 | these events is much easier and more readable with anonymous functions and closures. 17 | 18 | * Node makes handling binary data in JavaScript fast and efficient with its Buffer class. Decoding packets involves 19 | a lot of binary slicing and dicing which can be awkward with JavaScript strings. 20 | 21 | * Writing servers that capture packets, process them somehow, and then serve the processed data up in some way is 22 | very straightforward in node. 23 | 24 | * Node has a very good HTTP parser that is used to progressively decode HTTP sessions. 25 | ``` 26 | 27 | # Installation 28 | 29 | This module is available through the [npm registry](https://www.npmjs.com/). 30 | 31 | ```bash 32 | $ npm install pcap-ng-parser 33 | ``` 34 | 35 | # Usage 36 | 37 | ## Via .pcapng File 38 | Here is a quick example of how to log out packets to the console from a valid .pcapng file named `myfile.pcapng`. 39 | 40 | ```javascript 41 | const PCAPNGParser = require('pcap-ng-parser') 42 | const pcapNgParser = new PCAPNGParser() 43 | const myFileStream = require('fs').createReadStream('./myfile.pcapng') 44 | 45 | myFileStream.pipe(pcapNgParser) 46 | .on('data', parsedPacket => { 47 | console.log(parsedPacket) 48 | }) 49 | .on('interface', interfaceInfo => { 50 | console.log(interfaceInfo) 51 | }) 52 | ``` 53 | 54 | In the example above, we create a new Readable stream from our file and pipe the instance `pcapNgParser` which will read our packet data on the `_transform` event. 55 | 56 | ## Via TCPDump 57 | 58 | You can also pipe from TCPDump using `process.stdin` for a command line interaction. 59 | 60 | ```javascript 61 | const PCAPNGParser = require('pcap-ng-parser') 62 | const pcapNgParser = new PCAPNGParser() 63 | 64 | process.stdin.pipe(pcapNgParser) 65 | .on('data', parsedPacket => { 66 | console.log(parsedPacket) 67 | }) 68 | .on('interface', interfaceInfo => { 69 | console.log(interfaceInfo) 70 | }) 71 | ``` 72 | 73 | ```bash 74 | $ sudo tcpdump -w - | node exampleAbove.js 75 | ``` 76 | 77 | Note that in order to utilize tcpdump you must be a superuser. Refer to [tcpdump documentation](http://www.tcpdump.org/manpages/tcpdump.1.html) for details. 78 | 79 | ## Other Examples 80 | 81 | Additional examples can be found in the [examples directory](./examples). 82 | 83 | # Class PCAPNGParser 84 | 85 | PCAPNGParser is an extension of the [stream.Transform class](https://nodejs.org/api/stream.html#stream_class_stream_transform). The PCAPNGParser class has a modified `data` event and a custom `interface` event. For any additional details for how to interface with Transform streams, [refer to the Node.js stream documentation](https://nodejs.org/api/stream.html#stream_stream). 86 | 87 | ## Property 'interfaces' 88 | 89 | - `interfaces` | `Array` | List of all interfaces that the instance of PCAPNGParser has interacted with. 90 | 91 | ## Event 'data' 92 | 93 | - `parsedPacket` | `Object` | The parsed packet data. The `data` event is emitted whenever the PCAPNGParser stream is ready to relinquish ownership of packet data to a consumer. 94 | 95 | Example of a `parsedPacket` object: 96 | ```javascript 97 | { 98 | interfaceId: 0, 99 | timestampHigh: 355515, 100 | timestampLow: 1834438968, 101 | data: 102 | } 103 | ``` 104 | 105 | ### Description of parsedPacket Properties 106 | 107 | * `interfaceId` | `integer` | The order in which PCAPNGParser has interacted with the interface. Interface can be accessed by accessing the `interfaces` property of the instance of the PCAPNGParser class. 108 | * `timestampHigh` | `integer` | The upper 32 bits of the 64-bit timestamp integer. Refer to the [PCAPNG documentation](http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.3) on this matter for more details. 109 | * `timestampLow` | `integer` | The lower 32 bits of the 64-bit timestamp integer. Refer to the [PCAPNG documentation](http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.3) on this matter for more details. 110 | * `data` | `buffer` | A buffer with the data of the current packet. 111 | 112 | ## Event 'interface' 113 | 114 | - `interfaceInfo` | `object` | Interface Data. The `interface` event is emitted whenever the PCAPNGParser stream has encountered a new interface type not encountered yet. 115 | 116 | Example of an `interfaceInfo` object: 117 | ```javascript 118 | { 119 | linkType: 1, 120 | snapLen: 262144, 121 | name: 'en0' 122 | } 123 | ``` 124 | 125 | ### Description of interfaceInfo Properties 126 | 127 | * `linkType` | `integer` | The linktype of the current interface. Refer to the [TCPDump Link-Layer header documentation](http://www.tcpdump.org/linktypes.html) for more details. 128 | * `snapLen` | `integer` | An estimate for the length of the packets coming from the interface. 129 | * `name` | `string` | The name of the interface. 130 | 131 | # Contribution 132 | 133 | Refer to the the [Contribution Guide](./docs/CONTRIBUTING.md) for details on how to contribute. 134 | 135 | # License 136 | 137 | This module is covered under the BSD-3 Open Software License. Review the [License Documention](./docs/LICENSE.md) for more information. 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | ## Setting up the development enviroment 4 | In order to contribute, fork the git repository and install the dependies. You will at minimum need git and Node.js. 5 | ```bash 6 | git clone https://github.com/CollinearGroup/pcap-ng-parser.git 7 | cd pcap-ng-parser 8 | npm install 9 | npm test 10 | ``` 11 | 12 | ## Submitting Contributions 13 | Any changes to the resources in this repository must be submitted through pull requests. This applies to all changes to documentation, code, binary files, etc. All pull requests must be reviewed first before being merged into master. 14 | 15 | Please review our [Code of Conduct](./CODE_OF_CONDUCT.md) before beginning to contribute. 16 | 17 | ## How to create a Great Pull Request 18 | 19 | * Provide a clear and descriptive title for what was added and / or changed. 20 | 21 | * Create a unit test for whatever additions made. 22 | 23 | * A member of the Collinear team will review your additions before approving them. 24 | 25 | ## How Do I Submit A Great Bug Report? 26 | 27 | * Use a clear and descriptive title for the issue to identify the problem. 28 | 29 | * Describe the exact steps in which to reproduce the problem. 30 | 31 | * When listing steps, don't just say what you did, but explain how you did it. 32 | 33 | * Include the OS and version you are using in the report. 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /docs/LICENSE.md: -------------------------------------------------------------------------------- 1 | # LICENSE - BSD-3 2 | 3 | Copyright 2018 Collinear Group, Inc. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | 11 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | /*** EXAMPLE of using ether-frame package with PCAPNGParser to decode ethernet packet ***/ 2 | /*** Link to ether-frame package: https://www.npmjs.com/package/ether-frame ***/ 3 | 4 | const PCAPNGParser = require('../src/PCAPNGParser') 5 | const pcapNgParser = new PCAPNGParser() 6 | // const myFileStream = process.stdin // pipe from tcpdump 7 | const myFileStream = require('fs').createReadStream('./examples/res/myfile.pcapng') 8 | const EtherFrame = require('ether-frame') 9 | 10 | myFileStream 11 | .pipe(pcapNgParser) 12 | .on('data', parsedPacket => { 13 | console.log(parsedPacket) 14 | try { 15 | console.log(EtherFrame.fromBuffer(parsedPacket.data, pcapNgParser.endianess)) 16 | } catch(ex) { 17 | // Catches for type codes not currently supported by ether-frame 18 | console.log(ex.message) 19 | } 20 | }) 21 | .on('interface', interfaceInfo => { 22 | console.log(interfaceInfo) 23 | }) 24 | 25 | -------------------------------------------------------------------------------- /examples/res/myfile.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CollinearGroup/pcap-ng-parser/a37fecdf4143a06a3c56e93da39837cd6cac0fad/examples/res/myfile.pcapng -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pcap-ng-parser", 3 | "version": "1.0.0", 4 | "description": "Node.js Module for parsing .pcapng files and tcpdump output.", 5 | "main": "./src/PCAPNGParser.js", 6 | "scripts": { 7 | "example": "node examples/index.js", 8 | "test": "nyc mocha" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/CollinearGroup/pcap-ng-parser.git" 13 | }, 14 | "keywords": [ 15 | "pcap-ng", 16 | "tcpdump", 17 | "parser", 18 | "network", 19 | "packets" 20 | ], 21 | "author": "Collinear Group ", 22 | "contributors": [ 23 | { 24 | "name": "George Barta" 25 | }, 26 | { 27 | "name": "S. Everett Abbott" 28 | } 29 | ], 30 | "license": "BSD-3", 31 | "dependencies": { 32 | "ether-frame": "^0.2.0" 33 | }, 34 | "devDependencies": { 35 | "chai": "^4.1.2", 36 | "mocha": "^5.2.0", 37 | "nyc": "^12.0.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/BlockConfig.js: -------------------------------------------------------------------------------- 1 | /** @see documentation at http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.1 */ 2 | const sectionHeaderBlock = { 3 | blockType: { 4 | size: 4 5 | }, 6 | blockTotalLength: { 7 | size: 4 8 | }, 9 | byteOrderMagic: { 10 | size: 4 11 | }, 12 | majorVersion: { 13 | size: 2 14 | }, 15 | minorVersion: { 16 | size: 2 17 | }, 18 | 19 | //This is a signed 64 bit value... but node doesn't do 64 bit values 20 | sectionLengthTop: { 21 | size: 4 22 | }, 23 | sectionLengthBottom: { 24 | size: 4 25 | } 26 | } 27 | 28 | 29 | /** @see definition at http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.3 */ 30 | const blockConfig = { 31 | 32 | blockType: { 33 | size: 4, 34 | signed: true 35 | }, 36 | blockTotalLength: { 37 | size: 4 38 | } 39 | } 40 | 41 | /** @see definition at http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.2 */ 42 | const interfaceDescriptionBlockFormat = { 43 | 44 | blockType: { 45 | size: 4 46 | }, 47 | blockTotalLength: { 48 | size: 4 49 | }, 50 | linkType: { 51 | size: 2 52 | }, 53 | reserved: { 54 | size: 2 55 | }, 56 | snapLen: { 57 | size: 4 58 | } 59 | } 60 | 61 | /** @see definition at http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.3 */ 62 | const enhancedPacketBlockFormat = { 63 | blockType: { 64 | size: 4 65 | }, 66 | blockTotalLength: { 67 | size: 4 68 | }, 69 | interfaceId: { 70 | size: 4 71 | }, 72 | timestampHigh: { 73 | size: 4 74 | }, 75 | timestampLow: { 76 | size: 4 77 | }, 78 | capturedPacketLength: { 79 | size: 4 80 | }, 81 | originalPacketLength: { 82 | size: 4 83 | } 84 | /* Packet Data */ 85 | /* Options */ 86 | } 87 | 88 | /** @see definition at http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.3.5 */ 89 | const optionBlock = { 90 | code: { 91 | size: 2 92 | }, 93 | dataLength: { 94 | size: 2 95 | } 96 | } 97 | 98 | 99 | 100 | module.exports = { sectionHeaderBlock, optionBlock, enhancedPacketBlockFormat, interfaceDescriptionBlockFormat, blockConfig } -------------------------------------------------------------------------------- /src/PCAPNGParser.js: -------------------------------------------------------------------------------- 1 | const BlockConfig = require('./BlockConfig') 2 | const { Transform } = require('stream') 3 | 4 | class PCAPNGParser extends Transform { 5 | constructor() { 6 | //The magic bit to allow objects 7 | super({ 8 | readableObjectMode: true 9 | }) 10 | this.sectionHeader = undefined 11 | this.carryData = undefined 12 | this.endianess = 'LE' 13 | this.interfaces = [] 14 | } 15 | 16 | _transform(chunk, encoding, callback) { 17 | let buf = chunk 18 | //Stitch previous data packet fragment 19 | if (this.carryData) { 20 | buf = Buffer.concat([this.carryData, chunk]) 21 | this.carryData = undefined 22 | } 23 | 24 | let pos = 0 25 | while (pos < (buf.length)) { 26 | 27 | if (!this.sectionHeader) { 28 | try { 29 | pos = this.readHeaderBlockFromBuffer(buf) 30 | } catch (err) { 31 | callback(err) 32 | return 33 | } 34 | } else if (pos + 8 >= buf.length) { 35 | //If we don't have enough to read the next block length save the remaining data to be pre-pended to the next received data 36 | this.carryData = buf.slice(pos) 37 | pos = buf.length 38 | } else { 39 | //Read block type and length 40 | let block = this.readBlock(buf, BlockConfig.blockConfig, this.endianess, pos) 41 | 42 | if (pos + block.data.blockTotalLength > buf.length) { 43 | // This block is bigger than the data we have so save it to be pre-pended to the next received data 44 | this.carryData = buf.slice(pos) 45 | pos = buf.length 46 | } else { 47 | // We have the entire block, go ahead and process itj 48 | let blockData = buf.slice(pos, pos + block.data.blockTotalLength) 49 | let outputDataBlock = this.processRawBlock(blockData, block.data.blockType) 50 | if (outputDataBlock) { 51 | let sendBlock = Object.assign({}, outputDataBlock) 52 | delete sendBlock.blockType 53 | delete sendBlock.blockTotalLength 54 | delete sendBlock.capturedPacketLength 55 | delete sendBlock.originalPacketLength 56 | this.push(sendBlock) 57 | } 58 | pos = pos + block.data.blockTotalLength 59 | 60 | if(block.data.blockTotalLength <= 0) { 61 | callback(new Error("Invalid block with size 0, unable to scan stream")) 62 | return 63 | } 64 | } 65 | } 66 | 67 | } //end while pos 68 | 69 | callback() 70 | } 71 | 72 | _flush() { 73 | // console.log("Got end") 74 | this.emit('close') 75 | } 76 | 77 | readBlock (buf, blockDescriptor, endian = 'LE', offset = 0) { 78 | let pos = offset 79 | let props = {} 80 | for (let prop in blockDescriptor) { 81 | let { 82 | size, 83 | signed 84 | } = blockDescriptor[prop] 85 | let readMethod = 'read' + (signed ? '' : 'U') + 'Int' + (size * 8) + endian 86 | props[prop] = buf[readMethod](pos) 87 | pos += size 88 | } 89 | return { 90 | newOffset: pos, 91 | data: props 92 | } 93 | } 94 | 95 | readHeaderBlockFromBuffer(buf) { 96 | //Read the header block to determine endianess and make sure its a PCAP-NG file 97 | let blockType = buf.readUInt32BE(0) 98 | this.checkBlockTypeFromBuffer(blockType) 99 | 100 | let byteOrderMagic = buf.readUInt32BE(8) 101 | this.endianess = this.processByteOrderMagic(byteOrderMagic) 102 | 103 | let res = this.readBlock(buf, BlockConfig.sectionHeaderBlock, this.endianess, 0) 104 | this.sectionHeader = res.data 105 | 106 | // let secLen = buf.readUInt32LE(this.sectionHeader.blockTotalLength - 4) 107 | return this.sectionHeader.blockTotalLength 108 | } 109 | 110 | processByteOrderMagic(byteOrderMagic) { 111 | if (byteOrderMagic === 0x1A2B3C4D) { 112 | return 'BE' 113 | // console.log("Detected BE endian") 114 | } else if (byteOrderMagic === 0x4D3C2B1A) { 115 | return 'LE' 116 | // console.log("Detected LE endian") 117 | } else { 118 | // console.log("Unable to determine endian from " + byteOrderMagic.toString(16)) 119 | } 120 | } 121 | 122 | checkBlockTypeFromBuffer (blockType) { 123 | if (blockType === 0x0A0D0D0A) { 124 | // console.log("Recognized PCAP-NG file") 125 | } else { 126 | let err = "Invalid file, block type of " + blockType.toString(16) + " not recognized" 127 | throw new Error(err) 128 | } 129 | } 130 | 131 | readOptions (buf) { 132 | let pos = 0 133 | let foundEndOption = false 134 | let options = [] 135 | while (pos <= buf.length || foundEndOption) { 136 | let rb = this.readBlock(buf, BlockConfig.optionBlock, this.endianess, pos) 137 | pos = rb.newOffset + (rb.data.dataLength * 8) 138 | if (rb.data.code === 0) { 139 | foundEndOption = true 140 | } else { 141 | rb.data.data = buf.slice(pos - (rb.data.dataLength * 8), pos) 142 | options.push(rb.data) 143 | } 144 | } 145 | return options 146 | } 147 | /** 148 | * @see http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.3.1 149 | * @param {Buffer} blockData 150 | * @param {int} blockType 151 | */ 152 | 153 | processRawBlock (blockData, blockType) { 154 | if (blockType < 0) { 155 | //MSB of 1 indicates this is 'local use' data 156 | } else if (blockType === 1) { 157 | //Interface definition 158 | 159 | let iData = {} 160 | let idRes = this.readBlock(blockData, BlockConfig.interfaceDescriptionBlockFormat, this.endianess, 0) 161 | iData.linkType = idRes.data.linkType 162 | iData.snapLen = idRes.data.snapLen 163 | if (idRes.newOffset < blockData.length) { 164 | let opts = this.readOptions(blockData.slice(idRes.newOffset)) 165 | opts.forEach((opt) => { 166 | if(opt.code == 2) { 167 | iData.name = opt.data.toString('utf8').replace(/\0/g,'').trim() 168 | } else { 169 | iData['code_'+opt.code] = opt.data.toString() 170 | } 171 | }) 172 | } 173 | this.interfaces.push(iData) 174 | 175 | //Notify listeners we got a new interface 176 | this.emit('interface', iData) 177 | } else if (blockType === 6) { 178 | //Enhanced block... data 179 | let id = this.readBlock(blockData, BlockConfig.enhancedPacketBlockFormat, this.endianess, 0) 180 | id.data.data = blockData.slice(id.newOffset, id.newOffset+id.data.capturedPacketLength) 181 | return id.data 182 | } else { 183 | this.emit("Block type: " + blockType) 184 | } 185 | } 186 | 187 | } 188 | 189 | module.exports = PCAPNGParser -------------------------------------------------------------------------------- /test/buffer/buffer0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CollinearGroup/pcap-ng-parser/a37fecdf4143a06a3c56e93da39837cd6cac0fad/test/buffer/buffer0 -------------------------------------------------------------------------------- /test/buffer/buffer1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CollinearGroup/pcap-ng-parser/a37fecdf4143a06a3c56e93da39837cd6cac0fad/test/buffer/buffer1 -------------------------------------------------------------------------------- /test/buffer/buffer2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CollinearGroup/pcap-ng-parser/a37fecdf4143a06a3c56e93da39837cd6cac0fad/test/buffer/buffer2 -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('chai').assert; 2 | const PCAPNGParser = require('../src/PCAPNGParser') 3 | const pcapNgParser = new PCAPNGParser() 4 | const fs = require('fs') 5 | 6 | describe('PCAPNGParser', function() { 7 | describe(`.on('data')`, function() { 8 | it('should return an object given a Buffer Stream', function() { 9 | let bufferStream0 = fs.createReadStream('./test/buffer/buffer0') 10 | let bufferStream1 = fs.createReadStream('./test/buffer/buffer1') 11 | bufferStream0 12 | .pipe(pcapNgParser, { end: false }) 13 | .on('data', (parsedPacket) => { 14 | assert.isObject(parsedPacket, 'parsedPacket is an object') 15 | }) 16 | bufferStream1 17 | .pipe(pcapNgParser, { end: false }) 18 | .on('data', (parsedPacket) => { 19 | assert.isObject(parsedPacket, 'parsedPacket is an object') 20 | }) 21 | }); 22 | it('should return an object with properties interfaceId, timestampHigh, timestampLow, data & ethernet', function() { 23 | let bufferStream0 = fs.createReadStream('./test/buffer/buffer0') 24 | let bufferStream1 = fs.createReadStream('./test/buffer/buffer1') 25 | bufferStream0 26 | .pipe(pcapNgParser, { end: false }) 27 | .on('data', (parsedPacket) => { 28 | assert.property(parsedPacket, 'interfaceId', 'parsedPacket has property interfaceId') 29 | assert.property(parsedPacket, 'timestampHigh', 'parsedPacket has property interfaceId') 30 | assert.property(parsedPacket, 'timestampLow', 'parsedPacket has property interfaceId') 31 | assert.property(parsedPacket, 'data', 'parsedPacket has property interfaceId') 32 | }) 33 | bufferStream1 34 | .pipe(pcapNgParser, { end: false }) 35 | .on('data', (parsedPacket) => { 36 | assert.property(parsedPacket, 'interfaceId', 'parsedPacket has property interfaceId') 37 | assert.property(parsedPacket, 'timestampHigh', 'parsedPacket has property interfaceId') 38 | assert.property(parsedPacket, 'timestampLow', 'parsedPacket has property interfaceId') 39 | assert.property(parsedPacket, 'data', 'parsedPacket has property interfaceId') 40 | }) 41 | }) 42 | }); 43 | describe(`.on('interface')`, function() { 44 | it('should return an object given a Buffer Stream', function() { 45 | let bufferStream0 = fs.createReadStream('./test/buffer/buffer0') 46 | let bufferStream1 = fs.createReadStream('./test/buffer/buffer1') 47 | bufferStream0 48 | .pipe(pcapNgParser, { end: false }) 49 | .on('interface', (i) => { 50 | assert.isObject(i, 'i is an object') 51 | }) 52 | bufferStream1 53 | .pipe(pcapNgParser, { end: false }) 54 | .on('interface', (i) => { 55 | assert.isObject(i, 'i is an object') 56 | }) 57 | }) 58 | it('should return an object with properties linkType, snapLen & name', function() { 59 | let bufferStream0 = fs.createReadStream('./test/buffer/buffer0') 60 | let bufferStream1 = fs.createReadStream('./test/buffer/buffer1') 61 | bufferStream0 62 | .pipe(pcapNgParser, { end: false }) 63 | .on('interface', (i) => { 64 | assert.property(i, 'linkType', 'i has property linkType') 65 | assert.property(i, 'snapLen', 'i has property snapLen') 66 | assert.property(i, 'name', 'i has property name') 67 | }) 68 | bufferStream1 69 | .pipe(pcapNgParser, { end: false }) 70 | .on('interface', (i) => { 71 | assert.property(i, 'linkType', 'i has property linkType') 72 | assert.property(i, 'snapLen', 'i has property snapLen') 73 | assert.property(i, 'name', 'i has property name') 74 | }) 75 | }) 76 | }); 77 | }); 78 | 79 | 80 | --------------------------------------------------------------------------------