├── .dockerignore
├── .github
└── workflows
│ ├── ci.yaml
│ └── docs.yaml
├── .gitignore
├── .gitmodules
├── .nvmrc
├── CMakeLists.txt
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── assets
└── s1.svg
├── cxx
├── CMakeLists.txt
├── Sys.hpp
├── arp
│ ├── Arp.cpp
│ ├── Arp.hpp
│ ├── CMakeLists.txt
│ ├── Platform.cpp
│ ├── macOS.cpp
│ ├── macOS.hpp
│ ├── win32.cpp
│ └── win32.hpp
├── bpf-filter
│ ├── BpfFilter.cpp
│ ├── BpfFilter.hpp
│ └── CMakeLists.txt
├── checksums
│ ├── CMakeLists.txt
│ ├── Checksums.cpp
│ └── Checksums.hpp
├── common.hpp
├── converters
│ ├── CMakeLists.txt
│ ├── Converters.cpp
│ └── Converters.hpp
├── enums
│ ├── CMakeLists.txt
│ ├── Enums.cpp
│ └── Enums.hpp
├── error
│ ├── CMakeLists.txt
│ ├── Error.cpp
│ └── Error.hpp
├── example
│ ├── CMakeLists.txt
│ ├── Example.cpp
│ └── Example.hpp
├── main.cpp
├── routing
│ ├── CMakeLists.txt
│ ├── Platform.cpp
│ ├── Routing.cpp
│ ├── Routing.hpp
│ ├── macOS.cpp
│ ├── macOS.hpp
│ ├── win32.cpp
│ └── win32.hpp
├── toCxx.cpp
└── transports
│ ├── pcap
│ ├── CMakeLists.txt
│ ├── Pcap.cpp
│ └── Pcap.hpp
│ └── socket
│ ├── CMakeLists.txt
│ ├── Enums
│ ├── Enums.cpp
│ ├── Enums.hpp
│ ├── Linux.cpp
│ ├── MacOS.cpp
│ └── Windows.cpp
│ ├── InputPackets.cpp
│ ├── InputPackets.hpp
│ ├── Packets.cpp
│ ├── Packets.hpp
│ ├── SockAddr.cpp
│ ├── SockAddr.hpp
│ ├── Socket.cpp
│ └── Socket.hpp
├── jsdoc.json
├── lib
├── .gitignore
├── af.js
├── arp.js
├── bindings.js
├── bpfFilter.js
├── buffer.js
├── converters.js
├── defaults.js
├── enums.js
├── index.js
├── layers
│ ├── ARP.js
│ ├── Ethernet.js
│ ├── IPv4.js
│ ├── Payload.js
│ ├── TCP.js
│ ├── TLV.js
│ ├── UDP.js
│ ├── child.js
│ ├── enums.js
│ ├── exampleLayer.js
│ ├── index.js
│ ├── mac.js
│ ├── mixins.js
│ └── osi.js
├── layersList.js
├── liveDevice.js
├── packet.js
├── pcapFile
│ ├── index.js
│ ├── pcap.js
│ ├── pcapng
│ │ ├── const.js
│ │ ├── index.js
│ │ ├── readers
│ │ │ ├── empty.js
│ │ │ ├── enhancedPacket.js
│ │ │ ├── index.js
│ │ │ ├── interfaceDescription.js
│ │ │ ├── option.js
│ │ │ ├── packetBlock.js
│ │ │ ├── pcapng.js
│ │ │ ├── sectionBlock.js
│ │ │ └── simplePacket.js
│ │ └── tsresol.js
│ ├── reader.js
│ └── structs.js
├── pick.js
├── routing.js
├── socket.js
├── struct.js
└── timestamp.js
├── package-lock.json
├── package.json
└── test
├── Ethernet.test.js
├── IPv4.test.js
├── TCP.test.js
├── TLV.test.js
├── UDP.test.js
├── af.test.js
├── arp.test.js
├── arpTable.test.js
├── bpfFilter.test.js
├── converters.test.js
├── data
├── example1.pcap
├── example1.pcapng
└── example2.pcapng
├── liveDevice.test.js
├── packet.test.js
├── pcapFile.test.js
├── routing.test.js
├── test.pcapng
├── timestamp.test.js
└── tshark.sh
/.dockerignore:
--------------------------------------------------------------------------------
1 | .*
2 | *.md
3 | assets
4 | build
5 | Dockerfile
6 | docs
7 | memory.log
8 | node_modules
9 | notes.txt
10 | out
11 | prebuilds
12 | scr.sh
13 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: "build"
2 |
3 | on:
4 | push:
5 | branches:
6 | - release
7 |
8 | jobs:
9 | draft_release:
10 | name: Create Release
11 | runs-on: ubuntu-latest
12 | outputs:
13 | upload_url: ${{ steps.create_release.outputs.upload_url }}
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v4
17 | - name: Create Release
18 | id: create_release
19 | uses: actions/create-release@v1
20 | env:
21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22 | with:
23 | tag_name: ${{ github.ref }}
24 | release_name: Release ${{ github.ref }}
25 | body: |
26 | 🚀 This release contains necessary binaries to install over-the-wire on different platforms. See README.md of this repo for more details.
27 | draft: true
28 | prerelease: false
29 |
30 | build:
31 | needs: [draft_release]
32 | name: "Build"
33 | strategy:
34 | matrix:
35 | os: [ubuntu-latest, windows-latest, macos-latest, ubuntu-24.04-arm]
36 | runs-on: ${{ matrix.os }}
37 | steps:
38 | - name: Checkout code
39 | uses: actions/checkout@v4
40 |
41 | - name: Install coreutils for macOS
42 | if: matrix.os == 'macos-latest'
43 | run: brew install coreutils
44 |
45 | - name: Install Zip
46 | if: matrix.os == 'windows-latest'
47 | run: choco install zip
48 |
49 | - name: Set up Python
50 | uses: actions/setup-python@v5
51 | with:
52 | python-version: '3.x'
53 |
54 | - name: Install libpcap on Ubuntu
55 | if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
56 | run: |
57 | sudo apt-get update
58 | sudo apt-get install -y libpcap-dev
59 |
60 | - name: Install libpcap on macOS
61 | if: matrix.os == 'macos-latest'
62 | run: |
63 | brew update
64 | brew install libpcap
65 |
66 | - name: Install npcap on Windows
67 | if: matrix.os == 'windows-latest'
68 | run: |
69 | choco install wget --no-progress
70 | mkdir -p npcap
71 | wget "https://npcap.com/dist/npcap-sdk-1.13.zip"
72 | unzip npcap-sdk-1.13.zip -d npcap
73 | shell: bash
74 |
75 | - name: Install npcap on Windows (step 2)
76 | if: matrix.os == 'windows-latest'
77 | run: |
78 | set NPCAP_FILE=npcap-0.96.exe
79 | curl -L https://npcap.com/dist/%NPCAP_FILE% --output %NPCAP_FILE%
80 | %NPCAP_FILE% /S /winpcap_mode
81 | xcopy C:\Windows\System32\Npcap\*.dll C:\Windows\System32
82 | xcopy C:\Windows\SysWOW64\Npcap\*.dll C:\Windows\SysWOW64
83 | shell: cmd
84 |
85 | - name: Submodule update
86 | run: git submodule update --init --recursive
87 | shell: bash
88 |
89 | - name: Build and Test (Windows)
90 | if: matrix.os == 'windows-latest'
91 | run: |
92 | ls "$(pwd)/npcap"
93 | PCAP_ROOT="$(pwd)/npcap" npm i
94 | PCAP_ROOT="$(pwd)/npcap" npm run build
95 | node --test test/liveDevice.test.js test/arpTable.test.js test/routing.test.js
96 | shell: bash
97 |
98 | - name: Build and Test
99 | if: matrix.os != 'windows-latest'
100 | run: |
101 | npm i
102 | npm run build
103 | npm run test
104 | shell: bash
105 |
106 | - name: Prebuild (Windows)
107 | if: matrix.os == 'windows-latest'
108 | run: |
109 | ls "$(pwd)/npcap"
110 | PCAP_ROOT="$(pwd)/npcap" npm run precompile
111 | ls prebuilds
112 | zip -r prebuilds-${{ matrix.os }}.zip prebuilds
113 | shell: bash
114 |
115 | - name: Prebuild
116 | if: matrix.os != 'windows-latest'
117 | run: |
118 | npm run precompile
119 | ls prebuilds
120 | zip -r prebuilds-${{ matrix.os }}.zip prebuilds
121 | shell: bash
122 |
123 | - name: ls
124 | run: ls
125 | shell: bash
126 |
127 | - name: Upload
128 | uses: actions/upload-release-asset@v1
129 | env:
130 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
131 | with:
132 | upload_url: ${{ needs.draft_release.outputs.upload_url }}
133 | asset_path: prebuilds-${{ matrix.os }}.zip
134 | asset_name: prebuilds-${{ matrix.os }}.zip
135 | asset_content_type: application/zip
136 |
137 | alpine:
138 | needs: [draft_release]
139 | name: "Build Alpine"
140 | strategy:
141 | matrix:
142 | os: [ubuntu-latest, ubuntu-24.04-arm]
143 | runs-on: ${{ matrix.os }}
144 | steps:
145 | - name: Checkout code
146 | uses: actions/checkout@v4
147 |
148 | - name: Submodule update
149 | run: git submodule update --init --recursive
150 | shell: bash
151 |
152 | - name: Prebuild
153 | run: |
154 | docker buildx build . --output prebuilds
155 | ls prebuilds
156 | zip -r prebuilds-${{ matrix.os }}.zip prebuilds
157 |
158 | - name: Upload
159 | uses: actions/upload-release-asset@v1
160 | env:
161 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
162 | with:
163 | upload_url: ${{ needs.draft_release.outputs.upload_url }}
164 | asset_path: prebuilds-${{ matrix.os }}.zip
165 | asset_name: prebuilds-${{ matrix.os }}.zip
166 | asset_content_type: application/zip
167 |
--------------------------------------------------------------------------------
/.github/workflows/docs.yaml:
--------------------------------------------------------------------------------
1 | name: Generate and Deploy JSDoc
2 |
3 | on:
4 | push:
5 | branches:
6 | - release
7 |
8 | permissions:
9 | contents: read
10 | pages: write
11 | id-token: write
12 |
13 | jobs:
14 | docs:
15 | environment:
16 | name: github-pages
17 | url: ${{ steps.deployment.outputs.page_url }}
18 |
19 | runs-on: ubuntu-latest
20 |
21 | steps:
22 | - name: Checkout repository
23 | uses: actions/checkout@v3
24 |
25 | - name: Install libpcap on Ubuntu
26 | run: |
27 | sudo apt-get update
28 | sudo apt-get install -y libpcap-dev
29 |
30 | - name: Set up Node.js
31 | uses: actions/setup-node@v3
32 | with:
33 | node-version: '18'
34 |
35 | - name: Submodule update
36 | run: git submodule update --init --recursive
37 | shell: bash
38 |
39 | - name: Install dependencies
40 | run: npm ci
41 |
42 | - name: Generate JSDoc
43 | run: npm run generate-docs
44 |
45 | - name: Copy assets
46 | run: cp -r assets docs
47 |
48 | - name: Setup Pages
49 | uses: actions/configure-pages@v5
50 |
51 | - name: Upload artifact
52 | uses: actions/upload-pages-artifact@v3
53 | with:
54 | path: './docs'
55 | - name: Deploy to GitHub Pages
56 | id: deployment
57 | uses: actions/deploy-pages@v4
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
3 | scr.sh
4 | memory.log
5 | notes.txt
6 | out
7 | docs
8 | prebuilds
9 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "cxx_modules/PcapPlusPlus"]
2 | path = cxx_modules/PcapPlusPlus
3 | url = https://github.com/seladb/PcapPlusPlus/
4 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v21.5.0
2 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.9)
2 | cmake_policy(SET CMP0042 NEW)
3 | set (CMAKE_CXX_STANDARD 17)
4 |
5 | execute_process(COMMAND node -p "require('node-addon-api').include"
6 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
7 | OUTPUT_VARIABLE NODE_ADDON_API_DIR
8 | )
9 |
10 | execute_process(COMMAND node -p "require('os').arch()"
11 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
12 | OUTPUT_VARIABLE _CURRENT_ARCH
13 | )
14 |
15 | string(REGEX REPLACE "[\r\n\"]" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR})
16 | string(REGEX REPLACE "[\r\n\"]" "" _CURRENT_ARCH ${_CURRENT_ARCH})
17 |
18 | Message("NODE_ADDON_API_DIR: " ${NODE_ADDON_API_DIR})
19 | Message("_CURRENT_ARCH: " ${_CURRENT_ARCH})
20 |
21 | include_directories(${CMAKE_JS_INC})
22 |
23 | project (over-the-wire)
24 |
25 | if (WIN32)
26 | add_compile_options(/WX-)
27 | add_definitions(-D_SILENCE_ALL_MS_EXT_DEPRECATION_WARNINGS=1)
28 | message("Silencing MS warnings")
29 | endif()
30 |
31 | add_library(${PROJECT_NAME} SHARED ${CMAKE_JS_SRC})
32 | target_include_directories(${PROJECT_NAME} PRIVATE "${NODE_ADDON_API_DIR}")
33 | target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/cxx")
34 |
35 | if(DEFINED ENV{PCAP_ROOT})
36 | set(PCAP_ROOT $ENV{PCAP_ROOT})
37 | message("PCAP_ROOT: ${PCAP_ROOT}")
38 | add_definitions(-DPCAP_ROOT="${PCAP_ROOT}")
39 | else()
40 | message(WARNING "PCAP_ROOT is not set in the environment. Default paths will be used.")
41 | endif()
42 |
43 | if(PCAP_ROOT)
44 | target_include_directories(${PROJECT_NAME} PRIVATE "${PCAP_ROOT}/Include")
45 |
46 | set(PACKET_LIB "${PCAP_ROOT}/Lib/${_CURRENT_ARCH}/Packet.lib")
47 | set(WPCAP_LIB "${PCAP_ROOT}/Lib/${_CURRENT_ARCH}/wpcap.lib")
48 |
49 | file(TO_NATIVE_PATH "${PACKET_LIB}" PACKET_LIB)
50 | file(TO_NATIVE_PATH "${WPCAP_LIB}" WPCAP_LIB)
51 |
52 | message("PACKET_LIB: " ${PACKET_LIB})
53 | message("WPCAP_LIB: " ${WPCAP_LIB})
54 |
55 | if(EXISTS ${PACKET_LIB} AND EXISTS ${WPCAP_LIB})
56 | message("LINKING WITH: ${PACKET_LIB} ${WPCAP_LIB}")
57 | #target_link_libraries(${PROJECT_NAME} PUBLIC "${PACKET_LIB}" "${WPCAP_LIB}")
58 | else()
59 | message(FATAL_ERROR "Required libraries not found. Ensure that PCAP_ROOT is set correctly.")
60 | endif()
61 |
62 | endif()
63 |
64 | # PcapPlusPlus
65 | add_subdirectory(cxx_modules/PcapPlusPlus)
66 | add_dependencies(${PROJECT_NAME} Pcap++)
67 | include_directories(cxx_modules/PcapPlusPlus/Pcap++/header)
68 | include_directories(cxx_modules/PcapPlusPlus/Packet++/header)
69 | include_directories(cxx_modules/PcapPlusPlus/Common++/header)
70 | target_link_libraries(${PROJECT_NAME} PUBLIC Pcap++)
71 |
72 | add_subdirectory(cxx/)
73 | add_subdirectory(cxx/example)
74 | add_subdirectory(cxx/transports/pcap)
75 | add_subdirectory(cxx/transports/socket)
76 | add_subdirectory(cxx/bpf-filter)
77 | add_subdirectory(cxx/enums)
78 | add_subdirectory(cxx/checksums)
79 | add_subdirectory(cxx/arp)
80 | add_subdirectory(cxx/routing)
81 | add_subdirectory(cxx/error)
82 | add_subdirectory(cxx/converters)
83 |
84 | set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
85 |
86 | target_link_libraries(${PROJECT_NAME} PUBLIC ${CMAKE_JS_LIB})
87 |
88 | # define NPI_VERSION
89 | add_definitions(-DNAPI_VERSION=6)
90 |
91 | if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET)
92 | # Generate node.lib
93 | execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS})
94 | endif()
95 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Over-the-Wire
2 |
3 | We're thrilled that you're interested in helping with the development of Over-the-Wire! This document provides guidelines for contributing to the project and how to submit changes.
4 |
5 | ## Reporting Bugs or Requesting Features
6 |
7 | If you spot a bug or want to request a new feature, please start by searching our [Issues](https://github.com/vaguue/over-the-wire/issues) to see if it has already been reported. If it hasn't, feel free to open a new issue. Please provide as much information as possible to help us understand the problem or the nature of the feature you're proposing.
8 |
9 | ## Contributing Code
10 |
11 | Want to contribute code to fix a bug or add a new feature? Great! Here's how you can do it:
12 |
13 | 1. **Find an Issue**: Look through the [open issues](https://github.com/vaguue/over-the-wire/issues) for bugs or features that need help. If you're new to the project, look for issues tagged with "good first issue."
14 |
15 | 2. **Create an Issue**: If you want to add a feature or fix something that isn't yet reported, please open a new issue first. This lets us discuss the potential changes and how they fit into the project's direction.
16 |
17 | 3. **Fork & Clone**: Once you've identified an issue you'd like to tackle, fork the repository and clone it to your local machine.
18 |
19 | 4. **Make Your Changes**: Work on the changes as described in the issue. Make sure to keep your changes focused on resolving the issue or adding the feature discussed.
20 |
21 | 5. **Write Tests**: We use the native test runner of Node.js, so it's recommended to use Node.js v21.5.0 for development. All tests are located in the `test` folder. It's highly recommended to write tests for new features to ensure they work as expected and to help maintain the quality of the project.
22 |
23 | 6. **Submit a Pull Request (PR)**: Push your changes to your fork and then submit a pull request to the main repository. In your PR, include a description of the changes and reference the issue number (e.g., "Fixes #123").
24 |
25 | ## Future Plans for Bounties
26 |
27 | We're considering implementing a bounty system for certain issues to reward contributors for their hard work. Stay tuned for more details on this initiative.
28 |
29 | ## Code of Conduct
30 |
31 | Please note that this project is released with a Contributor Code of Conduct. By participating in this project, you agree to abide by its terms.
32 |
33 | Thank you for contributing to Over-the-Wire! We appreciate every contribution, big or small, and we look forward to working together to make Over-the-Wire even better.
34 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:22.14.0-alpine3.21 AS build
2 | RUN sed -i 's/https/http/' /etc/apk/repositories
3 | RUN apk add --no-cache \
4 | cmake \
5 | g++ \
6 | jq \
7 | less \
8 | libpcap-dev \
9 | make \
10 | tshark
11 | WORKDIR /app
12 | COPY package*.json ./
13 | RUN npm ci --ignore-scripts
14 | COPY . .
15 | RUN CMAKE_BUILD_PARALLEL_LEVEL=$(nproc) npm run precompile
16 | RUN npm test
17 |
18 | FROM scratch
19 | COPY --from=build /app/prebuilds/ .
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | # Over-the-wire [](https://github.com/vaguue/over-the-wire/blob/main/LICENSE) [](https://www.npmjs.com/package/over-the-wire) 
7 |
8 | *The project is currently under active development.*
9 |
10 | ## Overview
11 | `over-the-wire` is a Node.js packet manipulation library supporting:
12 | - Packet crafting and parsing
13 | - Capturing network traffic and sending packets in all formats
14 | - Parsing and serializing pcap and pcapng file formats
15 | - Creating custom non-TCP/UDP socket instances
16 |
17 | ## System Requirements
18 | - Libpcap/WinPcap/Npcap library installed (if Wireshark is installed on your system, you are good to go)
19 | - Node.js version 16.10.0 or higher recommended
20 | - C++ compiler, if there are no prebuilt bindings for your system
21 |
22 | ## Installation
23 |
24 | ```bash
25 | npm install over-the-wire --save
26 | ```
27 |
28 | ## Getting started
29 |
30 | ```javascript
31 | const fs = require('fs');
32 | const { Pcap, Packet } = require('over-the-wire');
33 |
34 | const dev = new Pcap.LiveDevice({
35 | iface: 'en0',
36 | direction: 'inout',
37 | filter: 'src port 443',
38 | });
39 |
40 | // Get info about interface
41 | console.log('[*] Interface: ', dev.iface);
42 |
43 | // Save captured packets to a pcapng file
44 | const dump = Pcap.createWriteStream({ format: 'pcapng' });
45 | dump.pipe(fs.createWriteStream('dump.pcapng'));
46 |
47 | dev.on('data', pkt => {
48 | if (pkt.layers.IPv4) {
49 | console.log(`[*] ${pkt.layers.IPv4.src} -> ${pkt.layers.IPv4.dst}`);
50 | }
51 | dump.write(pkt);
52 | });
53 |
54 | // Create and inject a packet
55 | const pkt = new Packet({ iface: dev.iface })
56 | .Ethernet()
57 | .IPv4({ dst: '192.168.1.1' })
58 | .ICMP();
59 | dev.write(pkt);
60 | ```
61 |
62 | ## Documentation
63 |
64 | [Here :)](https://vaguue.github.io/over-the-wire)
65 |
66 | ## Questions or Suggestions
67 | Feel free to open any issue in the Issues section of this repository. Currently, there are no restrictions on the format.
68 |
--------------------------------------------------------------------------------
/cxx/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | set(MAIN_SRC
2 | "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
3 | "${CMAKE_CURRENT_SOURCE_DIR}/toCxx.cpp"
4 | )
5 |
6 | source_group("Source Files\\Main" FILES ${MAIN_SRC})
7 |
8 | target_sources(${PROJECT_NAME} PRIVATE ${MAIN_SRC})
9 |
--------------------------------------------------------------------------------
/cxx/Sys.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifdef _WIN32
4 | #define WIN32_LEAN_AND_MEAN
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #define SOCKET_OPT_TYPE char *
11 | #define SOCKET_LEN_TYPE int
12 | #else
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #define SOCKET int
22 | #define closesocket close
23 | #define SOCKET_OPT_TYPE void *
24 | #define SOCKET_LEN_TYPE socklen_t
25 | #endif
26 |
27 | //from https://github.com/libuv/libuv/blob/v1.x/src/unix/internal.h
28 | #if defined(_AIX) || \
29 | defined(__APPLE__) || \
30 | defined(__DragonFly__) || \
31 | defined(__FreeBSD__) || \
32 | defined(__linux__) || \
33 | defined(__OpenBSD__) || \
34 | defined(__NetBSD__)
35 | #define USE_IOCTL 1
36 | #else
37 | #define USE_IOCTL 0
38 | #endif
39 |
--------------------------------------------------------------------------------
/cxx/arp/Arp.cpp:
--------------------------------------------------------------------------------
1 | #include "Arp.hpp"
2 |
3 | namespace OverTheWire::Arp {
4 |
5 | Napi::Object Init(Napi::Env env, Napi::Object exports) {
6 | exports.Set("getArpTable", Napi::Function::New(env, getArpTable));
7 | return exports;
8 | }
9 |
10 | ArpWorker::ArpWorker(Napi::Function& callback) : AsyncWorker{callback}, callback{callback} {}
11 |
12 | ArpWorker::~ArpWorker() {}
13 |
14 | void ArpWorker::Execute() {
15 | DEBUG_OUTPUT("ArpWorker::Execute");
16 | std::string err;
17 | std::tie(err, res) = fromSys();
18 | if (err.size() > 0) {
19 | SetError(err);
20 | }
21 | }
22 |
23 | void ArpWorker::OnOK() {
24 | Napi::HandleScope scope(Env());
25 |
26 | Napi::Object obj = Napi::Object::New(Env());
27 |
28 | for (auto& [k, v] : res) {
29 | Napi::Array arr = Napi::Array::New(Env(), v.size());
30 | for (size_t i{}; i < v.size(); ++i) {
31 | Napi::Object rec = Napi::Object::New(Env());
32 |
33 | rec.Set("ipAddr", Napi::String::New(Env(), v[i].ipAddr));
34 | rec.Set("hwAddr", Napi::String::New(Env(), v[i].hwAddr));
35 | rec.Set("hwType", Napi::Number::New(Env(), v[i].hwType));
36 |
37 | auto flags = Napi::Array::New(Env(), v[i].flags.size());
38 | rec.Set("flags", flags);
39 | for (size_t j{}; j < v[i].flags.size(); ++j) {
40 | flags.Set(j, Napi::String::New(Env(), v[i].flags[j]));
41 | }
42 |
43 | arr.Set(i, rec);
44 | }
45 |
46 | obj.Set(k, arr);
47 | }
48 |
49 | Callback().Call({ obj });
50 | }
51 |
52 | Napi::Value getArpTable(const Napi::CallbackInfo& info) {
53 | checkLength(info, 1);
54 | Napi::Function callback = info[0].As();
55 | ArpWorker* w = new ArpWorker(callback);
56 | w->Queue();
57 | return info.Env().Undefined();
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/cxx/arp/Arp.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "common.hpp"
4 | #include
5 |
6 | /*
7 | * Get ARP table for an interface
8 | */
9 |
10 | namespace OverTheWire::Arp {
11 | struct ArpRecord {
12 | std::string ipAddr;
13 | std::string hwAddr;
14 | uint32_t hwType = 0x1;
15 | std::vector flags;
16 | };
17 |
18 | using arp_table_t = std::map>;
19 |
20 | Napi::Object Init(Napi::Env env, Napi::Object exports);
21 |
22 | struct ArpWorker : public Napi::AsyncWorker {
23 | ArpWorker(Napi::Function&);
24 | ~ArpWorker();
25 | void Execute() override;
26 | void OnOK() override;
27 |
28 | Napi::Function& callback;
29 | arp_table_t res;
30 | };
31 |
32 | std::pair fromSys();
33 |
34 | Napi::Value getArpTable(const Napi::CallbackInfo& info);
35 | }
36 |
--------------------------------------------------------------------------------
/cxx/arp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | set(ARP_SRC
2 | "${CMAKE_CURRENT_SOURCE_DIR}/Arp.cpp"
3 | "${CMAKE_CURRENT_SOURCE_DIR}/Platform.cpp"
4 | )
5 |
6 | set(ARP_HDR
7 | "${CMAKE_CURRENT_SOURCE_DIR}/Arp.hpp"
8 | )
9 |
10 | source_group("Source Files\\Arp" FILES ${ARP_SRC})
11 | source_group("Header Files\\Arp" FILES ${ARP_HDR})
12 |
13 | target_sources(${PROJECT_NAME} PRIVATE ${ARP_SRC} ${ARP_HDR})
14 |
--------------------------------------------------------------------------------
/cxx/arp/Platform.cpp:
--------------------------------------------------------------------------------
1 | #include "Arp.hpp"
2 | #if defined(__APPLE__)
3 | #include "macOS.cpp"
4 | #elif defined(_WIN32)
5 | #include "win32.cpp"
6 | #else
7 | namespace OverTheWire::Arp {
8 | std::pair fromSys() {
9 | return {"ARP table not implemented for this platform", {}};
10 | }
11 | }
12 | #endif
13 |
--------------------------------------------------------------------------------
/cxx/arp/macOS.cpp:
--------------------------------------------------------------------------------
1 | //https://github.com/apple-oss-distributions/network_cmds/blob/network_cmds-669/arp.tproj/arp.c
2 |
3 | #include "macOS.hpp"
4 |
5 | namespace OverTheWire::Arp {
6 |
7 | static char* lladdr(struct sockaddr_dl *sdl) {
8 | static char buf[256];
9 | char* cp;
10 | int n, bufsize = sizeof (buf), p = 0;
11 |
12 | bzero(buf, sizeof (buf));
13 | cp = (char *)LLADDR(sdl);
14 | if ((n = sdl->sdl_alen) > 0) {
15 | while (--n >= 0) {
16 | p += snprintf(buf + p, bufsize - p, "%x%s", *cp++ & 0xff, n > 0 ? ":" : "");
17 | }
18 | }
19 | return (buf);
20 | }
21 |
22 | std::string save(arp_table_t& table, struct sockaddr_dl *sdl, struct sockaddr_inarp *addr, struct rt_msghdr *rtm) {
23 | const char *host;
24 | struct hostent *hp;
25 | char ifname[IF_NAMESIZE];
26 | if (if_indextoname(sdl->sdl_index, ifname) == NULL) {
27 | strcpy(ifname, "unknown");
28 | }
29 |
30 | auto& vec = table[ifname];
31 |
32 | ArpRecord rec;
33 | char str[INET6_ADDRSTRLEN];
34 | uv_inet_ntop(addr->sin_family, &addr->sin_addr, str, INET6_ADDRSTRLEN);
35 | rec.ipAddr = str;
36 | rec.hwAddr = sdl->sdl_alen ? lladdr(sdl) : "(incomplete)";
37 | rec.hwType = sdl->sdl_type;
38 |
39 | if ((rtm->rtm_flags & RTF_IFSCOPE)) {
40 | rec.flags.push_back("ifscope");
41 | }
42 |
43 | if (rtm->rtm_rmx.rmx_expire == 0) {
44 | rec.flags.push_back("permanent");
45 | }
46 |
47 | if (addr->sin_other & SIN_PROXY) {
48 | rec.flags.push_back("proxy");
49 | }
50 |
51 | if (rtm->rtm_addrs & RTA_NETMASK) {
52 | addr = (struct sockaddr_inarp *)(SA_SIZE(sdl) + (char *)sdl);
53 | if (addr->sin_addr.s_addr == 0xffffffff) {
54 | rec.flags.push_back("published");
55 | }
56 | if (addr->sin_len != 8) {
57 | rec.flags.push_back("weird");
58 | }
59 | }
60 |
61 | vec.push_back(std::move(rec));
62 |
63 | return "";
64 | }
65 |
66 | std::pair fromSys() {
67 | arp_table_t res{};
68 |
69 | int mib[6];
70 | size_t needed;
71 |
72 | char* buf;
73 |
74 | struct rt_msghdr* rtm;
75 | struct sockaddr_inarp* sin2;
76 | struct sockaddr_dl* sdl;
77 | char ifname[IF_NAMESIZE];
78 | int st;
79 |
80 | mib[0] = CTL_NET;
81 | mib[1] = PF_ROUTE;
82 | mib[2] = 0;
83 | //mib[3] = AF_INET;
84 | mib[3] = 0;
85 | mib[4] = NET_RT_FLAGS;
86 | mib[5] = RTF_LLINFO;
87 |
88 | if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
89 | return {"Route sysctl estimate error", res};
90 | }
91 | if (needed == 0) {
92 | return {"", res};
93 | }
94 |
95 | buf = NULL;
96 |
97 | for (;;) {
98 | char* newbuf = static_cast(realloc(buf, needed));
99 | if (newbuf == NULL) {
100 | if (buf != NULL) {
101 | free(buf);
102 | }
103 | return {"Could not reallocate memory", res};
104 | }
105 | buf = newbuf;
106 | st = sysctl(mib, 6, buf, &needed, NULL, 0);
107 | if (st == 0 || errno != ENOMEM) {
108 | break;
109 | }
110 | needed += needed / 8;
111 | }
112 | if (st == -1) {
113 | return {"actual retrieval of routing table", res};
114 | }
115 | char* lim = buf + needed;
116 | for (char* next = buf; next < lim; next += rtm->rtm_msglen) {
117 | rtm = (struct rt_msghdr *)next;
118 | sin2 = (struct sockaddr_inarp *)(rtm + 1);
119 | sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
120 | auto err = save(res, sdl, sin2, rtm);
121 | if (err.size() > 0) {
122 | return { err, res };
123 | }
124 | }
125 |
126 | free(buf);
127 |
128 | return {"", res};
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/cxx/arp/macOS.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | #include
17 | #include
18 |
19 | #include
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 |
33 | #ifndef SA_SIZE
34 | #define SA_SIZE(sa) \
35 | ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
36 | sizeof(uint32_t) : \
37 | 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(uint32_t) - 1) ) )
38 | #endif
39 |
--------------------------------------------------------------------------------
/cxx/arp/win32.cpp:
--------------------------------------------------------------------------------
1 | //https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-getipnettable2
2 |
3 | #include "win32.hpp"
4 |
5 | namespace OverTheWire::Arp {
6 |
7 | std::pair fromSys() {
8 | arp_table_t res{};
9 |
10 | PMIB_IPNET_TABLE2 pipTable = NULL;
11 |
12 | auto status = GetIpNetTable2(AF_UNSPEC, &pipTable);
13 | if (status != NO_ERROR) {
14 | return {"GetIpNetTable returned error" + std::to_string(status), res};
15 | }
16 |
17 | for (int i = 0; (unsigned) i < pipTable->NumEntries; ++i) {
18 | char ifname[IF_NAMESIZE];
19 | if (if_indextoname(pipTable->Table[i].InterfaceIndex, ifname) == NULL) {
20 | strcpy(ifname, "unknown");
21 | }
22 | auto& vec = res[ifname];
23 |
24 | ArpRecord rec;
25 |
26 | char str[INET6_ADDRSTRLEN];
27 | uv_inet_ntop(
28 | pipTable->Table[i].Address.si_family,
29 | pipTable->Table[i].Address.si_family == AF_INET6 ?
30 | (const void*)(&pipTable->Table[i].Address.Ipv6) :
31 | (const void*)(&pipTable->Table[i].Address.Ipv4),
32 | str, INET6_ADDRSTRLEN);
33 |
34 | rec.ipAddr = str;
35 | std::stringstream ss;
36 | ss << std::hex;
37 | for (int j = 0; j < pipTable->Table[i].PhysicalAddressLength; j++) {
38 | if (j) {
39 | ss << ":";
40 | }
41 | ss << (int)pipTable->Table[i].PhysicalAddress[j];
42 | }
43 |
44 | rec.hwAddr = ss.str();
45 | rec.hwType = pipTable->Table[i].InterfaceLuid.Info.IfType;
46 |
47 | switch (pipTable->Table[i].State) {
48 | case NlnsUnreachable:
49 | rec.flags.push_back("NlnsUnreachable");
50 | break;
51 | case NlnsIncomplete:
52 | rec.flags.push_back("NlnsIncomplete");
53 | break;
54 | case NlnsProbe:
55 | rec.flags.push_back("NlnsProbe");
56 | break;
57 | case NlnsDelay:
58 | rec.flags.push_back("NlnsDelay");
59 | break;
60 | case NlnsStale:
61 | rec.flags.push_back("NlnsStale");
62 | break;
63 | case NlnsReachable:
64 | rec.flags.push_back("NlnsReachable");
65 | break;
66 | case NlnsPermanent:
67 | rec.flags.push_back("NlnsPermanent");
68 | break;
69 | default:
70 | rec.flags.push_back("Unknown");
71 | break;
72 | }
73 |
74 | vec.push_back(std::move(rec));
75 | }
76 |
77 | return {"", res};
78 | }
79 |
80 | }
81 |
--------------------------------------------------------------------------------
/cxx/arp/win32.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | //#include
4 | //#include
5 | //#include
6 | //#include
7 | //#include
8 | #include
9 |
--------------------------------------------------------------------------------
/cxx/bpf-filter/BpfFilter.cpp:
--------------------------------------------------------------------------------
1 | #include "BpfFilter.hpp"
2 |
3 | namespace OverTheWire::BpfFilter {
4 |
5 | Napi::Object Init(Napi::Env env, Napi::Object exports) {
6 | BpfFilter::Init(env, exports);
7 | return exports;
8 | }
9 |
10 | Napi::Object BpfFilter::Init(Napi::Env env, Napi::Object exports) {
11 | Napi::Function func = DefineClass(env, "BpfFilter", {
12 | InstanceMethod<&BpfFilter::match>("match", static_cast(napi_writable | napi_configurable)),
13 | InstanceAccessor<&BpfFilter::getFilter, &BpfFilter::setFilter>("value"),
14 | InstanceAccessor<&BpfFilter::getLinkType, &BpfFilter::setLinkType>("linkType"),
15 | });
16 |
17 | env.GetInstanceData()->SetClass(typeid(BpfFilter), func);
18 | exports.Set("BpfFilter", func);
19 | return exports;
20 | }
21 |
22 | BpfFilter::BpfFilter(const Napi::CallbackInfo& info) : Napi::ObjectWrap{info}, obj{new pcpp::BpfFilterWrapper} {
23 | if (info.Length() > 0) {
24 | filter = info[0].As().Utf8Value();
25 | }
26 | if (info.Length() > 1) {
27 | linkType = static_cast(info[1].As().Uint32Value());
28 | }
29 |
30 | if (filter.size() > 0) {
31 | if (!obj->setFilter(filter, linkType)) {
32 | Napi::Error::New(info.Env(), "Error setting filter").ThrowAsJavaScriptException();
33 | }
34 | }
35 | }
36 |
37 | BpfFilter::~BpfFilter() {}
38 |
39 | Napi::Value BpfFilter::getFilter(const Napi::CallbackInfo& info) {
40 | return Napi::String::New(info.Env(), filter);
41 | }
42 |
43 | Napi::Value BpfFilter::getLinkType(const Napi::CallbackInfo& info) {
44 | return Napi::Number::New(info.Env(), linkType);
45 | }
46 |
47 | void BpfFilter::setFilter(const Napi::CallbackInfo& info, const Napi::Value& val) {
48 | filter = val.As().Utf8Value();
49 |
50 | if (!obj->setFilter(filter, linkType)) {
51 | Napi::Error::New(info.Env(), "Error setting filter").ThrowAsJavaScriptException();
52 | }
53 | }
54 |
55 | void BpfFilter::setLinkType(const Napi::CallbackInfo&, const Napi::Value& val) {
56 | linkType = static_cast(val.As().Uint32Value());
57 | }
58 |
59 | Napi::Value BpfFilter::match(const Napi::CallbackInfo& info) {
60 | checkLength(info, 1);
61 |
62 | if (filter.size() == 0) {
63 | return Napi::Boolean::New(info.Env(), true);
64 | }
65 |
66 | js_buffer_t buf = info[0].As();
67 | struct timespec ts;
68 | if (info.Length() > 1) {
69 | Napi::Array hrtime = info[1].As();
70 | ts.tv_sec = hrtime.Get("0").As().Uint32Value();
71 | ts.tv_nsec = hrtime.Get("1").As().Uint32Value();
72 | }
73 | else {
74 | timespec_get(&ts, TIME_UTC);
75 | }
76 |
77 | auto _linkType = linkType;
78 | if (info.Length() > 3) {
79 | _linkType = static_cast(info[2].As().Uint32Value());
80 | }
81 |
82 | return Napi::Boolean::New(info.Env(), obj->matchPacketWithFilter(buf.Data(), buf.Length(), ts, _linkType));
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/cxx/bpf-filter/BpfFilter.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include "common.hpp"
5 | #include "stdlib.h"
6 | #include "PcapLiveDeviceList.h"
7 | #include "IpUtils.h"
8 | #include "SystemUtils.h"
9 | #include "pcap.h"
10 |
11 | /* Wrapper around PcapPlusPlus BpfFilterWrapper.
12 | * Wrapper around wrapper.
13 | */
14 |
15 | namespace OverTheWire::BpfFilter {
16 | Napi::Object Init(Napi::Env env, Napi::Object exports);
17 |
18 | struct BpfFilter : public Napi::ObjectWrap {
19 | static Napi::Object Init(Napi::Env, Napi::Object);
20 | BpfFilter(const Napi::CallbackInfo& info);
21 | ~BpfFilter();
22 |
23 | Napi::Value getFilter(const Napi::CallbackInfo&);
24 | void setFilter(const Napi::CallbackInfo&, const Napi::Value&);
25 |
26 | Napi::Value getLinkType(const Napi::CallbackInfo&);
27 | void setLinkType(const Napi::CallbackInfo&, const Napi::Value&);
28 |
29 | Napi::Value match(const Napi::CallbackInfo&);
30 |
31 | std::unique_ptr obj;
32 | std::string filter = "";
33 | pcpp::LinkLayerType linkType = pcpp::LinkLayerType::LINKTYPE_ETHERNET;
34 | };
35 | }
36 |
--------------------------------------------------------------------------------
/cxx/bpf-filter/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | set(BPF_FILTER_SRC
2 | "${CMAKE_CURRENT_SOURCE_DIR}/BpfFilter.cpp"
3 | )
4 |
5 | set(BPF_FILTER_HDR
6 | "${CMAKE_CURRENT_SOURCE_DIR}/BpfFilter.hpp"
7 | )
8 |
9 | source_group("Source Files\\BpfFilter" FILES ${BPF_FILTER_SRC})
10 | source_group("Header Files\\BpfFilter" FILES ${BPF_FILTER_HDR})
11 |
12 | target_sources(${PROJECT_NAME} PRIVATE ${BPF_FILTER_SRC} ${BPF_FILTER_HDR})
13 |
--------------------------------------------------------------------------------
/cxx/checksums/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | set(CHECKSUMS_SRC
2 | "${CMAKE_CURRENT_SOURCE_DIR}/Checksums.cpp"
3 | )
4 |
5 | set(CHECKSUMS_HDR
6 | "${CMAKE_CURRENT_SOURCE_DIR}/Checksums.hpp"
7 | )
8 |
9 | source_group("Source Files\\Checksums" FILES ${CHECKSUMS_SRC})
10 | source_group("Header Files\\Checksums" FILES ${CHECKSUMS_HDR})
11 |
12 | target_sources(${PROJECT_NAME} PRIVATE ${CHECKSUMS_SRC} ${CHECKSUMS_HDR})
13 |
--------------------------------------------------------------------------------
/cxx/checksums/Checksums.cpp:
--------------------------------------------------------------------------------
1 | #include "Checksums.hpp"
2 |
3 | namespace OverTheWire::Checksums {
4 |
5 | Napi::Object Init(Napi::Env env, Napi::Object exports) {
6 | exports.Set("ip", Napi::Function::New(env, IPChecksum));
7 | exports.Set("pseudo", Napi::Function::New(env, PseudoHeaderChecksum));
8 | return exports;
9 | }
10 |
11 | pcpp::ScalarBuffer convertBuf(const Napi::Value& val) {
12 | Napi::Buffer buf = val.As>();
13 | return { buf.Data(), buf.Length() * 2 };
14 | }
15 |
16 | Napi::Value IPChecksum(const Napi::CallbackInfo& info) {
17 | checkLength(info, 1);
18 |
19 | std::vector> bufs;
20 |
21 | if (info[0].IsArray()) {
22 | Napi::Array ar = info[0].As();
23 | for (size_t i{}; i < ar.Length(); ++i) {
24 | bufs.push_back(convertBuf(ar[i]));
25 | }
26 | }
27 | else {
28 | bufs.push_back(convertBuf(info[0]));
29 | }
30 |
31 | return Napi::Number::New(info.Env(), computeChecksum(bufs.data(), bufs.size()));
32 | }
33 |
34 | Napi::Value PseudoHeaderChecksum(const Napi::CallbackInfo& info) {
35 | checkLength(info, 1);
36 | Napi::Object arg = info[0].As();
37 | uint8_t* data = arg.Get("data").As().Data();
38 | size_t dataLen = arg.Get("data").As().Length();
39 | pcpp::IPAddress::AddressType ipAddrType;
40 | std::string addrType = arg.Get("addrType").As().Utf8Value();
41 | if (addrType == "IPv4") {
42 | ipAddrType = pcpp::IPAddress::IPv4AddressType;
43 | }
44 | else if (addrType == "IPv6") {
45 | ipAddrType = pcpp::IPAddress::IPv6AddressType;
46 | }
47 | else {
48 | Napi::Error::New(info.Env(), "Invalid address type").ThrowAsJavaScriptException();
49 | return info.Env().Undefined();
50 | }
51 |
52 | uint8_t protocolType = arg.Get("protocolType").As().Uint32Value() & 0xff;
53 |
54 | std::string src = arg.Get("src").As().Utf8Value();
55 | std::string dst = arg.Get("dst").As().Utf8Value();
56 |
57 | pcpp::IPAddress srcIP{src};
58 | pcpp::IPAddress dstIP{dst};
59 |
60 | return Napi::Number::New(info.Env(), computePseudoHdrChecksum(data, dataLen, ipAddrType, protocolType, srcIP, dstIP));
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/cxx/checksums/Checksums.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "common.hpp"
4 | #include "PacketUtils.h"
5 |
6 | /*
7 | * Checksum helpers
8 | */
9 |
10 | namespace OverTheWire::Checksums {
11 | Napi::Object Init(Napi::Env env, Napi::Object exports);
12 |
13 | Napi::Value IPChecksum(const Napi::CallbackInfo&);
14 | Napi::Value PseudoHeaderChecksum(const Napi::CallbackInfo&);
15 | }
16 |
--------------------------------------------------------------------------------
/cxx/common.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include