├── .github └── workflows │ └── build.yml ├── .gitignore ├── .idea ├── .gitignore ├── IridiumNG.iml ├── modules.xml └── vcs.xml ├── README-zh.md ├── README.md ├── config.json ├── frontend ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.png │ ├── fonts │ │ ├── OpenSans │ │ │ ├── OpenSans-Bold.ttf │ │ │ ├── OpenSans-BoldItalic.ttf │ │ │ ├── OpenSans-ExtraBold.ttf │ │ │ ├── OpenSans-ExtraBoldItalic.ttf │ │ │ ├── OpenSans-Italic.ttf │ │ │ ├── OpenSans-Light.ttf │ │ │ ├── OpenSans-LightItalic.ttf │ │ │ ├── OpenSans-Medium.ttf │ │ │ ├── OpenSans-MediumItalic.ttf │ │ │ ├── OpenSans-Regular.ttf │ │ │ ├── OpenSans-SemiBold.ttf │ │ │ └── OpenSans-SemiBoldItalic.ttf │ │ ├── shicon.svg │ │ ├── shicon.ttf │ │ └── shicon.woff │ ├── global.css │ └── index.html ├── rollup.config.js ├── scripts │ └── setupTypeScript.js └── src │ ├── App.svelte │ ├── Packet.svelte │ ├── main.js │ ├── proto_raw_decoder.js │ └── protobuf_decoder │ ├── hexUtils.js │ ├── protobufDecoder.js │ ├── protobufPartDecoder.js │ └── varintUtils.js ├── frontend_server.go ├── go.mod ├── go.sum ├── main.go ├── makefile ├── mt19937_64.go ├── proto_service.go ├── sniffer.go └── util.go /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Set up Go 17 | uses: actions/setup-go@v3 18 | with: 19 | go-version: 1.18 20 | cache: true 21 | 22 | - name: Setup Node.js environment 23 | uses: actions/setup-node@v3.3.0 24 | with: 25 | node-version: 16.x 26 | cache: npm 27 | cache-dependency-path: ./frontend/package-lock.json 28 | 29 | - name: Build Frontend 30 | run: cd frontend && npm i && npm run build 31 | 32 | - name: Set up libpcap 33 | run: sudo apt-get install -y libpcap-dev 34 | 35 | - name: Build 36 | run: make pre-build && make build-win && make build-linux 37 | 38 | - name: Upload a Build Artifact 39 | uses: actions/upload-artifact@v3.1.0 40 | with: 41 | # Artifact name 42 | name: IridiumNG 43 | # A file, directory or wildcard pattern that describes what to upload 44 | path: ./build 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /vendor 3 | 4 | /data 5 | 6 | .DS_Store 7 | 8 | *.pcapng -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/IridiumNG.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # Iridium-NG 2 | 3 | A KCP packet sniffer + visualizer in one, backend rewritten in Go. 4 | 5 | ![Build](https://github.com/Akka0/Iridium-NG/actions/workflows/build.yml/badge.svg) 6 | 7 | # 用法 8 | 9 | 你可以从Actions中下载到编译好的,或者自己从源码构建。 10 | 11 | 0. 往 `data/` 目录放入 `packetIds.json`, `Keys.json` 和 `proto/` 文件. 12 | 1. 确保你已安装 [Npcap driver](https://npcap.com/#download) 或 wireshark. 13 | 2. 使用命令 `-l` 列出电脑上所有网卡,编辑 `config.json` 设置网卡设备, 或者使用命令 `-ip 192.x.x.x` 自动通过ip寻找设备. 14 | 3. 打开 http://localhost:1984/, 15 | 16 | **注意:在进门之前开始抓包** 17 | 18 | # Config.json 19 | 20 | ```json 21 | { 22 | "deviceName" : "", // 网络设备名称, 例如 eth0 23 | "packetFilter" : [ // 这里列出的包名将不会在前端显示 24 | "" 25 | ], 26 | "autoSavePcapFiles" : true // 自动保存抓包记录文件 27 | } 28 | ``` 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Iridium-NG 2 | 3 | A KCP packet sniffer + visualizer in one, backend rewritten in Go. 4 | 5 | ![Build](https://github.com/Akka0/Iridium-NG/actions/workflows/build.yml/badge.svg) 6 | 7 | [中文说明](/README-zh.md) 8 | 9 | # Usage 10 | 11 | You can download the binary(win/linux) from Actions, or build from source 12 | 13 | 0. Bring your `packetIds.json`, `Keys.json` and `proto/` to the `data/` folder. 14 | 1. Make sure you have installed [Npcap driver](https://npcap.com/#download) or wireshark. 15 | 2. Use cmd `-l` to list the network devices on your computer and edit `config.json` to set the device by its name, or use cmd `-ip 192.x.x.x` to let it auto find the device by its ip. 16 | 3. Open http://localhost:1984/ 17 | 18 | **Notice: START CAPTURE BEFORE YOU ENTER THE DOOR** 19 | 20 | # Config.json 21 | 22 | ```json 23 | { 24 | "deviceName" : "", // network device name, such as eth0 25 | "packetFilter" : [ // the packets listed here will not show in frontend 26 | "" 27 | ], 28 | "autoSavePcapFiles" : true // auto save capture to current folder 29 | } 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "deviceName" : "", 3 | "packetFilter" : [ 4 | "" 5 | ], 6 | "autoSavePcapFiles" : true 7 | } -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /public/build/ 3 | 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | *Psst — looking for a more complete solution? Check out [SvelteKit](https://kit.svelte.dev), the official framework for building web applications of all sizes, with a beautiful development experience and flexible filesystem-based routing.* 2 | 3 | *Looking for a shareable component template instead? You can [use SvelteKit for that as well](https://kit.svelte.dev/docs#packaging) or the older [sveltejs/component-template](https://github.com/sveltejs/component-template)* 4 | 5 | --- 6 | 7 | # svelte app 8 | 9 | This is a project template for [Svelte](https://svelte.dev) apps. It lives at https://github.com/sveltejs/template. 10 | 11 | To create a new project based on this template using [degit](https://github.com/Rich-Harris/degit): 12 | 13 | ```bash 14 | npx degit sveltejs/template svelte-app 15 | cd svelte-app 16 | ``` 17 | 18 | *Note that you will need to have [Node.js](https://nodejs.org) installed.* 19 | 20 | 21 | ## Get started 22 | 23 | Install the dependencies... 24 | 25 | ```bash 26 | cd svelte-app 27 | npm install 28 | ``` 29 | 30 | ...then start [Rollup](https://rollupjs.org): 31 | 32 | ```bash 33 | npm run dev 34 | ``` 35 | 36 | Navigate to [localhost:8080](http://localhost:8080). You should see your app running. Edit a component file in `src`, save it, and reload the page to see your changes. 37 | 38 | By default, the server will only respond to requests from localhost. To allow connections from other computers, edit the `sirv` commands in package.json to include the option `--host 0.0.0.0`. 39 | 40 | If you're using [Visual Studio Code](https://code.visualstudio.com/) we recommend installing the official extension [Svelte for VS Code](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). If you are using other editors you may need to install a plugin in order to get syntax highlighting and intellisense. 41 | 42 | ## Building and running in production mode 43 | 44 | To create an optimised version of the app: 45 | 46 | ```bash 47 | npm run build 48 | ``` 49 | 50 | You can run the newly built app with `npm run start`. This uses [sirv](https://github.com/lukeed/sirv), which is included in your package.json's `dependencies` so that the app will work when you deploy to platforms like [Heroku](https://heroku.com). 51 | 52 | 53 | ## Single-page app mode 54 | 55 | By default, sirv will only respond to requests that match files in `public`. This is to maximise compatibility with static fileservers, allowing you to deploy your app anywhere. 56 | 57 | If you're building a single-page app (SPA) with multiple routes, sirv needs to be able to respond to requests for *any* path. You can make it so by editing the `"start"` command in package.json: 58 | 59 | ```js 60 | "start": "sirv public --single" 61 | ``` 62 | 63 | ## Using TypeScript 64 | 65 | This template comes with a script to set up a TypeScript development environment, you can run it immediately after cloning the template with: 66 | 67 | ```bash 68 | node scripts/setupTypeScript.js 69 | ``` 70 | 71 | Or remove the script via: 72 | 73 | ```bash 74 | rm scripts/setupTypeScript.js 75 | ``` 76 | 77 | If you want to use `baseUrl` or `path` aliases within your `tsconfig`, you need to set up `@rollup/plugin-alias` to tell Rollup to resolve the aliases. For more info, see [this StackOverflow question](https://stackoverflow.com/questions/63427935/setup-tsconfig-path-in-svelte). 78 | 79 | ## Deploying to the web 80 | 81 | ### With [Vercel](https://vercel.com) 82 | 83 | Install `vercel` if you haven't already: 84 | 85 | ```bash 86 | npm install -g vercel 87 | ``` 88 | 89 | Then, from within your project folder: 90 | 91 | ```bash 92 | cd public 93 | vercel deploy --name my-project 94 | ``` 95 | 96 | ### With [surge](https://surge.sh/) 97 | 98 | Install `surge` if you haven't already: 99 | 100 | ```bash 101 | npm install -g surge 102 | ``` 103 | 104 | Then, from within your project folder: 105 | 106 | ```bash 107 | npm run build 108 | surge public my-project.surge.sh 109 | ``` 110 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-app", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "rollup -c", 7 | "dev": "rollup -c -w", 8 | "start": "sirv public --no-clear" 9 | }, 10 | "devDependencies": { 11 | "@rollup/plugin-commonjs": "^17.1.0", 12 | "@rollup/plugin-node-resolve": "^11.2.1", 13 | "rollup": "^2.75.6", 14 | "rollup-plugin-css-only": "^3.1.0", 15 | "rollup-plugin-livereload": "^2.0.5", 16 | "rollup-plugin-svelte": "^7.1.0", 17 | "rollup-plugin-terser": "^7.0.2", 18 | "svelte": "^3.49.0", 19 | "svelte-highlight": "^5.3.2" 20 | }, 21 | "dependencies": { 22 | "buffer": "^6.0.3", 23 | "jsbi": "^4.3.0", 24 | "sirv-cli": "^2.0.2", 25 | "svelte-jsoneditor": "^0.3.60", 26 | "svelte-virtual-list-ce": "^3.1.0-beta.2" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /frontend/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/favicon.png -------------------------------------------------------------------------------- /frontend/public/fonts/OpenSans/OpenSans-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/OpenSans/OpenSans-Bold.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/OpenSans/OpenSans-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/OpenSans/OpenSans-BoldItalic.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/OpenSans/OpenSans-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/OpenSans/OpenSans-ExtraBold.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/OpenSans/OpenSans-ExtraBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/OpenSans/OpenSans-ExtraBoldItalic.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/OpenSans/OpenSans-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/OpenSans/OpenSans-Italic.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/OpenSans/OpenSans-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/OpenSans/OpenSans-Light.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/OpenSans/OpenSans-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/OpenSans/OpenSans-LightItalic.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/OpenSans/OpenSans-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/OpenSans/OpenSans-Medium.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/OpenSans/OpenSans-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/OpenSans/OpenSans-MediumItalic.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/OpenSans/OpenSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/OpenSans/OpenSans-Regular.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/OpenSans/OpenSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/OpenSans/OpenSans-SemiBold.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/OpenSans/OpenSans-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/OpenSans/OpenSans-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/shicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 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 | -------------------------------------------------------------------------------- /frontend/public/fonts/shicon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/shicon.ttf -------------------------------------------------------------------------------- /frontend/public/fonts/shicon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Akka0/Iridium-NG/43cde009836a1fd1ae3e9ca000edd38ec2b74133/frontend/public/fonts/shicon.woff -------------------------------------------------------------------------------- /frontend/public/global.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap'); 2 | 3 | /* http://meyerweb.com/eric/tools/css/reset/ 4 | v2.0-modified | 20110126 5 | License: none (public domain) 6 | */ 7 | 8 | html, body, div, span, applet, object, iframe, 9 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 10 | a, abbr, acronym, address, big, cite, code, 11 | del, dfn, em, img, ins, kbd, q, s, samp, 12 | small, strike, strong, sub, sup, tt, var, 13 | b, u, i, center, 14 | dl, dt, dd, ol, ul, li, 15 | fieldset, form, label, legend, 16 | table, caption, tbody, tfoot, thead, tr, th, td, 17 | article, aside, canvas, details, embed, 18 | figure, figcaption, footer, header, hgroup, 19 | menu, nav, output, ruby, section, summary, 20 | time, mark, audio, video { 21 | margin: 0; 22 | padding: 0; 23 | border: 0; 24 | font-size: 100%; 25 | font: inherit; 26 | vertical-align: baseline; 27 | } 28 | 29 | /* make sure to set some focus styles for accessibility */ 30 | :focus { 31 | outline: 0; 32 | } 33 | 34 | /* HTML5 display-role reset for older browsers */ 35 | article, aside, details, figcaption, figure, 36 | footer, header, hgroup, menu, nav, section { 37 | display: block; 38 | } 39 | 40 | body { 41 | line-height: 1; 42 | } 43 | 44 | ol, ul { 45 | list-style: none; 46 | } 47 | 48 | blockquote, q { 49 | quotes: none; 50 | } 51 | 52 | blockquote:before, blockquote:after, 53 | q:before, q:after { 54 | content: ''; 55 | content: none; 56 | } 57 | 58 | table { 59 | border-collapse: collapse; 60 | border-spacing: 0; 61 | } 62 | 63 | input[type=search]::-webkit-search-cancel-button, 64 | input[type=search]::-webkit-search-decoration, 65 | input[type=search]::-webkit-search-results-button, 66 | input[type=search]::-webkit-search-results-decoration { 67 | -webkit-appearance: none; 68 | -moz-appearance: none; 69 | } 70 | 71 | input[type=search] { 72 | -webkit-appearance: none; 73 | -moz-appearance: none; 74 | -webkit-box-sizing: content-box; 75 | -moz-box-sizing: content-box; 76 | box-sizing: content-box; 77 | } 78 | 79 | textarea { 80 | overflow: auto; 81 | vertical-align: top; 82 | resize: vertical; 83 | } 84 | 85 | /** 86 | * Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. 87 | */ 88 | 89 | audio, 90 | canvas, 91 | video { 92 | display: inline-block; 93 | *display: inline; 94 | *zoom: 1; 95 | max-width: 100%; 96 | } 97 | 98 | /** 99 | * Prevent modern browsers from displaying `audio` without controls. 100 | * Remove excess height in iOS 5 devices. 101 | */ 102 | 103 | audio:not([controls]) { 104 | display: none; 105 | height: 0; 106 | } 107 | 108 | /** 109 | * Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. 110 | * Known issue: no IE 6 support. 111 | */ 112 | 113 | [hidden] { 114 | display: none; 115 | } 116 | 117 | /** 118 | * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using 119 | * `em` units. 120 | * 2. Prevent iOS text size adjust after orientation change, without disabling 121 | * user zoom. 122 | */ 123 | 124 | html { 125 | font-size: 100%;/* 1 */ 126 | -webkit-text-size-adjust: 100%;/* 2 */ 127 | -ms-text-size-adjust: 100%; /* 2 */ 128 | } 129 | 130 | /** 131 | * Address `outline` inconsistency between Chrome and other browsers. 132 | */ 133 | 134 | a:focus { 135 | outline: thin dotted; 136 | } 137 | 138 | /** 139 | * Improve readability when focused and also mouse hovered in all browsers. 140 | */ 141 | 142 | a:active, 143 | a:hover { 144 | outline: 0; 145 | } 146 | 147 | /** 148 | * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 149 | * 2. Improve image quality when scaled in IE 7. 150 | */ 151 | 152 | img { 153 | border: 0; /* 1 */ 154 | -ms-interpolation-mode: bicubic; /* 2 */ 155 | } 156 | 157 | /** 158 | * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. 159 | */ 160 | 161 | figure { 162 | margin: 0; 163 | } 164 | 165 | /** 166 | * Correct margin displayed oddly in IE 6/7. 167 | */ 168 | 169 | form { 170 | margin: 0; 171 | } 172 | 173 | /** 174 | * Define consistent border, margin, and padding. 175 | */ 176 | 177 | fieldset { 178 | border: 1px solid #c0c0c0; 179 | margin: 0 2px; 180 | padding: 0.35em 0.625em 0.75em; 181 | } 182 | 183 | /** 184 | * 1. Correct color not being inherited in IE 6/7/8/9. 185 | * 2. Correct text not wrapping in Firefox 3. 186 | * 3. Correct alignment displayed oddly in IE 6/7. 187 | */ 188 | 189 | legend { 190 | border: 0; /* 1 */ 191 | padding: 0; 192 | white-space: normal; /* 2 */ 193 | *margin-left: -7px; /* 3 */ 194 | } 195 | 196 | /** 197 | * 1. Correct font size not being inherited in all browsers. 198 | * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, 199 | * and Chrome. 200 | * 3. Improve appearance and consistency in all browsers. 201 | */ 202 | 203 | button, 204 | input, 205 | select, 206 | textarea { 207 | font-size: 100%; /* 1 */ 208 | margin: 0; /* 2 */ 209 | vertical-align: baseline; /* 3 */ 210 | *vertical-align: middle; /* 3 */ 211 | } 212 | 213 | /** 214 | * Address Firefox 3+ setting `line-height` on `input` using `!important` in 215 | * the UA stylesheet. 216 | */ 217 | 218 | button, 219 | input { 220 | line-height: normal; 221 | } 222 | 223 | /** 224 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 225 | * All other form control elements do not inherit `text-transform` values. 226 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. 227 | * Correct `select` style inheritance in Firefox 4+ and Opera. 228 | */ 229 | 230 | button, 231 | select { 232 | text-transform: none; 233 | } 234 | 235 | /** 236 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 237 | * and `video` controls. 238 | * 2. Correct inability to style clickable `input` types in iOS. 239 | * 3. Improve usability and consistency of cursor style between image-type 240 | * `input` and others. 241 | * 4. Remove inner spacing in IE 7 without affecting normal text inputs. 242 | * Known issue: inner spacing remains in IE 6. 243 | */ 244 | 245 | button, 246 | html input[type="button"], /* 1 */ 247 | input[type="reset"], 248 | input[type="submit"] { 249 | -webkit-appearance: button; /* 2 */ 250 | cursor: pointer; /* 3 */ 251 | *overflow: visible; /* 4 */ 252 | } 253 | 254 | /** 255 | * Re-set default cursor for disabled elements. 256 | */ 257 | 258 | button[disabled], 259 | html input[disabled] { 260 | cursor: default; 261 | } 262 | 263 | /** 264 | * 1. Address box sizing set to content-box in IE 8/9. 265 | * 2. Remove excess padding in IE 8/9. 266 | * 3. Remove excess padding in IE 7. 267 | * Known issue: excess padding remains in IE 6. 268 | */ 269 | 270 | input[type="checkbox"], 271 | input[type="radio"] { 272 | box-sizing: border-box; /* 1 */ 273 | padding: 0; /* 2 */ 274 | *height: 13px; /* 3 */ 275 | *width: 13px; /* 3 */ 276 | } 277 | 278 | /** 279 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 280 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 281 | * (include `-moz` to future-proof). 282 | */ 283 | 284 | input[type="search"] { 285 | -webkit-appearance: textfield; /* 1 */ 286 | -moz-box-sizing: content-box; 287 | -webkit-box-sizing: content-box; /* 2 */ 288 | box-sizing: content-box; 289 | } 290 | 291 | /** 292 | * Remove inner padding and search cancel button in Safari 5 and Chrome 293 | * on OS X. 294 | */ 295 | 296 | input[type="search"]::-webkit-search-cancel-button, 297 | input[type="search"]::-webkit-search-decoration { 298 | -webkit-appearance: none; 299 | } 300 | 301 | /** 302 | * Remove inner padding and border in Firefox 3+. 303 | */ 304 | 305 | button::-moz-focus-inner, 306 | input::-moz-focus-inner { 307 | border: 0; 308 | padding: 0; 309 | } 310 | 311 | /** 312 | * 1. Remove default vertical scrollbar in IE 6/7/8/9. 313 | * 2. Improve readability and alignment in all browsers. 314 | */ 315 | 316 | textarea { 317 | overflow: auto; /* 1 */ 318 | vertical-align: top; /* 2 */ 319 | } 320 | 321 | /** 322 | * Remove most spacing between table cells. 323 | */ 324 | 325 | table { 326 | border-collapse: collapse; 327 | border-spacing: 0; 328 | } 329 | 330 | html, 331 | button, 332 | input, 333 | select, 334 | textarea { 335 | color: #222; 336 | } 337 | 338 | 339 | ::-moz-selection { 340 | background: #b3d4fc; 341 | text-shadow: none; 342 | } 343 | 344 | ::selection { 345 | background: #b3d4fc; 346 | text-shadow: none; 347 | } 348 | 349 | img { 350 | vertical-align: middle; 351 | } 352 | 353 | fieldset { 354 | border: 0; 355 | margin: 0; 356 | padding: 0; 357 | } 358 | 359 | textarea { 360 | resize: vertical; 361 | } 362 | 363 | .chromeframe { 364 | margin: 0.2em 0; 365 | background: #ccc; 366 | color: #000; 367 | padding: 0.2em 0; 368 | } 369 | 370 | 371 | @font-face { 372 | font-family: 'shicon'; 373 | src: 374 | url('fonts/shicon.ttf?kckii0') format('truetype'), 375 | url('fonts/shicon.woff?kckii0') format('woff'), 376 | url('fonts/shicon.svg?kckii0#shicon') format('svg'); 377 | font-weight: normal; 378 | font-style: normal; 379 | font-display: block; 380 | } 381 | 382 | [data-icon]:before { 383 | /* use !important to prevent issues with browser extensions that change fonts */ 384 | font-family: 'shicon' !important; 385 | speak: never; 386 | font-style: normal; 387 | font-weight: normal; 388 | font-variant: normal; 389 | text-transform: none; 390 | line-height: 1; 391 | 392 | /* Better Font Rendering =========== */ 393 | -webkit-font-smoothing: antialiased; 394 | -moz-osx-font-smoothing: grayscale; 395 | } 396 | 397 | [data-icon="network-off-outline"]:before{content:"\e924"} 398 | [data-icon="play-network-outline"]:before{content:"\e934"} 399 | [data-icon="open-in-app"]:before{content:"\e937"} 400 | [data-icon="card-off"]:before{content:"\e923"} 401 | [data-icon="sharios"]:before{content:"\e922"} 402 | [data-icon="image-off-1"]:before{content:"\e921"} 403 | [data-icon="language"]:before{content:"\e918"} 404 | [data-icon="twitter"]:before{content:"\e916"} 405 | [data-icon="discord"]:before{content:"\e917"} 406 | [data-icon="heart"]:before{content:"\e912"} 407 | [data-icon="help"]:before{content:"\e910"} 408 | [data-icon="numeric-3"]:before{content:"\e90e"} 409 | [data-icon="numeric-4"]:before{content:"\e90f"} 410 | [data-icon="arrow-all"]:before{content:"\e90a"} 411 | [data-icon="email-outline"]:before{content:"\e90b"} 412 | [data-icon="keyboard_arrow_down"]:before{content:"\e90c"} 413 | [data-icon="information-outline"]:before{content:"\e90d"} 414 | [data-icon="clear"]:before{content:"\e911"} 415 | [data-icon="keyboard_arrow_left"]:before{content:"\e913"} 416 | [data-icon="keyboard_arrow_right"]:before{content:"\e914"} 417 | [data-icon="keyboard_arrow_up"]:before{content:"\e915"} 418 | [data-icon="fullscreen"]:before{content:"\e91a"} 419 | [data-icon="unfold_more"]:before{content:"\e91b"} 420 | [data-icon="check_box"]:before{content:"\e91c"} 421 | [data-icon="check_box_outline_blank"]:before{content:"\e91d"} 422 | [data-icon="collections_bookmark"]:before{content:"\e925"} 423 | [data-icon="code"]:before{content:"\e926"} 424 | [data-icon="chat"]:before{content:"\e927"} 425 | [data-icon="link"]:before{content:"\e928"} 426 | [data-icon="launch"]:before{content:"\e929"} 427 | [data-icon="file"]:before{content:"\e92a"} 428 | [data-icon="crop_free"]:before{content:"\e92b"} 429 | [data-icon="crop_original"]:before{content:"\e92c"} 430 | [data-icon="zoom_out_map"]:before{content:"\e92d"} 431 | [data-icon="fullscreen_exit"]:before{content:"\e92e"} 432 | [data-icon="menu"]:before{content:"\e92f"} 433 | [data-icon="keyboard_control"]:before{content:"\e930"} 434 | [data-icon="more_vert"]:before{content:"\e931"} 435 | [data-icon="search"]:before{content:"\e932"} 436 | [data-icon="settings"]:before{content:"\e933"} 437 | [data-icon="insert_drive_file"]:before{content:"\e935"} 438 | [data-icon="file-image-outline"]:before{content:"\e936"} 439 | [data-icon="pan"]:before{content:"\e939"} 440 | [data-icon="image"]:before{content:"\e93b"} 441 | [data-icon="zoom-out"]:before{content:"\e93c"} 442 | [data-icon="zoom-in"]:before{content:"\e93d"} 443 | [data-icon="cogs"]:before{content:"\e93e"} 444 | [data-icon="view-grid-plus"]:before{content:"\e93f"} 445 | [data-icon="palette"]:before{content:"\e941"} 446 | [data-icon="download"]:before{content:"\e942"} 447 | [data-icon="check"]:before{content:"\e943"} 448 | [data-icon="home"]:before{content:"\e944"} 449 | [data-icon="loading"]:before{content:"\e97c"} 450 | [data-icon="layout-gid"]:before{content:"\e91f"} 451 | [data-icon="layout-wide"]:before{content:"\e919"} 452 | [data-icon="layout-mid"]:before{content:"\e91e"} 453 | [data-icon="shinshin"]:before{content:"\e920"} 454 | [data-icon="art0"]:before{content:"\e900"} 455 | [data-icon="art1"]:before{content:"\e901"} 456 | [data-icon="art2"]:before{content:"\e902"} 457 | [data-icon="art3"]:before{content:"\e903"} 458 | [data-icon="art4"]:before{content:"\e904"} 459 | [data-icon="weapon"]:before{content:"\e905"} 460 | [data-icon="const"]:before{content:"\e906"} 461 | [data-icon="table"]:before{content:"\e907"} 462 | [data-icon="talent"]:before{content:"\e908"} 463 | [data-icon="info"]:before{content:"\e909"} 464 | 465 | 466 | body { 467 | font-family: 'Open Sans', sans-serif; 468 | background: #333; 469 | display: flex; 470 | min-height: 100vh; 471 | max-height: 100vh; 472 | max-width: 100vw; 473 | align-items: stretch; 474 | justify-content: stretch; 475 | color: white; 476 | } 477 | 478 | button { 479 | background: #4360A2; 480 | border: 0; 481 | border-bottom: 2px solid rgba(0,0,0,0.4); 482 | padding: 0.3em 0.4em; 483 | color: inherit; 484 | 485 | } 486 | 487 | button:hover { 488 | filter: brightness(1.5) 489 | } 490 | 491 | .green { 492 | background: green; 493 | } 494 | .red { 495 | background: darkred; 496 | } 497 | 498 | -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Iridium frontend 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /frontend/rollup.config.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte'; 2 | import commonjs from '@rollup/plugin-commonjs'; 3 | import resolve from '@rollup/plugin-node-resolve'; 4 | import livereload from 'rollup-plugin-livereload'; 5 | import { terser } from 'rollup-plugin-terser'; 6 | import css from 'rollup-plugin-css-only'; 7 | 8 | const production = !process.env.ROLLUP_WATCH; 9 | 10 | function serve() { 11 | let server; 12 | 13 | function toExit() { 14 | if (server) server.kill(0); 15 | } 16 | 17 | return { 18 | writeBundle() { 19 | if (server) return; 20 | server = require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { 21 | stdio: ['ignore', 'inherit', 'inherit'], 22 | shell: true 23 | }); 24 | 25 | process.on('SIGTERM', toExit); 26 | process.on('exit', toExit); 27 | } 28 | }; 29 | } 30 | 31 | export default { 32 | input: 'src/main.js', 33 | output: { 34 | sourcemap: true, 35 | format: 'iife', 36 | name: 'app', 37 | file: 'public/build/bundle.js', 38 | inlineDynamicImports: true, 39 | }, 40 | plugins: [ 41 | svelte({ 42 | compilerOptions: { 43 | // enable run-time checks when not in production 44 | dev: !production 45 | } 46 | }), 47 | // we'll extract any component CSS out into 48 | // a separate file - better for performance 49 | css({ output: 'bundle.css' }), 50 | 51 | // If you have external dependencies installed from 52 | // npm, you'll most likely need these plugins. In 53 | // some cases you'll need additional configuration - 54 | // consult the documentation for details: 55 | // https://github.com/rollup/plugins/tree/master/packages/commonjs 56 | resolve({ 57 | browser: true, 58 | dedupe: ['svelte'] 59 | }), 60 | commonjs(), 61 | 62 | // In dev mode, call `npm run start` once 63 | // the bundle has been generated 64 | !production && serve(), 65 | 66 | // Watch the `public` directory and refresh the 67 | // browser on changes when not in production 68 | !production && livereload('public'), 69 | 70 | // If we're building for production (npm run build 71 | // instead of npm run dev), minify 72 | production && terser() 73 | ], 74 | watch: { 75 | clearScreen: false 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /frontend/scripts/setupTypeScript.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** This script modifies the project to support TS code in .svelte files like: 4 | 5 | 8 | 9 | As well as validating the code for CI. 10 | */ 11 | 12 | /** To work on this script: 13 | rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template 14 | */ 15 | 16 | const fs = require("fs") 17 | const path = require("path") 18 | const { argv } = require("process") 19 | 20 | const projectRoot = argv[2] || path.join(__dirname, "..") 21 | 22 | // Add deps to pkg.json 23 | const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8")) 24 | packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, { 25 | "svelte-check": "^2.0.0", 26 | "svelte-preprocess": "^4.0.0", 27 | "@rollup/plugin-typescript": "^8.0.0", 28 | "typescript": "^4.0.0", 29 | "tslib": "^2.0.0", 30 | "@tsconfig/svelte": "^2.0.0" 31 | }) 32 | 33 | // Add script for checking 34 | packageJSON.scripts = Object.assign(packageJSON.scripts, { 35 | "check": "svelte-check --tsconfig ./tsconfig.json" 36 | }) 37 | 38 | // Write the package JSON 39 | fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " ")) 40 | 41 | // mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too 42 | const beforeMainJSPath = path.join(projectRoot, "src", "main.js") 43 | const afterMainTSPath = path.join(projectRoot, "src", "main.ts") 44 | fs.renameSync(beforeMainJSPath, afterMainTSPath) 45 | 46 | // Switch the app.svelte file to use TS 47 | const appSveltePath = path.join(projectRoot, "src", "App.svelte") 48 | let appFile = fs.readFileSync(appSveltePath, "utf8") 49 | appFile = appFile.replace(" 270 | 271 | 272 | {@html materialDarker} 273 | 274 | 275 | 339 |
340 |
341 |
342 | 343 |
(orand = !orand)}> 344 | AND 345 | OR 346 |
347 | 348 |
349 |
350 |
351 |
352 |
Time
353 |
#
354 |
Sender
355 |
ID
356 |
Proto Name
357 |
Length
358 |
JSON
359 |
360 |
361 | 367 | { 372 | showPacketDetails(packet); 373 | scrollToIndex(Math.max(currentPacket.index - 5, 0)); 374 | }} 375 | /> 376 | 377 |
378 |
379 |
380 |
381 |
382 |
383 |
Time
384 |
#
385 |
Sender
386 |
ID
387 |
Proto Name
388 |
Length
389 |
JSON
390 |
391 |
392 | 398 | 399 | showPacketDetails(packet)} 404 | /> 405 | 406 |
407 |
408 |
409 |
410 |
411 |
412 | {#if currentPacket} 413 | {#if currentPacket.object} 414 |
415 | 420 |
421 | {/if} 422 | {#if currentPacket.decode && showDecode} 423 |
424 | 425 |
426 | {/if} 427 | {/if} 428 |
429 |
430 | 431 | 641 | -------------------------------------------------------------------------------- /frontend/src/Packet.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 |
13 | {#if packet.reltime}{packet.reltime.toFixed(3)}{/if} 14 |
15 |
{idx}
16 |
17 | {packet.source ? "CLIENT" : "SERVER"} 20 |
21 |
{packet.packetID}
22 |
{packet.protoName}
23 |
{packetLength || ""}
24 |
{packetString}
25 |
26 | 27 | 84 | -------------------------------------------------------------------------------- /frontend/src/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.svelte'; 2 | 3 | const app = new App({ 4 | target: document.body, 5 | props: { 6 | name: 'world' 7 | } 8 | }); 9 | 10 | export default app; -------------------------------------------------------------------------------- /frontend/src/proto_raw_decoder.js: -------------------------------------------------------------------------------- 1 | const { decodeProto, TYPES } = require("./protobuf_decoder/protobufDecoder"); 2 | const { 3 | decodeFixed32, 4 | decodeFixed64, 5 | decodeVarintParts, 6 | } = require("./protobuf_decoder/protobufPartDecoder"); 7 | const { parseInput } = require("./protobuf_decoder/hexUtils"); 8 | const { 9 | decodeVarint, 10 | interpretAsSignedType, 11 | } = require("./protobuf_decoder/varintUtils"); 12 | 13 | function parseType(decodeList) { 14 | return decodeList.reduce((pre, cur, index, arr) => { 15 | if (index === 0) { 16 | return `${cur.type}:${cur.value}`; 17 | } else { 18 | } 19 | return `${pre}, ${cur.type}:${cur.value}`; 20 | }, ""); 21 | } 22 | 23 | function getNextIndex(key, repeatedMap) { 24 | if (!repeatedMap[key]) { 25 | repeatedMap[key] = 0; 26 | } 27 | repeatedMap[key]++; 28 | return repeatedMap[key] < 2 ? key : `${key}#${repeatedMap[key].toString()}`; 29 | } 30 | 31 | function processProtoPart(raw) { 32 | const data = decodeProto(parseInput(raw)); 33 | const result = {}; 34 | const repeatedMap = {}; 35 | data.parts.forEach((e) => { 36 | let key; 37 | let res; 38 | switch (e.type) { 39 | case TYPES.FIXED32: 40 | const fixed32 = decodeFixed32(e.value); 41 | key = getNextIndex(`${e.index}:32b`, repeatedMap); 42 | res = parseType(fixed32); 43 | break; 44 | 45 | case TYPES.FIXED64: 46 | const fixed64 = decodeFixed64(e.value); 47 | key = getNextIndex(`${e.index}:64b`, repeatedMap); 48 | res = parseType(fixed64); 49 | break; 50 | 51 | case TYPES.VARINT: 52 | const varint = decodeVarintParts(e.value); 53 | key = getNextIndex(`${e.index}:varint`, repeatedMap); 54 | res = parseType(varint); 55 | break; 56 | 57 | case TYPES.STRING: 58 | const str = processProtoPart(e.value.toString("base64")); 59 | key = getNextIndex(`${e.index}:ld`, repeatedMap); 60 | 61 | if (e.value.length > 0 && !str.leftOver) { 62 | res = str; 63 | } else { 64 | res = []; 65 | decodeRepeated(e, res); 66 | 67 | res.push( 68 | `String:${e.value.toString()}, Raw:${e.value.toString("hex")}` 69 | ); 70 | } 71 | 72 | break; 73 | } 74 | result[key] = res; 75 | }); 76 | 77 | if (data.leftOver && data.leftOver.length > 0) { 78 | result.leftOver = data.leftOver.toString("base64"); 79 | } 80 | 81 | return result; 82 | } 83 | 84 | function decodeRepeated(e, res) { 85 | try { 86 | let list = []; 87 | let len = 0; 88 | while (len < e.value.length) { 89 | const reslove = decodeVarint(e.value, len); 90 | len += reslove.length; 91 | list.push(reslove.value); 92 | } 93 | 94 | if (list.length > 0) { 95 | res.push(`Repeated Int:[${list.toString()}]`); 96 | 97 | if (list[0] !== interpretAsSignedType(list[0])) { 98 | const newList = list.map((i) => interpretAsSignedType(i)); 99 | res.push(`Repeated Signed Int:[${newList.toString()}]`); 100 | } 101 | } 102 | } catch (ex) { 103 | // it must be not this type 104 | } 105 | } 106 | 107 | function protoRawDecode(raw) { 108 | return processProtoPart(raw); 109 | } 110 | 111 | module.exports = { 112 | protoRawDecode, 113 | }; 114 | -------------------------------------------------------------------------------- /frontend/src/protobuf_decoder/hexUtils.js: -------------------------------------------------------------------------------- 1 | /* 2 | Source: https://github.com/pawitp/protobuf-decoder by pawitp 3 | Modified to adapt common-js 4 | */ 5 | const Buffer = require('buffer/').Buffer 6 | 7 | function parseInput(input) { 8 | const normalizedInput = input.replace(/\s/g, ""); 9 | const normalizedHexInput = normalizedInput.replace(/0x/g, "").toLowerCase(); 10 | if (isHex(normalizedHexInput)) { 11 | return Buffer.from(normalizedHexInput, "hex"); 12 | } else { 13 | return Buffer.from(normalizedInput, "base64"); 14 | } 15 | } 16 | 17 | function isHex(string) { 18 | let result = true; 19 | for (const char of string) { 20 | if (!((char >= "a" && char <= "f") || (char >= "0" && char <= "9"))) { 21 | result = false; 22 | } 23 | } 24 | return result; 25 | } 26 | 27 | function bufferToPrettyHex(buffer) { 28 | let output = ""; 29 | for (const v of buffer) { 30 | if (output !== "") { 31 | output += " "; 32 | } 33 | 34 | const hex = v.toString(16); 35 | if (hex.length === 1) { 36 | output += "0" + hex; 37 | } else { 38 | output += hex; 39 | } 40 | } 41 | return output; 42 | } 43 | 44 | function bufferLeToBeHex(buffer) { 45 | let output = ""; 46 | for (const v of buffer) { 47 | const hex = v.toString(16); 48 | if (hex.length === 1) { 49 | output = "0" + hex + output; 50 | } else { 51 | output = hex + output; 52 | } 53 | } 54 | return output; 55 | } 56 | 57 | module.exports = { 58 | parseInput, isHex, bufferToPrettyHex, bufferLeToBeHex 59 | } -------------------------------------------------------------------------------- /frontend/src/protobuf_decoder/protobufDecoder.js: -------------------------------------------------------------------------------- 1 | /* 2 | Source: https://github.com/pawitp/protobuf-decoder by pawitp 3 | Modified to adapt common-js 4 | */ 5 | const { decodeVarint } = require("./varintUtils"); 6 | 7 | class BufferReader { 8 | constructor(buffer) { 9 | this.buffer = buffer; 10 | this.offset = 0; 11 | } 12 | 13 | readVarInt() { 14 | const result = decodeVarint(this.buffer, this.offset); 15 | this.offset += result.length; 16 | 17 | return result.value; 18 | } 19 | 20 | readBuffer(length) { 21 | this.checkByte(length); 22 | const result = this.buffer.slice(this.offset, this.offset + length); 23 | this.offset += length; 24 | 25 | return result; 26 | } 27 | 28 | // gRPC has some additional header - remove it 29 | trySkipGrpcHeader() { 30 | const backupOffset = this.offset; 31 | 32 | if (this.buffer[this.offset] === 0) { 33 | this.offset++; 34 | const length = this.buffer.readInt32BE(this.offset); 35 | this.offset += 4; 36 | 37 | if (length > this.leftBytes()) { 38 | // Something is wrong, revert 39 | this.offset = backupOffset; 40 | } 41 | } 42 | } 43 | 44 | leftBytes() { 45 | return this.buffer.length - this.offset; 46 | } 47 | 48 | checkByte(length) { 49 | const bytesAvailable = this.leftBytes(); 50 | if (length > bytesAvailable) { 51 | throw new Error( 52 | "Not enough bytes left. Requested: " + 53 | length + 54 | " left: " + 55 | bytesAvailable 56 | ); 57 | } 58 | } 59 | 60 | checkpoint() { 61 | this.savedOffset = this.offset; 62 | } 63 | 64 | resetToCheckpoint() { 65 | this.offset = this.savedOffset; 66 | } 67 | } 68 | 69 | const TYPES = { 70 | VARINT: 0, 71 | FIXED64: 1, 72 | STRING: 2, 73 | FIXED32: 5 74 | }; 75 | 76 | function decodeProto(buffer) { 77 | const reader = new BufferReader(buffer); 78 | const parts = []; 79 | 80 | reader.trySkipGrpcHeader(); 81 | 82 | try { 83 | while (reader.leftBytes() > 0) { 84 | reader.checkpoint(); 85 | 86 | const indexType = parseInt(reader.readVarInt().toString()); 87 | const type = indexType & 0b111; 88 | const index = indexType >> 3; 89 | 90 | let value; 91 | if (type === TYPES.VARINT) { 92 | value = reader.readVarInt().toString(); 93 | } else if (type === TYPES.STRING) { 94 | const length = parseInt(reader.readVarInt().toString()); 95 | value = reader.readBuffer(length); 96 | } else if (type === TYPES.FIXED32) { 97 | value = reader.readBuffer(4); 98 | } else if (type === TYPES.FIXED64) { 99 | value = reader.readBuffer(8); 100 | } else { 101 | throw new Error("Unknown type: " + type); 102 | } 103 | 104 | parts.push({ 105 | index, 106 | type, 107 | value 108 | }); 109 | } 110 | } catch (err) { 111 | reader.resetToCheckpoint(); 112 | } 113 | 114 | return { 115 | parts, 116 | leftOver: reader.readBuffer(reader.leftBytes()) 117 | }; 118 | } 119 | 120 | function typeToString(type) { 121 | switch (type) { 122 | case TYPES.VARINT: 123 | return "varint"; 124 | case TYPES.STRING: 125 | return "string"; 126 | case TYPES.FIXED32: 127 | return "fixed32"; 128 | case TYPES.FIXED64: 129 | return "fixed64"; 130 | default: 131 | return "unknown"; 132 | } 133 | } 134 | 135 | module.exports = { 136 | TYPES, decodeProto, typeToString 137 | } -------------------------------------------------------------------------------- /frontend/src/protobuf_decoder/protobufPartDecoder.js: -------------------------------------------------------------------------------- 1 | /* 2 | Source: https://github.com/pawitp/protobuf-decoder by pawitp 3 | Modified to adapt common-js 4 | */ 5 | const JSBI = require("jsbi"); 6 | const { bufferLeToBeHex } = require( "./hexUtils"); 7 | const { interpretAsSignedType } = require( "./varintUtils"); 8 | 9 | function decodeFixed32(value) { 10 | const floatValue = value.readFloatLE(0); 11 | const intValue = value.readInt32LE(0); 12 | const uintValue = value.readUInt32LE(0); 13 | 14 | const result = []; 15 | 16 | result.push({ type: "Int", value: intValue }); 17 | 18 | if (intValue !== uintValue) { 19 | result.push({ type: "Unsigned Int", value: uintValue }); 20 | } 21 | 22 | result.push({ type: "Float", value: floatValue }); 23 | 24 | return result; 25 | } 26 | 27 | function decodeFixed64(value) { 28 | const floatValue = value.readDoubleLE(0); 29 | const uintValue = JSBI.BigInt("0x" + bufferLeToBeHex(value)); 30 | const intValue = twoComplements(uintValue); 31 | 32 | const result = []; 33 | 34 | result.push({ type: "Int", value: intValue.toString() }); 35 | 36 | if (intValue !== uintValue) { 37 | result.push({ type: "Unsigned Int", value: uintValue.toString() }); 38 | } 39 | 40 | result.push({ type: "Double", value: floatValue }); 41 | 42 | return result; 43 | } 44 | 45 | function decodeVarintParts(value) { 46 | const result = []; 47 | const intVal = JSBI.BigInt(value); 48 | result.push({ type: "Int", value: intVal.toString() }); 49 | 50 | const signedIntVal = interpretAsSignedType(intVal); 51 | if (signedIntVal !== intVal) { 52 | result.push({ type: "Signed Int", value: signedIntVal.toString() }); 53 | } 54 | return result; 55 | } 56 | 57 | const maxLong = JSBI.BigInt("0x7fffffffffffffff"); 58 | const longForComplement = JSBI.BigInt("0x10000000000000000"); 59 | 60 | function twoComplements(uintValue) { 61 | if (JSBI.greaterThan(uintValue, maxLong)) { 62 | return JSBI.subtract(uintValue, longForComplement); 63 | } else { 64 | return uintValue; 65 | } 66 | } 67 | 68 | module.exports = { 69 | decodeFixed32, decodeFixed64, decodeVarintParts 70 | } -------------------------------------------------------------------------------- /frontend/src/protobuf_decoder/varintUtils.js: -------------------------------------------------------------------------------- 1 | /* 2 | Source: https://github.com/pawitp/protobuf-decoder by pawitp 3 | Modified to adapt common-js 4 | */ 5 | const JSBI = require("jsbi"); 6 | 7 | const BIGINT_1 = JSBI.BigInt(1); 8 | const BIGINT_2 = JSBI.BigInt(2); 9 | 10 | function interpretAsSignedType(n) { 11 | // see https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/wire_format_lite.h#L857-L876 12 | // however, this is a simpler equivalent formula 13 | const isEven = JSBI.equal(JSBI.bitwiseAnd(n, JSBI.BigInt(1)), JSBI.BigInt(0)); 14 | if (isEven) { 15 | return JSBI.divide(n, BIGINT_2); 16 | } else { 17 | return JSBI.multiply( 18 | JSBI.BigInt(-1), 19 | JSBI.divide(JSBI.add(n, BIGINT_1), BIGINT_2) 20 | ); 21 | } 22 | } 23 | 24 | function decodeVarint(buffer, offset) { 25 | let res = JSBI.BigInt(0); 26 | let shift = 0; 27 | let byte = 0; 28 | 29 | do { 30 | if (offset >= buffer.length) { 31 | throw new RangeError("Index out of bound decoding varint"); 32 | } 33 | 34 | byte = buffer[offset++]; 35 | 36 | const multiplier = JSBI.exponentiate(BIGINT_2, JSBI.BigInt(shift)); 37 | const thisByteValue = JSBI.multiply(JSBI.BigInt(byte & 0x7f), multiplier); 38 | shift += 7; 39 | res = JSBI.add(res, thisByteValue); 40 | } while (byte >= 0x80); 41 | 42 | return { 43 | value: res, 44 | length: shift / 7 45 | }; 46 | } 47 | 48 | module.exports = { 49 | interpretAsSignedType, decodeVarint 50 | } 51 | -------------------------------------------------------------------------------- /frontend_server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "embed" 5 | "github.com/gin-gonic/contrib/static" 6 | "github.com/gin-gonic/gin" 7 | "io" 8 | "io/fs" 9 | "log" 10 | "net/http" 11 | "os" 12 | ) 13 | 14 | var eventStream = make(chan string) 15 | 16 | //go:embed frontend/public 17 | var staticFolder embed.FS 18 | 19 | func startServer() { 20 | r := gin.Default() 21 | r.GET("/api/start", apiStart) 22 | r.GET("/api/stop", apiStop) 23 | r.POST("/api/upload", apiUpload) 24 | r.GET("/api/stream", stream) 25 | r.Use(static.Serve("/", EmbedFolder(staticFolder, "frontend/public"))) 26 | 27 | defer close(eventStream) 28 | err := r.Run(":1984") 29 | if err != nil { 30 | log.Fatalln("Could not start http server", err) 31 | } 32 | } 33 | 34 | func apiStart(c *gin.Context) { 35 | go openCapture() 36 | } 37 | 38 | func apiStop(c *gin.Context) { 39 | go closeHandle() 40 | } 41 | 42 | func apiUpload(c *gin.Context) { 43 | file, err := c.FormFile("file") 44 | if err != nil { 45 | log.Println("Could not handle upload file", err) 46 | return 47 | } 48 | err = c.SaveUploadedFile(file, os.TempDir()+file.Filename) 49 | if err != nil { 50 | log.Println("Could not handle upload file", err) 51 | return 52 | } 53 | go openPcap(os.TempDir() + file.Filename) 54 | } 55 | 56 | func stream(c *gin.Context) { 57 | c.Stream(func(w io.Writer) bool { 58 | c.SSEvent("packetNotify", <-eventStream) 59 | return true 60 | }) 61 | } 62 | 63 | func sendStreamMsg(msg string) { 64 | go func() { 65 | eventStream <- msg 66 | }() 67 | } 68 | 69 | type embedFileSystem struct { 70 | http.FileSystem 71 | } 72 | 73 | func (e embedFileSystem) Exists(prefix string, path string) bool { 74 | _, err := e.Open(path) 75 | if err != nil { 76 | return false 77 | } 78 | return true 79 | } 80 | 81 | func EmbedFolder(fsEmbed embed.FS, targetPath string) static.ServeFileSystem { 82 | fsys, err := fs.Sub(fsEmbed, targetPath) 83 | if err != nil { 84 | panic(err) 85 | } 86 | return embedFileSystem{ 87 | FileSystem: http.FS(fsys), 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/Akka0/Iridium-NG 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/fatih/color v1.13.0 7 | github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19 8 | github.com/gin-gonic/gin v1.8.0 9 | github.com/google/gopacket v1.1.19 10 | github.com/jhump/protoreflect v1.12.0 11 | github.com/xtaci/kcp-go v5.4.20+incompatible 12 | ) 13 | 14 | require ( 15 | github.com/gin-contrib/sse v0.1.0 // indirect 16 | github.com/go-playground/locales v0.14.0 // indirect 17 | github.com/go-playground/universal-translator v0.18.0 // indirect 18 | github.com/go-playground/validator/v10 v10.11.0 // indirect 19 | github.com/goccy/go-json v0.9.7 // indirect 20 | github.com/golang/protobuf v1.5.2 // indirect 21 | github.com/json-iterator/go v1.1.12 // indirect 22 | github.com/klauspost/cpuid/v2 v2.0.6 // indirect 23 | github.com/klauspost/reedsolomon v1.9.16 // indirect 24 | github.com/leodido/go-urn v1.2.1 // indirect 25 | github.com/mattn/go-colorable v0.1.9 // indirect 26 | github.com/mattn/go-isatty v0.0.14 // indirect 27 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 28 | github.com/modern-go/reflect2 v1.0.2 // indirect 29 | github.com/pelletier/go-toml/v2 v2.0.1 // indirect 30 | github.com/pkg/errors v0.9.1 // indirect 31 | github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect 32 | github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect 33 | github.com/tjfoc/gmsm v1.4.1 // indirect 34 | github.com/ugorji/go/codec v1.2.7 // indirect 35 | github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 // indirect 36 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect 37 | golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect 38 | golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect 39 | golang.org/x/text v0.3.7 // indirect 40 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect 41 | google.golang.org/protobuf v1.28.0 // indirect 42 | gopkg.in/yaml.v2 v2.4.0 // indirect 43 | ) 44 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 4 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 5 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 6 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 7 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 11 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 12 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 13 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 14 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 15 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 16 | github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= 17 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 18 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 19 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 20 | github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19 h1:J2LPEOcQmWaooBnBtUDV9KHFEnP5LYTZY03GiQ0oQBw= 21 | github.com/gin-gonic/contrib v0.0.0-20201101042839-6a891bf89f19/go.mod h1:iqneQ2Df3omzIVTkIfn7c1acsVnMGiSLn4XF5Blh3Yg= 22 | github.com/gin-gonic/gin v1.8.0 h1:4WFH5yycBMA3za5Hnl425yd9ymdw1XPm4666oab+hv4= 23 | github.com/gin-gonic/gin v1.8.0/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= 24 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 25 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 26 | github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= 27 | github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= 28 | github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= 29 | github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= 30 | github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw= 31 | github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= 32 | github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= 33 | github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 34 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 35 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 36 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 37 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 38 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 39 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 40 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 41 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 42 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 43 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 44 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 45 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 46 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 47 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 48 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 49 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 50 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 51 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 52 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 53 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 54 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= 55 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 56 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 57 | github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= 58 | github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= 59 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 60 | github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= 61 | github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= 62 | github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ= 63 | github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E= 64 | github.com/jhump/protoreflect v1.12.0 h1:1NQ4FpWMgn3by/n1X0fbeKEUxP1wBt7+Oitpv01HR10= 65 | github.com/jhump/protoreflect v1.12.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI= 66 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 67 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 68 | github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI= 69 | github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 70 | github.com/klauspost/reedsolomon v1.9.16 h1:mR0AwphBwqFv/I3B9AHtNKvzuowI1vrj8/3UX4XRmHA= 71 | github.com/klauspost/reedsolomon v1.9.16/go.mod h1:eqPAcE7xar5CIzcdfwydOEdcmchAKAP/qs14y4GCBOk= 72 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 73 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 74 | github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 75 | github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 76 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 77 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 78 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 79 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 80 | github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= 81 | github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= 82 | github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= 83 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 84 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 85 | github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= 86 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 87 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 88 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 89 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 90 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 91 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 92 | github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= 93 | github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= 94 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 95 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 96 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 97 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 98 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 99 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 100 | github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= 101 | github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= 102 | github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= 103 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 104 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 105 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 106 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 107 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 108 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 109 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 110 | github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= 111 | github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= 112 | github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI= 113 | github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= 114 | github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= 115 | github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= 116 | github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= 117 | github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= 118 | github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= 119 | github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZWQ1bABv5xAg= 120 | github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= 121 | github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E= 122 | github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0= 123 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 124 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 125 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 126 | golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 127 | golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 128 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= 129 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 130 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 131 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 132 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 133 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 134 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 135 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 136 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 137 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 138 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 139 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 140 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 141 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 142 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 143 | golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 144 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 145 | golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8= 146 | golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 147 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 148 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 149 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 150 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 151 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 152 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 153 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 154 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 155 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 156 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 157 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 158 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 159 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 160 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 161 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 162 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 163 | golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 164 | golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 h1:z8Hj/bl9cOV2grsOpEaQFUaly0JWN3i97mo3jXKJNp0= 165 | golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 166 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 167 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 168 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 169 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 170 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 171 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 172 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 173 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 174 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 175 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 176 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 177 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 178 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 179 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 180 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 181 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 182 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 183 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 184 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 185 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 186 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= 187 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 188 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 189 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 190 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 191 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 192 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 193 | google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= 194 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 195 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 196 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 197 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 198 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 199 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 200 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 201 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 202 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 203 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 204 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 205 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 206 | google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= 207 | google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 208 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 209 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 210 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 211 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 212 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 213 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 214 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 215 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 216 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 217 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 218 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 219 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 220 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 221 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 222 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 223 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "github.com/fatih/color" 7 | "github.com/google/gopacket/pcap" 8 | "io/ioutil" 9 | "log" 10 | "os" 11 | ) 12 | 13 | type Config struct { 14 | DeviceName string `json:"deviceName"` 15 | PacketFilter []string `json:"packetFilter"` 16 | AutoSavePcapFiles bool `json:"autoSavePcapFiles"` 17 | } 18 | 19 | var config *Config 20 | var listDevice = flag.Bool("l", false, "List the network devices on this machine") 21 | var ip = flag.String("ip", "", "Designate the network devices to capture by IP") 22 | 23 | func main() { 24 | bytes, err := ioutil.ReadFile("./config.json") 25 | if err != nil { 26 | log.Fatalln("Could not load ./config.json", err) 27 | } 28 | err = json.Unmarshal(bytes, &config) 29 | if err != nil { 30 | log.Fatalln("Could not load ./config.json", err) 31 | } 32 | for packet := range config.PacketFilter { 33 | packetFilter[config.PacketFilter[packet]] = true 34 | } 35 | 36 | flag.Parse() 37 | 38 | if *listDevice { 39 | devices, err := pcap.FindAllDevs() 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | 44 | log.Println(color.RedString("Name"), "\tDescription\t", color.CyanString("IP address"), "\tSubnet mask") 45 | for _, device := range devices { 46 | log.Println(color.RedString(device.Name), "\t", device.Description, "\t") 47 | for _, address := range device.Addresses { 48 | log.Println("\t\t\t", color.CyanString(address.IP.String()), "\t", address.Netmask) 49 | } 50 | } 51 | os.Exit(0) 52 | } 53 | 54 | if len(*ip) > 0 { 55 | devices, err := pcap.FindAllDevs() 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | 60 | for _, device := range devices { 61 | for _, address := range device.Addresses { 62 | if address.IP.String() == *ip { 63 | log.Println("Device ", device.Name, " is chose") 64 | config.DeviceName = device.Name 65 | break 66 | } 67 | } 68 | } 69 | } 70 | 71 | go InitProto() 72 | startServer() 73 | } 74 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | BINARY=IridiumNG 2 | 3 | build-win: 4 | CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build -o build/${BINARY}_windows_amd64.exe 5 | build-darwin: 6 | CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -o build/${BINARY}_darwin_amd64 7 | build-linux: 8 | CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o build/${BINARY}_linux_amd64 9 | 10 | build-arm64: 11 | CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 go build -o build/${BINARY}_darwin_arm64 12 | CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -o build/${BINARY}_linux_arm64 13 | 14 | pre-build: 15 | mkdir build 16 | mkdir data 17 | cp ./config.json ./build/config.json 18 | -------------------------------------------------------------------------------- /mt19937_64.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | const N = 312 4 | const M = 156 5 | const MATRIX_A = 0xB5026F5AA96619E9 6 | const UPPER_MASK = 0xFFFFFFFF80000000 7 | const LOWER_MASK = 0x7FFFFFFF 8 | 9 | type MT19937_64 struct { 10 | array [312]uint64 //state vector 11 | index uint64 // array index 12 | } 13 | 14 | func New() *MT19937_64 { 15 | return &MT19937_64{ 16 | index: N + 1, 17 | } 18 | } 19 | 20 | func (m *MT19937_64) Seed(seed int64) { 21 | m.array[0] = uint64(seed) 22 | for m.index = 1; m.index < N; m.index++ { 23 | m.array[m.index] = (6364136223846793005*(m.array[m.index-1]^(m.array[m.index-1]>>62)) + m.index) 24 | } 25 | } 26 | 27 | func (m *MT19937_64) Generate() int64 { 28 | return m.Int63() 29 | } 30 | 31 | func (m *MT19937_64) Int63() int64 { 32 | var i int 33 | var x uint64 34 | mag01 := []uint64{0, MATRIX_A} 35 | if m.index >= N { 36 | /* Initialize with a default Seed if Seed() not previously called */ 37 | if m.index == N+1 { 38 | m.Seed(int64(5489)) 39 | } 40 | 41 | for i = 0; i < N-M; i++ { 42 | x = (m.array[i] & UPPER_MASK) | (m.array[i+1] & LOWER_MASK) 43 | m.array[i] = m.array[i+(M)] ^ (x >> 1) ^ mag01[int(x&uint64(1))] 44 | } 45 | for ; i < N-1; i++ { 46 | x = (m.array[i] & UPPER_MASK) | (m.array[i+1] & LOWER_MASK) 47 | m.array[i] = m.array[i+(M-N)] ^ (x >> 1) ^ mag01[int(x&uint64(1))] 48 | } 49 | x = (m.array[N-1] & UPPER_MASK) | (m.array[0] & LOWER_MASK) 50 | m.array[N-1] = m.array[M-1] ^ (x >> 1) ^ mag01[int(x&uint64(1))] 51 | m.index = 0 52 | 53 | } 54 | x = m.array[m.index] 55 | m.index++ 56 | x ^= (x >> 29) & 0x5555555555555555 57 | x ^= (x << 17) & 0x71D67FFFEDA60000 58 | x ^= (x << 37) & 0xFFF7EEE000000000 59 | x ^= (x >> 43) 60 | 61 | return int64(x) 62 | } 63 | -------------------------------------------------------------------------------- /proto_service.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "github.com/jhump/protoreflect/desc" 7 | "github.com/jhump/protoreflect/desc/protoparse" 8 | "github.com/jhump/protoreflect/dynamic" 9 | "io/ioutil" 10 | "log" 11 | ) 12 | 13 | var msgMap = make(map[string]*desc.MessageDescriptor) 14 | var packetIdMap map[uint16]string 15 | var packetNameMap = make(map[string]uint16) 16 | var protoParser = protoparse.Parser{} 17 | 18 | func InitProto() { 19 | packetIdFile, _ := ioutil.ReadFile("./data/packetIds.json") 20 | err := json.Unmarshal(packetIdFile, &packetIdMap) 21 | if err != nil { 22 | log.Fatalln("Could not load ./data/packetIds.json", err) 23 | } 24 | 25 | for k, v := range packetIdMap { 26 | packetNameMap[v] = k 27 | } 28 | 29 | protoParser.ImportPaths = []string{"./data/proto/"} 30 | for _, v := range packetIdMap { 31 | LoadProto(v) 32 | } 33 | 34 | } 35 | 36 | func LoadProto(protoName string) { 37 | fileDesc, err := protoParser.ParseFiles(protoName + ".proto") 38 | if err != nil { 39 | log.Println("Could not load proto file", protoName, err) 40 | return 41 | } 42 | 43 | msgMap[protoName] = fileDesc[0].FindMessage(protoName) 44 | } 45 | 46 | func GetProtoById(id uint16) *desc.MessageDescriptor { 47 | protoName, ok := packetIdMap[id] 48 | if !ok { 49 | return nil 50 | } 51 | return msgMap[protoName] 52 | } 53 | 54 | func GetProtoNameById(id uint16) string { 55 | protoName, ok := packetIdMap[id] 56 | if !ok { 57 | return "" 58 | } 59 | return protoName 60 | } 61 | 62 | func parseProto(id uint16, data []byte) (*dynamic.Message, error) { 63 | msg := GetProtoById(id) 64 | if msg == nil { 65 | return nil, errors.New("not found") 66 | } 67 | dMsg := dynamic.NewMessage(msg) 68 | 69 | err := dMsg.Unmarshal(data) 70 | return dMsg, err 71 | } 72 | 73 | func parseProtoToJson(id uint16, data []byte) string { 74 | dMsg, err := parseProto(id, data) 75 | if err != nil { 76 | return "" 77 | } 78 | 79 | marshalJSON, err := dMsg.MarshalJSON() 80 | if err != nil { 81 | return "" 82 | } 83 | 84 | return string(marshalJSON) 85 | } 86 | 87 | func parseProtoToInterface(id uint16, data []byte) *interface{} { 88 | object := parseProtoToJson(id, data) 89 | 90 | var result *interface{} 91 | err := json.Unmarshal([]byte(object), &result) 92 | if err != nil { 93 | return nil 94 | } 95 | 96 | return result 97 | } 98 | -------------------------------------------------------------------------------- /sniffer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/binary" 6 | "encoding/json" 7 | "github.com/fatih/color" 8 | "github.com/google/gopacket" 9 | "github.com/google/gopacket/layers" 10 | "github.com/google/gopacket/pcap" 11 | "github.com/google/gopacket/pcapgo" 12 | "github.com/jhump/protoreflect/dynamic" 13 | "github.com/xtaci/kcp-go" 14 | "io/ioutil" 15 | "log" 16 | "os" 17 | "strconv" 18 | "strings" 19 | "time" 20 | ) 21 | 22 | type Packet struct { 23 | Time int64 `json:"time"` 24 | FromServer bool `json:"fromServer"` 25 | PacketId uint16 `json:"packetId"` 26 | PacketName string `json:"packetName"` 27 | Object interface{} `json:"object"` 28 | Raw []byte `json:"raw"` 29 | } 30 | 31 | type UniCmdItem struct { 32 | PacketId uint16 `json:"packetId"` 33 | PacketName string `json:"packetName"` 34 | Object interface{} `json:"object"` 35 | Raw []byte `json:"raw"` 36 | } 37 | 38 | var getPlayerTokenRspPacketId uint16 39 | var unionCmdNotifyPacketId uint16 40 | 41 | var initialKey = make(map[uint16][]byte) 42 | var sessionKey []byte 43 | 44 | var captureHandler *pcap.Handle 45 | var kcpMap map[string]*kcp.KCP 46 | var packetFilter = make(map[string]bool) 47 | var pcapFile *os.File 48 | 49 | func openPcap(fileName string) { 50 | readKeys() 51 | var err error 52 | captureHandler, err = pcap.OpenOffline(fileName) 53 | if err != nil { 54 | log.Println("Could not open pacp file", err) 55 | return 56 | } 57 | startSniffer() 58 | } 59 | 60 | func openCapture() { 61 | readKeys() 62 | var err error 63 | captureHandler, err = pcap.OpenLive(config.DeviceName, 1500, true, -1) 64 | if err != nil { 65 | log.Println("Could not open capture", err) 66 | return 67 | } 68 | 69 | if config.AutoSavePcapFiles { 70 | pcapFile, err = os.Create(time.Now().Format("06-01-02 15.04.05") + ".pcapng") 71 | if err != nil { 72 | log.Println("Could not create pcapng file", err) 73 | } 74 | defer pcapFile.Close() 75 | } 76 | 77 | startSniffer() 78 | } 79 | 80 | func closeHandle() { 81 | if captureHandler != nil { 82 | captureHandler.Close() 83 | captureHandler = nil 84 | } 85 | if pcapFile != nil { 86 | pcapFile.Close() 87 | pcapFile = nil 88 | } 89 | } 90 | 91 | func readKeys() { 92 | var initialKeyJson map[uint16]string 93 | file, err := ioutil.ReadFile("./data/Keys.json") 94 | if err != nil { 95 | log.Fatal("Could not load initial key @ ./data/Keys.json #1", err) 96 | } 97 | err = json.Unmarshal(file, &initialKeyJson) 98 | if err != nil { 99 | log.Fatal("Could not load initial key @ ./data/Keys.json #2", err) 100 | } 101 | 102 | for k, v := range initialKeyJson { 103 | decode, _ := base64.RawStdEncoding.DecodeString(v) 104 | initialKey[k] = decode 105 | } 106 | 107 | getPlayerTokenRspPacketId = packetNameMap["GetPlayerTokenRsp"] 108 | unionCmdNotifyPacketId = packetNameMap["UnionCmdNotify"] 109 | } 110 | 111 | func startSniffer() { 112 | defer captureHandler.Close() 113 | 114 | err := captureHandler.SetBPFFilter("udp portrange 22101-22102") 115 | if err != nil { 116 | log.Println("Could not set the filter of capture") 117 | return 118 | } 119 | 120 | packetSource := gopacket.NewPacketSource(captureHandler, captureHandler.LinkType()) 121 | packetSource.NoCopy = true 122 | 123 | kcpMap = make(map[string]*kcp.KCP) 124 | 125 | var pcapWriter *pcapgo.NgWriter 126 | if pcapFile != nil { 127 | pcapWriter, err = pcapgo.NewNgWriter(pcapFile, captureHandler.LinkType()) 128 | if err != nil { 129 | log.Println("Could not create pcapng writer", err) 130 | } 131 | } 132 | 133 | for packet := range packetSource.Packets() { 134 | if pcapWriter != nil { 135 | err := pcapWriter.WritePacket(packet.Metadata().CaptureInfo, packet.Data()) 136 | if err != nil { 137 | log.Println("Could not write packet to pcap file", err) 138 | } 139 | } 140 | 141 | capTime := packet.Metadata().Timestamp 142 | data := packet.ApplicationLayer().Payload() 143 | udp := packet.TransportLayer().(*layers.UDP) 144 | fromServer := udp.SrcPort == 22101 || udp.SrcPort == 22102 145 | 146 | if len(data) <= 20 { 147 | handleSpecialPacket(data, fromServer, capTime) 148 | continue 149 | } 150 | 151 | handleKcp(data, fromServer, capTime) 152 | } 153 | } 154 | 155 | func handleKcp(data []byte, fromServer bool, capTime time.Time) { 156 | data = reformData(data) 157 | conv := binary.LittleEndian.Uint32(data[:4]) 158 | key := strconv.Itoa(int(conv)) 159 | if fromServer { 160 | key += "svr" 161 | } else { 162 | key += "cli" 163 | } 164 | 165 | if _, ok := kcpMap[key]; !ok { 166 | kcpInstance := kcp.NewKCP(conv, func(buf []byte, size int) {}) 167 | kcpInstance.WndSize(1024, 1024) 168 | kcpMap[key] = kcpInstance 169 | } 170 | kcpInstance := kcpMap[key] 171 | _ = kcpInstance.Input(data, true, true) 172 | 173 | size := kcpInstance.PeekSize() 174 | for size > 0 { 175 | kcpBytes := make([]byte, size) 176 | kcpInstance.Recv(kcpBytes) 177 | handleProtoPacket(kcpBytes, fromServer, capTime) 178 | size = kcpInstance.PeekSize() 179 | } 180 | kcpInstance.Update() 181 | } 182 | 183 | func handleSpecialPacket(data []byte, fromServer bool, timestamp time.Time) { 184 | sessionKey = nil 185 | switch binary.BigEndian.Uint32(data[:4]) { 186 | case 0xFF: 187 | buildPacketToSend(data, fromServer, timestamp, 0, "Hamdshanke pls.") 188 | break 189 | case 404: 190 | buildPacketToSend(data, fromServer, timestamp, 0, "Disconnected.") 191 | break 192 | default: 193 | buildPacketToSend(data, fromServer, timestamp, 0, "Hamdshanke estamblished.") 194 | break 195 | } 196 | } 197 | 198 | func handleProtoPacket(data []byte, fromServer bool, timestamp time.Time) { 199 | key := binary.BigEndian.Uint16(data[:4]) 200 | key = key ^ 0x4567 201 | var xorPad []byte 202 | 203 | if sessionKey != nil { 204 | xorPad = sessionKey 205 | } else { 206 | if len(initialKey[key]) == 0 { 207 | log.Println("Could not found initial key to decrypt", key) 208 | closeHandle() 209 | } 210 | xorPad = initialKey[key] 211 | } 212 | xorDecrypt(data, xorPad) 213 | 214 | packetId := binary.BigEndian.Uint16(data[2:4]) 215 | var objectJson interface{} 216 | 217 | if packetId == getPlayerTokenRspPacketId { 218 | data, objectJson = handleGetPlayerTokenRspPacket(data, packetId, objectJson) 219 | } else if packetId == unionCmdNotifyPacketId { 220 | data, objectJson = handleUnionCmdNotifyPacket(data, packetId, objectJson) 221 | } else { 222 | data = removeHeaderForParse(data) 223 | objectJson = parseProtoToInterface(packetId, data) 224 | } 225 | 226 | buildPacketToSend(data, fromServer, timestamp, packetId, objectJson) 227 | } 228 | 229 | func handleGetPlayerTokenRspPacket(data []byte, packetId uint16, objectJson interface{}) ([]byte, interface{}) { 230 | data = removeMagic(data) 231 | dMsg, err := parseProto(packetId, data) 232 | if err != nil { 233 | log.Println("Could not parse GetPlayerTokenRspPacket proto", err) 234 | closeHandle() 235 | } 236 | oj, err := dMsg.MarshalJSON() 237 | if err != nil { 238 | log.Println("Could not parse GetPlayerTokenRspPacket proto", err) 239 | closeHandle() 240 | } 241 | err = json.Unmarshal(oj, &objectJson) 242 | if err != nil { 243 | log.Println("Could not parse GetPlayerTokenRspPacket proto", err) 244 | closeHandle() 245 | } 246 | seed := dMsg.GetFieldByName("secret_key_seed").(uint64) 247 | sessionKey = createXorPad(seed) 248 | 249 | return data, objectJson 250 | } 251 | 252 | func handleUnionCmdNotifyPacket(data []byte, packetId uint16, objectJson interface{}) ([]byte, interface{}) { 253 | data = removeHeaderForParse(data) 254 | dMsg, err := parseProto(packetId, data) 255 | if err != nil { 256 | log.Println("Could not parse UnionCmdNotify proto", err) 257 | } 258 | 259 | cmdList := dMsg.GetFieldByName("cmd_list").([]interface{}) 260 | cmdListJson := make([]*UniCmdItem, len(cmdList)) 261 | for i, item := range cmdList { 262 | msgItem := item.(*dynamic.Message) 263 | itemPacketId := uint16(msgItem.GetFieldByName("message_id").(uint32)) 264 | itemData := msgItem.GetFieldByName("body").([]byte) 265 | 266 | childJson := parseProtoToInterface(itemPacketId, itemData) 267 | 268 | cmdListJson[i] = &UniCmdItem{ 269 | PacketId: itemPacketId, 270 | PacketName: GetProtoNameById(itemPacketId), 271 | Object: childJson, 272 | Raw: itemData, 273 | } 274 | } 275 | objectJson = cmdListJson 276 | return data, objectJson 277 | } 278 | 279 | func buildPacketToSend(data []byte, fromSever bool, timestamp time.Time, packetId uint16, objectJson interface{}) { 280 | packet := &Packet{ 281 | Time: timestamp.UnixMilli(), 282 | FromServer: fromSever, 283 | PacketId: packetId, 284 | PacketName: GetProtoNameById(packetId), 285 | Object: objectJson, 286 | Raw: data, 287 | } 288 | 289 | jsonResult, err := json.Marshal(packet) 290 | if err != nil { 291 | log.Println("Json marshal error", err) 292 | } 293 | logPacket(packet) 294 | 295 | if packetFilter[GetProtoNameById(packetId)] { 296 | return 297 | } 298 | sendStreamMsg(string(jsonResult)) 299 | } 300 | 301 | func logPacket(packet *Packet) { 302 | from := "[Client]" 303 | if packet.FromServer { 304 | from = "[Server]" 305 | } 306 | forward := "" 307 | if strings.Contains(packet.PacketName, "Rsp") { 308 | forward = "<--" 309 | } else if strings.Contains(packet.PacketName, "Req") { 310 | forward = "-->" 311 | } else if strings.Contains(packet.PacketName, "Notify") && packet.FromServer { 312 | forward = "<-i" 313 | } else if strings.Contains(packet.PacketName, "Notify") { 314 | forward = "i->" 315 | } 316 | 317 | log.Println(color.GreenString(from), 318 | "\t", 319 | color.CyanString(forward), 320 | "\t", 321 | color.RedString(packet.PacketName), 322 | color.YellowString("#"+strconv.Itoa(int(packet.PacketId))), 323 | "\t", 324 | len(packet.Raw), 325 | ) 326 | 327 | if packet.PacketId == unionCmdNotifyPacketId { 328 | logUnionCmdNotifyPacket(packet) 329 | } 330 | } 331 | 332 | func logUnionCmdNotifyPacket(packet *Packet) { 333 | uniCmdItem := packet.Object.([]*UniCmdItem) 334 | 335 | for i, item := range uniCmdItem { 336 | group := "├─" 337 | if i == len(uniCmdItem) { 338 | group = "└─" 339 | } 340 | 341 | log.Println("\t", 342 | "\t", 343 | color.CyanString(group), 344 | "\t", 345 | color.RedString(item.PacketName), 346 | color.YellowString("#"+strconv.Itoa(int(item.PacketId))), 347 | "\t", 348 | len(item.Raw), 349 | ) 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | ) 7 | 8 | func removeMagic(data []byte) []byte { 9 | cut := data[5] 10 | data = data[8+2:] // Removes token + two byte magic 11 | data = data[0 : len(data)-2] // Removes two byte magic at the end 12 | data = data[cut:] 13 | return data 14 | } 15 | 16 | func removeHeaderForParse(data []byte) []byte { 17 | cut := data[6] 18 | data = removeMagic(data) 19 | return data[cut:] 20 | } 21 | 22 | func xorDecrypt(data []byte, key []byte) { 23 | for i := 0; i < len(data); i++ { 24 | data[i] = data[i] ^ key[i%len(key)] 25 | } 26 | } 27 | 28 | func reformData(data []byte) []byte { 29 | i := 0 30 | tokenSizeTotal := 0 31 | var messages [][]byte 32 | for i < len(data) { 33 | convId := data[i : i+4] 34 | remainingHeader := data[i+8 : i+28] 35 | contentLen := int(binary.LittleEndian.Uint32(data[i+24 : i+28])) 36 | content := data[i+28 : (i + 28 + contentLen)] 37 | 38 | formattedMessage := make([]byte, 24+contentLen) 39 | copy(formattedMessage, convId) 40 | copy(formattedMessage[4:], remainingHeader) 41 | copy(formattedMessage[24:], content) 42 | i += 28 + contentLen 43 | tokenSizeTotal += 4 44 | messages = append(messages, formattedMessage) 45 | } 46 | 47 | return bytes.Join(messages, []byte{}) 48 | } 49 | 50 | func createXorPad(seed uint64) []byte { 51 | first := New() 52 | first.Seed(int64(seed)) 53 | generator := New() 54 | generator.Seed(first.Generate()) 55 | generator.Generate() 56 | xorPad := make([]byte, 4096) 57 | 58 | for i := 0; i < 4096; i += 8 { 59 | value := generator.Generate() 60 | binary.BigEndian.PutUint64(xorPad[i:i+8], uint64(value)) 61 | } 62 | return xorPad 63 | } 64 | --------------------------------------------------------------------------------