lensfrex 6 months ago
commit 7833c49c4a
Signed by: lensfrex
GPG Key ID: 0F69A0A2FBEE98A0
  1. 39
      .gitignore
  2. 3
      README.md
  3. 180
      app.go
  4. 86
      core/capture.go
  5. 50
      core/capture_test.go
  6. 34
      core/device.go
  7. 32
      core/device_test.go
  8. 79
      core/handler.go
  9. 61
      core/parser/arp.go
  10. 20
      core/parser/custom.go
  11. 49
      core/parser/ethernet.go
  12. 33
      core/parser/icmp.go
  13. 136
      core/parser/ipv4.go
  14. 20
      core/parser/ipv6.go
  15. 116
      core/parser/tcp.go
  16. 45
      core/parser/udp.go
  17. 28
      describer/arp.go
  18. 13
      describer/describer.go
  19. 33
      describer/ethernet.go
  20. 61
      describer/ipv4.go
  21. 69
      describer/tcp.go
  22. 23
      describer/udp.go
  23. 9
      frontend/.editorconfig
  24. 7
      frontend/.eslintignore
  25. 66
      frontend/.eslintrc.cjs
  26. 33
      frontend/.gitignore
  27. 5
      frontend/.npmrc
  28. 3
      frontend/README.md
  29. 21
      frontend/index.html
  30. 39
      frontend/jsconfig.json
  31. 10276
      frontend/package-lock.json
  32. 37
      frontend/package.json
  33. 1
      frontend/package.json.md5
  34. 27
      frontend/postcss.config.cjs
  35. 6
      frontend/postcss.config.js
  36. BIN
      frontend/public/favicon.ico
  37. BIN
      frontend/public/icons/favicon-128x128.png
  38. BIN
      frontend/public/icons/favicon-16x16.png
  39. BIN
      frontend/public/icons/favicon-32x32.png
  40. BIN
      frontend/public/icons/favicon-96x96.png
  41. 209
      frontend/quasar.config.js
  42. 11
      frontend/src/App.vue
  43. 15
      frontend/src/assets/quasar-logo-vertical.svg
  44. 0
      frontend/src/boot/.gitkeep
  45. 107
      frontend/src/components/PacketFlowView.vue
  46. 12
      frontend/src/css/app.scss
  47. 25
      frontend/src/css/quasar.variables.scss
  48. 323
      frontend/src/pages/IndexPage.vue
  49. 30
      frontend/src/router/index.js
  50. 8
      frontend/src/router/routes.js
  51. 11
      frontend/tailwind.config.js
  52. 14
      frontend/wailsjs/go/main/App.d.ts
  53. 23
      frontend/wailsjs/go/main/App.js
  54. 23
      frontend/wailsjs/go/models.ts
  55. 24
      frontend/wailsjs/runtime/package.json
  56. 235
      frontend/wailsjs/runtime/runtime.d.ts
  57. 202
      frontend/wailsjs/runtime/runtime.js
  58. 3284
      frontend/yarn.lock
  59. 41
      go.mod
  60. 110
      go.sum
  61. 37
      main.go
  62. 22
      types.go
  63. 13
      wails.json

39
.gitignore vendored

@ -0,0 +1,39 @@
build/bin
build
node_modules
frontend/dist
.idea
.DS_Store
.thumbs.db
node_modules
# Quasar core related directories
.quasar
/dist
/quasar.config.*.temporary.compiled*
# Cordova related directories and files
/src-cordova/node_modules
/src-cordova/platforms
/src-cordova/plugins
/src-cordova/www
# Capacitor related directories and files
/src-capacitor/www
/src-capacitor/node_modules
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
# local .env files
.env.local*

@ -0,0 +1,3 @@
# eruhs
nil

180
app.go

@ -0,0 +1,180 @@
package main
import (
"context"
"encoding/hex"
"eruhs/core"
"eruhs/core/parser"
"eruhs/describer"
"fmt"
"github.com/wailsapp/wails/v2/pkg/runtime"
"time"
)
// App struct
type App struct {
ctx context.Context
capture *core.Capture
}
// NewApp creates a new App application struct
func NewApp() *App {
return &App{}
}
// startup is called when the app starts. The context is saved
// so we can call the runtime methods
func (a *App) startup(ctx context.Context) {
a.ctx = ctx
}
func (a *App) GetNetworkDeviceList() ([]core.Device, error) {
devices, err := core.GetAllDevice()
if err != nil {
return nil, err
}
return devices, nil
}
var globalPackCnt = 0
var startTime = time.Now().UnixMilli()
var prevSendTime = time.Now().UnixMilli()
var batchSendBuffer []UIDataPackView
var packetMap = map[int]*core.DataPack{}
func (a *App) StartCapture(device string) {
if a.capture == nil {
a.capture = core.NewCapture(packCallback(a.ctx), errCallback(a.ctx))
}
globalPackCnt = 0
startTime = time.Now().UnixMilli()
prevSendTime = time.Now().UnixMilli()
batchSendBuffer = nil
batchSendBuffer = make([]UIDataPackView, 0, 1000)
runtime.LogPrint(a.ctx, "StartCapture")
a.capture.StartCaptureOnDevice(device)
}
func (a *App) PauseCapture() {
}
func (a *App) StopCapture() {
a.capture.Stop()
}
func (a *App) GetDetail(id int) map[int]LayerDescribe {
pack := packetMap[id]
if pack == nil {
return map[int]LayerDescribe{}
}
result := map[int]LayerDescribe{}
for _, layer := range pack.LayerStack {
switch layer.LayerType() {
case parser.LayerTypeEthernet:
eth := layer.(*parser.Ethernet)
result[0] = LayerDescribe{
HeaderRaw: hex.Dump(eth.Header),
LayerName: "ETH",
Describe: (&describer.EthernetDescriber{Eth: eth}).GetDetailDescribe(),
}
case parser.LayerTypeARP:
arp := layer.(*parser.ARP)
result[1] = LayerDescribe{
HeaderRaw: hex.Dump(arp.Header),
LayerName: "ARP",
Describe: (&describer.ARPDescriber{ARP: arp}).GetDetailDescribe(),
}
case parser.LayerTypeIPv4:
ipv4 := layer.(*parser.IPv4)
result[1] = LayerDescribe{
HeaderRaw: hex.Dump(ipv4.Header),
LayerName: "IPv4",
Describe: (&describer.IPv4Describer{IPv4: ipv4}).GetDetailDescribe(),
}
case parser.LayerTypeTCP:
tcp := layer.(*parser.TCP)
result[2] = LayerDescribe{
HeaderRaw: hex.Dump(tcp.Header),
ApplicationPayloadRaw: hex.Dump(tcp.Payload),
LayerName: "TCP",
Describe: (&describer.TCPDescriber{TCP: tcp}).GetDetailDescribe(),
}
case parser.LayerTypeUDP:
udp := layer.(*parser.UDP)
result[2] = LayerDescribe{
HeaderRaw: hex.Dump(udp.Header),
ApplicationPayloadRaw: hex.Dump(udp.Payload),
LayerName: "UDP",
Describe: (&describer.UDPDescriber{UDP: udp}).GetDetailDescribe(),
}
default:
}
}
return result
}
func errCallback(ctx context.Context) func(err error) {
return func(err error) {
fmt.Println(err)
runtime.EventsEmit(ctx, "onErrOccurrence", err.Error())
}
}
func packCallback(ctx context.Context) func(pack *core.DataPack) {
return func(pack *core.DataPack) {
globalPackCnt++
packetMap[globalPackCnt] = pack
dataPackView := UIDataPackView{
Id: globalPackCnt,
Time: time.Duration(time.Now().UnixMilli() - startTime),
Source: "",
Dest: "",
Proto: "",
Length: pack.TotalLength,
}
for _, layer := range pack.LayerStack {
switch layer.LayerType() {
case parser.LayerTypeEthernet:
eth := layer.(*parser.Ethernet)
dataPackView.Source = eth.Source.String()
dataPackView.Dest = eth.Destination.String()
dataPackView.Proto = "ETH"
case parser.LayerTypeARP:
dataPackView.Proto = "ARP"
case parser.LayerTypeIPv4:
ipv4 := layer.(*parser.IPv4)
dataPackView.Source = ipv4.SrcIP.String()
dataPackView.Dest = ipv4.DstIP.String()
dataPackView.Proto = "IPv4"
case parser.LayerTypeTCP:
tcp := layer.(*parser.TCP)
dataPackView.Source = fmt.Sprintf("%s:%d", dataPackView.Source, tcp.SrcPort)
dataPackView.Dest = fmt.Sprintf("%s:%d", dataPackView.Dest, tcp.DstPort)
dataPackView.Proto = "TCP"
case parser.LayerTypeUDP:
udp := layer.(*parser.UDP)
dataPackView.Source = fmt.Sprintf("%s:%d", dataPackView.Source, udp.SrcPort)
dataPackView.Dest = fmt.Sprintf("%s:%d", dataPackView.Dest, udp.DstPort)
dataPackView.Proto = "UDP"
default:
}
}
if (time.Now().UnixMilli() - prevSendTime) > int64(0.2*1000) {
runtime.EventsEmit(ctx, "onNewDataPacketBatch", batchSendBuffer)
batchSendBuffer = nil
batchSendBuffer = make([]UIDataPackView, 0, 1000)
prevSendTime = time.Now().UnixMilli()
} else {
batchSendBuffer = append(batchSendBuffer, dataPackView)
}
}
}

@ -0,0 +1,86 @@
package core
import (
"context"
"eruhs/core/parser"
"github.com/dustin/go-humanize"
"github.com/google/gopacket"
"github.com/google/gopacket/pcap"
"time"
)
type DataPack struct {
TotalLength int
LayerStack []parser.Layer
}
func (d *DataPack) AddLayer(layer parser.Layer) {
d.LayerStack = append(d.LayerStack, layer)
}
type Capture struct {
handle *pcap.Handle
started bool
stopFunc func()
callback func(pack *DataPack)
errCallback func(err error)
}
func NewCapture(callback func(pack *DataPack), errCallback func(err error)) *Capture {
return &Capture{
callback: callback,
errCallback: errCallback,
started: false,
}
}
// StartCaptureOnDevice 传入设备ID,启动抓包
func (c *Capture) StartCaptureOnDevice(device string) {
if c.started {
return
} else {
c.started = true
}
handle, err := pcap.OpenLive(device, 4*humanize.KByte, false, 1*time.Second)
if err != nil {
c.errCallback(err)
return
}
c.handle = handle
ctx, cancel := context.WithCancel(context.Background())
c.stopFunc = cancel
// 开启一个goroutine协程,实时读取数据包,并在抓取到数据包后调用callback,继续下一步的解析操作
go func(ctx context.Context) {
defer c.handle.Close()
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {
select {
case <-ctx.Done():
return
default:
parsedPack, err := c.handlePacket(packet)
if err != nil {
if parsedPack != nil {
c.callback(parsedPack)
} else {
c.errCallback(err)
}
}
c.callback(parsedPack)
}
}
}(ctx)
}
func (c *Capture) Pause() {
}
func (c *Capture) Stop() {
c.stopFunc()
c.started = false
}

@ -0,0 +1,50 @@
package core
import (
"encoding/json"
"eruhs/core/parser"
"fmt"
_ "net/http/pprof"
"testing"
"time"
)
func TestCapture_StartCaptureOnDevice(t *testing.T) {
cnt := 0
capture := NewCapture(func(pack *DataPack) {
cnt++
for _, layer := range pack.LayerStack {
switch layer.(type) {
case *parser.Ethernet:
fmt.Printf("%+v", layer.(*parser.Ethernet))
case *parser.IPv4:
fmt.Printf("%+v", layer.(*parser.IPv4))
case *parser.ARP:
fmt.Printf("%+v", layer.(*parser.ARP))
case *parser.TCP:
fmt.Printf("%+v", layer.(*parser.TCP))
case *parser.UDP:
fmt.Printf("%+v", layer.(*parser.UDP))
default:
j, err := json.MarshalIndent(layer, "", " ")
if err != nil {
fmt.Println(err)
}
fmt.Println(j)
}
}
fmt.Println("----------------")
//if cnt == 16 {
// t.SkipNow()
//}
}, func(err error) {
t.Error(err)
})
go func() {
time.Sleep(3 * time.Second)
capture.Stop()
}()
capture.StartCaptureOnDevice("\\Device\\NPF_{AA5A8BD6-43B3-489B-9858-0ECB96EAB05E}")
}

@ -0,0 +1,34 @@
package core
import "github.com/google/gopacket/pcap"
type Device struct {
Name string `json:"Name"`
Description string `json:"Description"`
Flags uint32 `json:"Flags"`
Addresses []string `json:"Addresses"`
}
func GetAllDevice() ([]Device, error) {
netInterfaces, err := pcap.FindAllDevs()
if err != nil {
return nil, err
}
devices := make([]Device, 0, len(netInterfaces))
for _, netInterface := range netInterfaces {
addr := make([]string, 0, len(netInterface.Addresses))
for _, address := range netInterface.Addresses {
addr = append(addr, address.IP.String())
}
devices = append(devices, Device{
Name: netInterface.Name,
Description: netInterface.Description,
Flags: netInterface.Flags,
Addresses: addr,
})
}
return devices, nil
}

@ -0,0 +1,32 @@
package core
import (
"encoding/json"
"fmt"
"testing"
)
func TestGetDevice(t *testing.T) {
devices, err := GetAllDevice()
if err != nil {
t.Fatal(err)
}
for _, device := range devices {
println("--------")
j, _ := json.MarshalIndent(device, "", " ")
//fmt.Printf("%+v\n", device)
println(string(j))
}
}
func TestName(t *testing.T) {
a := []int{1, 2, 3, 4, 5}
fmt.Printf("%+v\n", a)
as(a)
fmt.Printf("%+v\n", a)
}
func as(a []int) {
a[1] = 500
}

@ -0,0 +1,79 @@
package core
import (
"eruhs/core/parser"
"github.com/google/gopacket"
)
func (c *Capture) handlePacket(packet gopacket.Packet) (*DataPack, error) {
parsedPack := DataPack{}
data := packet.Data()
parsedPack.TotalLength = len(data)
eth, err := parser.DecodeEthernet(data)
if err != nil {
return nil, err
}
parsedPack.AddLayer(eth)
switch eth.Type {
case parser.EthernetTypeARP:
return c.handleARP(eth.Payload, parsedPack)
case parser.EthernetTypeIPv4:
return c.handleIPv4(eth.Payload, parsedPack)
//case custom.EthernetTypeIPv6:
default:
}
return &parsedPack, nil
}
func (c *Capture) handleARP(data []byte, parsedPack DataPack) (*DataPack, error) {
arp, err := parser.DecodeARP(data)
if err != nil {
return &parsedPack, err
}
parsedPack.AddLayer(arp)
return &parsedPack, nil
}
func (c *Capture) handleIPv4(data []byte, parsedPack DataPack) (*DataPack, error) {
ipv4, err := parser.DecodeIPV4(data)
if err != nil {
return &parsedPack, err
}
parsedPack.AddLayer(ipv4)
switch ipv4.Protocol {
case parser.IPv4ProtoTCP:
return c.handleTCP(ipv4.Payload, parsedPack)
case parser.IPv4ProtoUDP:
return c.handleUDP(ipv4.Payload, parsedPack)
default:
// unsupported protocol
}
return &parsedPack, nil
}
func (c *Capture) handleTCP(data []byte, parsedPack DataPack) (*DataPack, error) {
tcp, err := parser.DecodeTCP(data)
if err != nil {
return &parsedPack, err
}
parsedPack.AddLayer(tcp)
return &parsedPack, nil
}
func (c *Capture) handleUDP(data []byte, parsedPack DataPack) (*DataPack, error) {
udp, err := parser.DecodeUDP(data)
if err != nil {
return &parsedPack, err
}
parsedPack.AddLayer(udp)
return &parsedPack, nil
}

@ -0,0 +1,61 @@
package parser
import (
"encoding/binary"
"fmt"
"net"
)
// ARP ref: https://zh.wikipedia.org/zh-cn/%E5%9C%B0%E5%9D%80%E8%A7%A3%E6%9E%90%E5%8D%8F%E8%AE%AE#%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F
type ARP struct {
BaseLayer
HardwareType uint16 // 硬件类型
ProtoType uint16 // 协议类型
HwAddressSize uint8 // 硬件地址长度
ProtoAddrSize uint8 // 协议地址长度
Opcode uint16 // 操作码
SrcHwAddress net.HardwareAddr // 源硬件地址
SrcProtoAddress net.IP // 源协议地址
DstHwAddress net.HardwareAddr // 目标硬件地址
DstProtoAddress net.IP // 目标协议地址
}
func (A *ARP) LayerType() int {
return LayerTypeARP
}
// DecodeARP ref: https://zh.wikipedia.org/zh-cn/%E5%9C%B0%E5%9D%80%E8%A7%A3%E6%9E%90%E5%8D%8F%E8%AE%AE#%E6%8A%A5%E6%96%87%E6%A0%BC%E5%BC%8F
func DecodeARP(data []byte) (*ARP, error) {
// 至少要有8bytes
if len(data) < 8 {
return nil, fmt.Errorf("[arp] 数据包太短: %d bytes", len(data))
}
arp := ARP{}
arp.HardwareType = binary.BigEndian.Uint16(data[0:2])
arp.ProtoType = binary.BigEndian.Uint16(data[2:4])
arp.HwAddressSize = data[4]
arp.ProtoAddrSize = data[5]
arp.Opcode = binary.BigEndian.Uint16(data[6:8])
arpLen := 8 + 2*arp.HwAddressSize + 2*arp.ProtoAddrSize
if len(data) < int(arpLen) {
return nil, fmt.Errorf("[arp] 实际长度小于声明长度:实际:%d,声明:%d", len(data), arpLen)
}
var offset uint8 = 8
arp.SrcHwAddress = data[offset : offset+arp.HwAddressSize]
offset += arp.HwAddressSize
arp.SrcProtoAddress = data[offset : offset+arp.ProtoAddrSize]
offset += arp.ProtoAddrSize
arp.DstHwAddress = data[offset : offset+arp.HwAddressSize]
offset += arp.HwAddressSize
arp.DstProtoAddress = data[offset : offset+arp.ProtoAddrSize]
arp.Header = data[:arpLen]
arp.Payload = data[arpLen:]
return &arp, nil
}

@ -0,0 +1,20 @@
package parser
const (
LayerTypeEthernet = iota
LayerTypeARP
LayerTypeIPv4
LayerTypeIPv6
LayerTypeICMP
LayerTypeTCP
LayerTypeUDP
)
type Layer interface {
LayerType() int
}
type BaseLayer struct {
Header []byte
Payload []byte
}

@ -0,0 +1,49 @@
package parser
import (
"encoding/binary"
"errors"
"net"
)
// 取值参考自https://en.wikipedia.org/wiki/EtherType,只取常用的ipv4/6和arp类型
const (
EthernetTypeIPv4 = 0x0800
EthernetTypeARP = 0x0806
EthernetTypeIPv6 = 0x86DD
)
type Ethernet struct {
BaseLayer
Source net.HardwareAddr // 源mac
Destination net.HardwareAddr // 目的mac
Type uint16 // 网络层协议类型
}
func (e *Ethernet) LayerType() int {
return LayerTypeEthernet
}
func (e *Ethernet) GetSourceMac() string {
return e.Source.String()
}
func (e *Ethernet) GetDestinationMac() string {
return e.Destination.String()
}
func DecodeEthernet(data []byte) (*Ethernet, error) {
// 帧数据大小小于14,不符合以太网帧格式要求
if len(data) < 14 {
return nil, errors.New("[ethernet] 数据包太小,不符合以太网帧格式要求")
}
eth := &Ethernet{}
eth.Destination = data[:6] // 0~5 byte: 目标mac
eth.Source = data[6:12] // 6~11 byte: 源mac
eth.Type = binary.BigEndian.Uint16(data[12:14]) // 12~13 byte: 类型字段,大端序,uint16
eth.Header = data[:14]
eth.Payload = data[14:]
return eth, nil
}

@ -0,0 +1,33 @@
package parser
import (
"encoding/binary"
"errors"
)
type ICMP struct {
BaseLayer
TypeCode uint16
Checksum uint16
Id uint16
Seq uint16
}
func (I *ICMP) LayerType() int {
return LayerTypeICMP
}
func DecodeICMP(data []byte) (*ICMP, error) {
icmp := ICMP{}
if len(data) < 8 {
return nil, errors.New("ICMP数据包头部少于8字节")
}
icmp.TypeCode = binary.BigEndian.Uint16(data[0:2])
icmp.Checksum = binary.BigEndian.Uint16(data[2:4])
icmp.Id = binary.BigEndian.Uint16(data[4:6])
icmp.Seq = binary.BigEndian.Uint16(data[6:8])
icmp.BaseLayer = BaseLayer{data[:8], data[8:]}
return &icmp, nil
}

@ -0,0 +1,136 @@
package parser
import (
"encoding/binary"
"errors"
"fmt"
"net"
)
const (
IPv4ProtoICMP = 1 //互联网控制消息协议
IPv4ProtoIGMP = 2 //互联网组管理协议
IPv4ProtoTCP = 6 //传输控制协议
IPv4ProtoUDP = 17 //用户数据报协议
IPv4ProtoENCAP = 41 //IPv6封装
IPv4ProtoOSPF = 89 //开放式最短路径优先
IPv4ProtoSCTP = 132 //流控制传输协议
)
type IPv4 struct {
BaseLayer
Version uint8 // 版本
IHL uint8 // 首部长度
DS uint8 // 区分服务
TotalLength uint16 // 总长
Id uint16 // 标识符
Flags uint8 // 标志
FragOffset uint16 // 分片偏移
TTL uint8 // 存活时间
Protocol uint8 // 协议
Checksum uint16 // 首部校验和
SrcIP net.IP // 源地址
DstIP net.IP // 目的地址
Options []IPv4Option // 选项字段
Padding []byte // 填充
}
type IPv4Option struct {
OptionType uint8
OptionLength uint8
OptionData []byte
}
func (I *IPv4) LayerType() int {
return LayerTypeIPv4
}
// DecodeIPV4 ref: https://zh.wikipedia.org/wiki/IPv4#%E6%8A%A5%E6%96%87%E7%BB%93%E6%9E%84
func DecodeIPV4(data []byte) (*IPv4, error) {
// 至少要有20bytes
if len(data) < 20 {
return nil, fmt.Errorf("[ipv4] 无效的长度:%d, 至少需要20", len(data))
}
ipv4 := IPv4{}
ipv4.Version = data[0] >> 4
ipv4.IHL = uint8(data[0]) & 0x0F
ipv4.DS = data[1]
ipv4.TotalLength = binary.BigEndian.Uint16(data[2:4])
ipv4.Id = binary.BigEndian.Uint16(data[4:6])
flagsFrags := binary.BigEndian.Uint16(data[6:8])
ipv4.Flags = uint8(flagsFrags >> 13)
ipv4.FragOffset = flagsFrags & 0x1FFF
ipv4.TTL = data[8]
ipv4.Protocol = data[9]
ipv4.Checksum = binary.BigEndian.Uint16(data[10:12])
ipv4.SrcIP = data[12:16]
ipv4.DstIP = data[16:20]
if ipv4.TotalLength == 0 {
ipv4.TotalLength = uint16(len(data))
}
// 检查数据
if ipv4.TotalLength < 20 {
return nil, fmt.Errorf("[IPv4] 数据包长度太小 (%d < 20)", ipv4.TotalLength)
} else if ipv4.IHL < 5 {
return nil, fmt.Errorf("[IPv4] 标头长度太小 (%d < 5)", ipv4.IHL)
} else if int(ipv4.IHL*4) > int(ipv4.TotalLength) {
return nil, fmt.Errorf("[IPv4] 标头长度 > IP 总长度 (%d > %d)", ipv4.IHL, ipv4.TotalLength)
}
// 检查数据实际长度是否大于声明长度,大于(有padding)则除去padding拿到实际长度的payload
if cmp := len(data) - int(ipv4.TotalLength); cmp > 0 {
data = data[:ipv4.TotalLength]
} else if cmp < 0 {
// 如果声明的头部长度比整体数据还大,异常
if int(ipv4.IHL)*4 > len(data) {
return nil, errors.New("[ipv4] 数据包标头长度大于总数据包长度,标头长度无效或某些数据丢失")
}
}
ipv4.Header = data[:ipv4.IHL*4]
ipv4.Payload = data[ipv4.IHL*4:]
// 20bytes固定头部往后到IHL都是选项字段了
data = data[20 : ipv4.IHL*4]
// 解析可选字段,如果data[20 : ipv4.IHL*4]之间还有数据,说明选项字段有内容
for len(data) > 0 {
if ipv4.Options == nil {
// Pre-allocate to avoid growing the slice too much.
ipv4.Options = make([]IPv4Option, 0, 4)
}
opt := IPv4Option{OptionType: data[0]}
switch opt.OptionType {
case 0: // option字段结束,EOL标志
opt.OptionLength = 1
ipv4.Options = append(ipv4.Options, opt)
ipv4.Padding = data[1:]
goto FINISH
case 1: // 1byte填充
opt.OptionLength = 1
data = data[1:]
ipv4.Options = append(ipv4.Options, opt)
default:
// 字段未结束且不是1byte填充字段长度不足2byte
if len(data) < 2 {
return nil, fmt.Errorf("[IPv4] 选项长度 %d 小于2", len(data))
}
opt.OptionLength = data[1]
if len(data) < int(opt.OptionLength) {
return nil, fmt.Errorf("[IPv4] 可选字段长度超过剩余 IP 标头大小,选项类型:%v, 长度:%v", opt.OptionType, opt.OptionLength)
}
if opt.OptionLength <= 2 {
return nil, fmt.Errorf("[IPv4] 可选字段长度小于2,选项类型:%v 长度:%d", opt.OptionType, opt.OptionLength)
}
opt.OptionData = data[2:opt.OptionLength]
data = data[opt.OptionLength:]
ipv4.Options = append(ipv4.Options, opt)
}
}
FINISH:
return &ipv4, nil
}

@ -0,0 +1,20 @@
package parser
import (
"github.com/google/gopacket/layers"
"net"
)
type IPv6 struct {
layers.BaseLayer
Version uint8
TrafficClass uint8
FlowLabel uint32
Length uint16
NextHeader uint8
HopLimit uint8
SrcIP net.IP
DstIP net.IP
HopByHop *layers.IPv6HopByHop
hbh layers.IPv6HopByHop
}

@ -0,0 +1,116 @@
package parser
import (
"encoding/binary"
"errors"
"fmt"
)
const (
TCPOptionKindEndList = 0 // 选项表结束(1字节)
TCPOptionKindNop = 1 // 无操作(1字节)
TCPOptionKindMSS = 2 // 最大报文段长度(4字节,Maximum Segment Size,MSS)
TCPOptionKindWindowScale = 3 // 窗口扩大因子(3字节,wscale)
TCPOptionKindSACKOK = 4 // sackOK—发送端支持并同意使用SACK选项。
TCPOptionKindSACK = 5 // SACK实际工作的选项。
TCPOptionKindTimestamps = 8 // 时间戳(10字节,TCP Timestamps Option,TSopt)
)
// TCP ref: https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE#%E7%8A%B6%E6%80%81%E7%BC%96%E7%A0%81
type TCP struct {
BaseLayer
SrcPort, DstPort uint16 // 源、目的端口
Seq uint32 // 序列号
Ack uint32 // 确认号
DataOffset uint8 // 数据偏移量(起始)
NS, CWR, ECE, URG, ACK, PSH, RST, SYN, FIN bool // 标志符
Window uint16 // 窗口
Checksum uint16 // 校验和
Urgent uint16 // 紧急指针
Options []TCPOption // 可选字段
Padding []byte // 填充
}
type TCPOption struct {
OptionType uint8
OptionLength uint8
OptionData []byte
}
func (T *TCP) LayerType() int {
return LayerTypeTCP
}
// DecodeTCP ref: https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE#%E7%8A%B6%E6%80%81%E7%BC%96%E7%A0%81
// 解码TCP包,参考自wiki
func DecodeTCP(data []byte) (*TCP, error) {
if len(data) < 20 {
return nil, fmt.Errorf("[TCP] 无效标头。长度 %d bytes,至少需要20 bytes", len(data))
}
tcp := TCP{}
tcp.SrcPort = binary.BigEndian.Uint16(data[0:2])
tcp.DstPort = binary.BigEndian.Uint16(data[2:4])
tcp.Seq = binary.BigEndian.Uint32(data[4:8])
tcp.Ack = binary.BigEndian.Uint32(data[8:12])
tcp.DataOffset = data[12] >> 4
{ // 位运算取相得应的标志位
tcp.NS = data[12]&0x01 != 0
tcp.CWR = data[13]&0x80 != 0
tcp.ECE = data[13]&0x40 != 0
tcp.URG = data[13]&0x20 != 0
tcp.ACK = data[13]&0x10 != 0
tcp.PSH = data[13]&0x08 != 0
tcp.RST = data[13]&0x04 != 0
tcp.SYN = data[13]&0x02 != 0
tcp.FIN = data[13]&0x01 != 0
}
tcp.Window = binary.BigEndian.Uint16(data[14:16])
tcp.Checksum = binary.BigEndian.Uint16(data[16:18])
tcp.Urgent = binary.BigEndian.Uint16(data[18:20])
dataStart := int(tcp.DataOffset) * 4
if dataStart > len(data) {
return nil, errors.New("[TCP] 数据偏移大于数据包长度")
}
tcp.Header = data[:dataStart]
tcp.Payload = data[dataStart:]
// 20bytes固定头部往后到dataStart都是选项字段了
data = data[20:dataStart]
// 解析可选字段
for len(data) > 0 {
tcp.Options = append(tcp.Options, TCPOption{OptionType: data[0]})
opt := &tcp.Options[len(tcp.Options)-1]
switch opt.OptionType {
case TCPOptionKindEndList: // 选项表结束
opt.OptionLength = 1
opt.OptionData = []byte{0}
tcp.Padding = data[1:]
goto FINISH
case TCPOptionKindNop: // 无操作(1字节),用于选项字段之间的字边界对齐。
opt.OptionLength = 1
opt.OptionData = []byte{0}
data = data[opt.OptionLength:]
default:
if len(data) < 2 {
return nil, fmt.Errorf("[TCP] 可选字段长度无效")
}
opt.OptionLength = data[1]
if opt.OptionLength < 2 {
return nil, fmt.Errorf("[TCP] 可选字段长度无效,%d < 2", opt.OptionLength)
} else if int(opt.OptionLength) > len(data) {
return nil, fmt.Errorf("[TCP] 无效可选字段长度: %d, 超出剩余的 %d 个字节", opt.OptionLength, len(data))
}
opt.OptionData = data[2:opt.OptionLength]
data = data[opt.OptionLength:]
}
}
FINISH:
return &tcp, nil
}

@ -0,0 +1,45 @@
package parser
import (
"encoding/binary"
"fmt"
)
type UDP struct {
BaseLayer
SrcPort, DstPort uint16 // 源端口(可选)、目标端口
Length uint16 // 数据包总长(header+payload)
Checksum uint16 // 校验和(ipv4下可选)
}
func (U *UDP) LayerType() int {
return LayerTypeUDP
}
func DecodeUDP(data []byte) (*UDP, error) {
if len(data) < 8 {
return nil, fmt.Errorf("[UDP] 无效数据包长度: %d bytes, 至少需要8 bytes", len(data))
}
udp := UDP{}
udp.SrcPort = binary.BigEndian.Uint16(data[0:2])
udp.DstPort = binary.BigEndian.Uint16(data[2:4])
udp.Length = binary.BigEndian.Uint16(data[4:6])
udp.Checksum = binary.BigEndian.Uint16(data[6:8])
udp.Header = data[:8]
switch {
case udp.Length >= 8:
hlen := int(udp.Length)
if hlen > len(data) {
hlen = len(data)
}
udp.Payload = data[8:hlen]
case udp.Length == 0: // IPv6 Jumbogram, 剩下的所有数据都认为是payload
udp.Payload = data[8:]
default:
return nil, fmt.Errorf("[UDP] 数据包太小:%d bytes", udp.Length)
}
return &udp, nil
}

@ -0,0 +1,28 @@
package describer
import (
"eruhs/core/parser"
"fmt"
)
type ARPDescriber struct {
ARP *parser.ARP
}
func (a *ARPDescriber) GetHeader() []byte {
return a.ARP.Header
}
func (a *ARPDescriber) GetDetailDescribe() Describe {
return Describe{
1: {"硬件类型", fmt.Sprintf("%d", a.ARP.HardwareType)},
2: {"协议类型", fmt.Sprintf("%d", a.ARP.ProtoType)},
3: {"硬件地址长度", fmt.Sprintf("%d", a.ARP.HwAddressSize)},
4: {"协议地址长度", fmt.Sprintf("%d", a.ARP.ProtoAddrSize)},
5: {"操作码", fmt.Sprintf("%d", a.ARP.Opcode)},
6: {"源硬件地址", a.ARP.SrcHwAddress.String()},
7: {"源协议地址", a.ARP.SrcProtoAddress.String()},
8: {"目标硬件地址", a.ARP.DstHwAddress.String()},
9: {"目标协议地址", a.ARP.DstProtoAddress.String()},
}
}

@ -0,0 +1,13 @@
package describer
type DescribeItem struct {
Name string
Describe string
}
type Describe map[int]DescribeItem
type LayerDescriber interface {
GetHeader() []byte
GetDetailDescribe() Describe
}

@ -0,0 +1,33 @@
package describer
import (
"eruhs/core/parser"
"strings"
)
type EthernetDescriber struct {
Eth *parser.Ethernet
}
func (e *EthernetDescriber) GetHeader() []byte {
return e.Eth.Header
}
func (e *EthernetDescriber) GetDetailDescribe() Describe {
protoName := "ETH"
switch e.Eth.Type {
case parser.EthernetTypeIPv4:
protoName = "IPv4"
case parser.EthernetTypeIPv6:
protoName = "IPv6"
case parser.LayerTypeARP:
protoName = "ARP"
default:
}
return Describe{
1: {"源地址", strings.ToUpper(e.Eth.Source.String())},
2: {"目的地址", strings.ToUpper(e.Eth.Destination.String())},
3: {"网络层协议类型", protoName},
}
}

@ -0,0 +1,61 @@
package describer
import (
"encoding/hex"
"eruhs/core/parser"
"fmt"
)
type IPv4Describer struct {
IPv4 *parser.IPv4
}
func (i *IPv4Describer) GetHeader() []byte {
return i.IPv4.Header
}
func (i *IPv4Describer) GetDetailDescribe() Describe {
optionDescribe := make([]map[string]string, 0)
for _, option := range i.IPv4.Options {
optionDescribe = append(optionDescribe, map[string]string{
"类型": fmt.Sprintf("%d", option.OptionType),
"长度": fmt.Sprintf("%d", option.OptionLength),
"数据": hex.EncodeToString(option.OptionData),
})
}
protoName := ""
switch i.IPv4.Protocol {
case parser.IPv4ProtoICMP:
protoName = "ICMP"
case parser.IPv4ProtoIGMP:
protoName = "IGMP"
case parser.IPv4ProtoTCP:
protoName = "TCP"
case parser.IPv4ProtoUDP:
protoName = "UDP"
case parser.IPv4ProtoENCAP:
protoName = "ENCAP"
case parser.IPv4ProtoOSPF:
protoName = "OSPF"
case parser.IPv4ProtoSCTP:
protoName = "SCTP"
}
return Describe{
1: {"版本", fmt.Sprintf("%d", i.IPv4.Version)},
2: {"首部长度", fmt.Sprintf("%d", i.IPv4.IHL)},
3: {"区分服务", fmt.Sprintf("%d", i.IPv4.DS)},
4: {"总长", fmt.Sprintf("%d", i.IPv4.TotalLength)},
5: {"标识符", fmt.Sprintf("%d", i.IPv4.Id)},
6: {"标志", fmt.Sprintf("%d", i.IPv4.Flags)},
7: {"分片偏移", fmt.Sprintf("%d", i.IPv4.FragOffset)},
8: {"存活时间", fmt.Sprintf("%d", i.IPv4.TTL)},
9: {"应用层协议", protoName},
10: {"首部校验和", fmt.Sprintf("%d", i.IPv4.Checksum)},
11: {"源地址", i.IPv4.SrcIP.String()},
12: {"目的地址", i.IPv4.DstIP.String()},
13: {"选项字段", fmt.Sprintf("%+v", optionDescribe)},
14: {"填充", fmt.Sprintf("%d Byte", len(i.IPv4.Padding))},
}
}

@ -0,0 +1,69 @@
package describer
import (
"encoding/hex"
"eruhs/core/parser"
"fmt"
)
type TCPDescriber struct {
TCP *parser.TCP
}
func (t *TCPDescriber) GetHeader() []byte {
return t.TCP.Header
}
func (t *TCPDescriber) GetDetailDescribe() Describe {
tcpFlags := make([]string, 0)
if t.TCP.NS {
tcpFlags = append(tcpFlags, "NS")
}
if t.TCP.CWR {
tcpFlags = append(tcpFlags, "CWR")
}
if t.TCP.ECE {
tcpFlags = append(tcpFlags, "ECE")
}
if t.TCP.URG {
tcpFlags = append(tcpFlags, "URG")
}
if t.TCP.ACK {
tcpFlags = append(tcpFlags, "ACK")
}
if t.TCP.PSH {
tcpFlags = append(tcpFlags, "PSH")
}
if t.TCP.RST {
tcpFlags = append(tcpFlags, "RST")
}
if t.TCP.SYN {
tcpFlags = append(tcpFlags, "SYN")
}
if t.TCP.FIN {
tcpFlags = append(tcpFlags, "FIN")
}
optionDescribe := make([]map[string]string, 0)
for _, option := range t.TCP.Options {
optionDescribe = append(optionDescribe, map[string]string{
"类型": fmt.Sprintf("%d", option.OptionType),
"长度": fmt.Sprintf("%d", option.OptionLength),
"数据": hex.EncodeToString(option.OptionData),
})
}
return Describe{
1: {"源端口号", fmt.Sprintf("%d", t.TCP.SrcPort)},
2: {"目的端口号", fmt.Sprintf("%d", t.TCP.DstPort)},
3: {"序列号", fmt.Sprintf("%d", t.TCP.Seq)},
4: {"确认号", fmt.Sprintf("%d", t.TCP.Ack)},
5: {"数据偏移", fmt.Sprintf("%d", t.TCP.DataOffset)},
6: {"标志符", fmt.Sprintf("%v", tcpFlags)},
7: {"窗口", fmt.Sprintf("%d", t.TCP.Window)},
8: {"校验和", fmt.Sprintf("%d", t.TCP.Checksum)},
9: {"紧急指针", fmt.Sprintf("%d", t.TCP.Urgent)},
10: {"可选字段", fmt.Sprintf("%+v", optionDescribe)},
11: {"填充", fmt.Sprintf("%d", len(t.TCP.Padding))},
}
}

@ -0,0 +1,23 @@
package describer
import (
"eruhs/core/parser"
"fmt"
)
type UDPDescriber struct {
UDP *parser.UDP
}
func (u *UDPDescriber) GetHeader() []byte {
return u.UDP.Header
}
func (u *UDPDescriber) GetDetailDescribe() Describe {
return Describe{
1: {"源端口(0为无)", fmt.Sprintf("%d", u.UDP.SrcPort)},
2: {"目标端口", fmt.Sprintf("%d", u.UDP.DstPort)},
3: {"数据包总长", fmt.Sprintf("%d", u.UDP.Length)},
4: {"校验和(0为无)", fmt.Sprintf("%d", u.UDP.Checksum)},
}
}

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

@ -0,0 +1,7 @@
/dist
/src-capacitor
/src-cordova
/.quasar
/node_modules
.eslintrc.cjs
/quasar.config.*.temporary.compiled*

@ -0,0 +1,66 @@
module.exports = {
// https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
// This option interrupts the configuration hierarchy at this file
// Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
root: true,
parserOptions: {
ecmaVersion: 2021, // Allows for the parsing of modern ECMAScript features
},
env: {
node: true,
browser: true
},
// Rules order is important, please avoid shuffling them
extends: [
// Base ESLint recommended rules
// 'eslint:recommended',
// Uncomment any of the lines below to choose desired strictness,
// but leave only one uncommented!
// See https://eslint.vuejs.org/rules/#available-rules
'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
// 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
// https://github.com/prettier/eslint-config-prettier#installation
// usage with Prettier, provided by 'eslint-config-prettier'.
'prettier'
],
plugins: [
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
// required to lint *.vue files
'vue',
// https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674
// Prettier has not been included as plugin to avoid performance impact
// add it as an extension for your IDE
],
globals: {
ga: 'readonly', // Google Analytics
cordova: 'readonly',
__statics: 'readonly',
__QUASAR_SSR__: 'readonly',
__QUASAR_SSR_SERVER__: 'readonly',
__QUASAR_SSR_CLIENT__: 'readonly',
__QUASAR_SSR_PWA__: 'readonly',
process: 'readonly',
Capacitor: 'readonly',
chrome: 'readonly'
},
// add your custom rules here
rules: {
'vue/no-unused-components': 'off',
'vue/no-unused-vars': 'off',
'prefer-promise-reject-errors': 'off',
// allow debugger during development only
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
}
}

@ -0,0 +1,33 @@
.DS_Store
.thumbs.db
node_modules
# Quasar core related directories
.quasar
/dist
/quasar.config.*.temporary.compiled*
# Cordova related directories and files
/src-cordova/node_modules
/src-cordova/platforms
/src-cordova/plugins
/src-cordova/www
# Capacitor related directories and files
/src-capacitor/www
/src-capacitor/node_modules
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
# local .env files
.env.local*

@ -0,0 +1,5 @@
# pnpm-related options
shamefully-hoist=true
strict-peer-dependencies=false
# to get the latest compatible packages when creating the project https://github.com/pnpm/pnpm/issues/6463
resolution-mode=highest

@ -0,0 +1,3 @@
# eruhs-ui
undefined

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title><%= productName %></title>
<meta charset="utf-8">
<meta name="description" content="<%= productDescription %>">
<meta name="format-detection" content="telephone=no">
<meta name="msapplication-tap-highlight" content="no">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>">
<link rel="icon" type="image/png" sizes="128x128" href="icons/favicon-128x128.png">
<link rel="icon" type="image/png" sizes="96x96" href="icons/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="32x32" href="icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="icons/favicon-16x16.png">
<link rel="icon" type="image/ico" href="favicon.ico">
</head>
<body>
<!-- quasar:entry-point -->
</body>
</html>

@ -0,0 +1,39 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"src/*": [
"src/*"
],
"app/*": [
"*"
],
"components/*": [
"src/components/*"
],
"layouts/*": [
"src/layouts/*"
],
"pages/*": [
"src/pages/*"
],
"assets/*": [
"src/assets/*"
],
"boot/*": [
"src/boot/*"
],
"stores/*": [
"src/stores/*"
],
"vue$": [
"node_modules/vue/dist/vue.runtime.esm-bundler.js"
]
}
},
"exclude": [
"dist",
".quasar",
"node_modules"
]
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,37 @@
{
"name": "eruhs-ui",
"version": "0.0.1",
"description": "eruhs ui",
"productName": "eruhs",
"author": "1@2.com",
"private": true,
"scripts": {
"lint": "eslint --ext .js,.vue ./",
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
"test": "echo \"No test specified\" && exit 0",
"dev": "quasar dev",
"build": "quasar build"
},
"dependencies": {
"@quasar/extras": "^1.16.4",
"quasar": "^2.8.0",
"vue": "^3.4.18",
"vue-router": "^4.0.12"
},
"devDependencies": {
"@quasar/app-vite": "^1.8.0",
"autoprefixer": "^10.4.18",
"eslint": "^8.11.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-vue": "^9.0.0",
"postcss": "^8.4.35",
"prettier": "^2.5.1",
"tailwindcss": "^3.4.1",
"vite-plugin-checker": "^0.6.4"
},
"engines": {
"node": "^20 || ^18 || ^16",
"npm": ">= 6.13.4",
"yarn": ">= 1.21.1"
}
}

@ -0,0 +1 @@
7d942fd325b75f919c368510ac080ec8

@ -0,0 +1,27 @@
/* eslint-disable */
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
plugins: [
// https://github.com/postcss/autoprefixer
require('autoprefixer')({
overrideBrowserslist: [
'last 4 Chrome versions',
'last 4 Firefox versions',
'last 4 Edge versions',
'last 4 Safari versions',
'last 4 Android versions',
'last 4 ChromeAndroid versions',
'last 4 FirefoxAndroid versions',
'last 4 iOS versions'
]
})
// https://github.com/elchininet/postcss-rtlcss
// If you want to support RTL css, then
// 1. yarn/npm install postcss-rtlcss
// 2. optionally set quasar.config.js > framework > lang to an RTL language
// 3. uncomment the following line:
// require('postcss-rtlcss')
]
}

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

@ -0,0 +1,209 @@
/* eslint-env node */
/*
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
* the ES6 features that are supported by your Node version. https://node.green/
*/
// Configuration for your app
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
const { configure } = require('quasar/wrappers');
module.exports = configure(function (/* ctx */) {
return {
// https://v2.quasar.dev/quasar-cli-vite/prefetch-feature
// preFetch: true,
// app boot file (/src/boot)
// --> boot files are part of "main.js"
// https://v2.quasar.dev/quasar-cli-vite/boot-files
boot: [
],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
css: [
'app.scss'
],
// https://github.com/quasarframework/quasar/tree/dev/extras
extras: [
// 'ionicons-v4',
// 'mdi-v7',
// 'fontawesome-v6',
// 'eva-icons',
// 'themify',
// 'line-awesome',
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
'roboto-font', // optional, you are not bound to it
'material-icons', // optional, you are not bound to it
],
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
build: {
target: {
browser: [ 'es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1' ],
node: 'node20'
},
vueRouterMode: 'hash', // available values: 'hash', 'history'
// vueRouterBase,
// vueDevtools,
// vueOptionsAPI: false,
// rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
// publicPath: '/',
// analyze: true,
// env: {},
// rawDefine: {}
// ignorePublicFolder: true,
// minify: false,
// polyfillModulePreload: true,
// distDir
// extendViteConf (viteConf) {},
// viteVuePluginOptions: {},
vitePlugins: [
['vite-plugin-checker', {
eslint: {
lintCommand: 'eslint "./**/*.{js,mjs,cjs,vue}"'
}
}, { server: false }]
]
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
devServer: {
// https: true
open: true, // opens browser window automatically
},
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
framework: {
config: {},
// iconSet: 'material-icons', // Quasar icon set
// lang: 'en-US', // Quasar language pack
// For special cases outside of where the auto-import strategy can have an impact
// (like functional components as one of the examples),
// you can manually specify Quasar components/directives to be available everywhere:
//
// components: [],
// directives: [],
// Quasar plugins
plugins: [
'Dialog'
]
},
// animations: 'all', // --- includes all animations
// https://v2.quasar.dev/options/animations
animations: [],
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#property-sourcefiles
// sourceFiles: {
// rootComponent: 'src/App.vue',
// router: 'src/router/index',
// store: 'src/store/index',
// registerServiceWorker: 'src-pwa/register-service-worker',
// serviceWorker: 'src-pwa/custom-service-worker',
// pwaManifestFile: 'src-pwa/manifest.json',
// electronMain: 'src-electron/electron-main',
// electronPreload: 'src-electron/electron-preload'
// },
// https://v2.quasar.dev/quasar-cli-vite/developing-ssr/configuring-ssr
ssr: {
// ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name!
// will mess up SSR
// extendSSRWebserverConf (esbuildConf) {},
// extendPackageJson (json) {},
pwa: false,
// manualStoreHydration: true,
// manualPostHydrationTrigger: true,
prodPort: 3000, // The default port that the production server should use
// (gets superseded if process.env.PORT is specified at runtime)
middlewares: [
'render' // keep this as last one
]
},
// https://v2.quasar.dev/quasar-cli-vite/developing-pwa/configuring-pwa
pwa: {
workboxMode: 'generateSW', // or 'injectManifest'
injectPwaMetaTags: true,
swFilename: 'sw.js',
manifestFilename: 'manifest.json',
useCredentialsForManifestTag: false,
// useFilenameHashes: true,
// extendGenerateSWOptions (cfg) {}
// extendInjectManifestOptions (cfg) {},
// extendManifestJson (json) {}
// extendPWACustomSWConf (esbuildConf) {}
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-cordova-apps/configuring-cordova
cordova: {
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-capacitor-apps/configuring-capacitor
capacitor: {
hideSplashscreen: true
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-electron-apps/configuring-electron
electron: {
// extendElectronMainConf (esbuildConf)
// extendElectronPreloadConf (esbuildConf)
// specify the debugging port to use for the Electron app when running in development mode
inspectPort: 5858,
bundler: 'packager', // 'packager' or 'builder'
packager: {
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
// OS X / Mac App Store
// appBundleId: '',
// appCategoryType: '',
// osxSign: '',
// protocol: 'myapp://path',
// Windows only
// win32metadata: { ... }
},
builder: {
// https://www.electron.build/configuration/configuration
appId: 'eruhs-ui'
}
},
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex
bex: {
contentScripts: [
'my-content-script'
],
// extendBexScriptsConf (esbuildConf) {}
// extendBexManifestJson (json) {}
}
}
});

@ -0,0 +1,11 @@
<template>
<router-view />
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'App'
})
</script>

@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 356 360">
<path
d="M43.4 303.4c0 3.8-2.3 6.3-7.1 6.3h-15v-22h14.4c4.3 0 6.2 2.2 6.2 5.2 0 2.6-1.5 4.4-3.4 5 2.8.4 4.9 2.5 4.9 5.5zm-8-13H24.1v6.9H35c2.1 0 4-1.3 4-3.8 0-2.2-1.3-3.1-3.7-3.1zm5.1 12.6c0-2.3-1.8-3.7-4-3.7H24.2v7.7h11.7c3.4 0 4.6-1.8 4.6-4zm36.3 4v2.7H56v-22h20.6v2.7H58.9v6.8h14.6v2.3H58.9v7.5h17.9zm23-5.8v8.5H97v-8.5l-11-13.4h3.4l8.9 11 8.8-11h3.4l-10.8 13.4zm19.1-1.8V298c0-7.9 5.2-10.7 12.7-10.7 7.5 0 13 2.8 13 10.7v1.4c0 7.9-5.5 10.8-13 10.8s-12.7-3-12.7-10.8zm22.7 0V298c0-5.7-3.9-8-10-8-6 0-9.8 2.3-9.8 8v1.4c0 5.8 3.8 8.1 9.8 8.1 6 0 10-2.3 10-8.1zm37.2-11.6v21.9h-2.9l-15.8-17.9v17.9h-2.8v-22h3l15.6 18v-18h2.9zm37.9 10.2v1.3c0 7.8-5.2 10.4-12.4 10.4H193v-22h11.2c7.2 0 12.4 2.8 12.4 10.3zm-3 0c0-5.3-3.3-7.6-9.4-7.6h-8.4V307h8.4c6 0 9.5-2 9.5-7.7V298zm50.8-7.6h-9.7v19.3h-3v-19.3h-9.7v-2.6h22.4v2.6zm34.4-2.6v21.9h-3v-10.1h-16.8v10h-2.8v-21.8h2.8v9.2H296v-9.2h2.9zm34.9 19.2v2.7h-20.7v-22h20.6v2.7H316v6.8h14.5v2.3H316v7.5h17.8zM24 340.2v7.3h13.9v2.4h-14v9.6H21v-22h20v2.7H24zm41.5 11.4h-9.8v7.9H53v-22h13.3c5.1 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6H66c3.1 0 5.3-1.5 5.3-4.7 0-3.3-2.2-4.1-5.3-4.1H55.7v8.8zm47.9 6.2H89l-2 4.3h-3.2l10.7-22.2H98l10.7 22.2h-3.2l-2-4.3zm-1-2.3l-6.3-13-6 13h12.2zm46.3-15.3v21.9H146v-17.2L135.7 358h-2.1l-10.2-15.6v17h-2.8v-21.8h3l11 16.9 11.3-17h3zm35 19.3v2.6h-20.7v-22h20.6v2.7H166v6.8h14.5v2.3H166v7.6h17.8zm47-19.3l-8.3 22h-3l-7.1-18.6-7 18.6h-3l-8.2-22h3.3L204 356l6.8-18.5h3.4L221 356l6.6-18.5h3.3zm10 11.6v-1.4c0-7.8 5.2-10.7 12.7-10.7 7.6 0 13 2.9 13 10.7v1.4c0 7.9-5.4 10.8-13 10.8-7.5 0-12.7-3-12.7-10.8zm22.8 0v-1.4c0-5.7-4-8-10-8s-9.9 2.3-9.9 8v1.4c0 5.8 3.8 8.2 9.8 8.2 6.1 0 10-2.4 10-8.2zm28.3 2.4h-9.8v7.9h-2.8v-22h13.2c5.2 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6h10.2c3 0 5.2-1.5 5.2-4.7 0-3.3-2.1-4.1-5.2-4.1h-10.2v8.8zm40.3-1.5l-6.8 5.6v6.4h-2.9v-22h2.9v12.3l15.2-12.2h3.7l-9.9 8.1 10.3 13.8h-3.6l-8.9-12z" />
<path fill="#050A14"
d="M188.4 71.7a10.4 10.4 0 01-20.8 0 10.4 10.4 0 1120.8 0zM224.2 45c-2.2-3.9-5-7.5-8.2-10.7l-12 7c-3.7-3.2-8-5.7-12.6-7.3a49.4 49.4 0 00-9.7 13.9 59 59 0 0140.1 14l7.6-4.4a57 57 0 00-5.2-12.5zM178 125.1c4.5 0 9-.6 13.4-1.7v-14a40 40 0 0012.5-7.2 47.7 47.7 0 00-7.1-15.3 59 59 0 01-32.2 27.7v8.7c4.4 1.2 8.9 1.8 13.4 1.8zM131.8 45c-2.3 4-4 8.1-5.2 12.5l12 7a40 40 0 000 14.4c5.7 1.5 11.3 2 16.9 1.5a59 59 0 01-8-41.7l-7.5-4.3c-3.2 3.2-6 6.7-8.2 10.6z" />
<path fill="#00B4FF"
d="M224.2 98.4c2.3-3.9 4-8 5.2-12.4l-12-7a40 40 0 000-14.5c-5.7-1.5-11.3-2-16.9-1.5a59 59 0 018 41.7l7.5 4.4c3.2-3.2 6-6.8 8.2-10.7zm-92.4 0c2.2 4 5 7.5 8.2 10.7l12-7a40 40 0 0012.6 7.3c4-4.1 7.3-8.8 9.7-13.8a59 59 0 01-40-14l-7.7 4.4c1.2 4.3 3 8.5 5.2 12.4zm46.2-80c-4.5 0-9 .5-13.4 1.7V34a40 40 0 00-12.5 7.2c1.5 5.7 4 10.8 7.1 15.4a59 59 0 0132.2-27.7V20a53.3 53.3 0 00-13.4-1.8z" />
<path fill="#00B4FF"
d="M178 9.2a62.6 62.6 0 11-.1 125.2A62.6 62.6 0 01178 9.2m0-9.2a71.7 71.7 0 100 143.5A71.7 71.7 0 00178 0z" />
<path fill="#050A14"
d="M96.6 212v4.3c-9.2-.8-15.4-5.8-15.4-17.8V180h4.6v18.4c0 8.6 4 12.6 10.8 13.5zm16-31.9v18.4c0 8.9-4.3 12.8-10.9 13.5v4.4c9.2-.7 15.5-5.6 15.5-18v-18.3h-4.7zM62.2 199v-2.2c0-12.7-8.8-17.4-21-17.4-12.1 0-20.7 4.7-20.7 17.4v2.2c0 12.8 8.6 17.6 20.7 17.6 1.5 0 3-.1 4.4-.3l11.8 6.2 2-3.3-8.2-4-6.4-3.1a32 32 0 01-3.6.2c-9.8 0-16-3.9-16-13.3v-2.2c0-9.3 6.2-13.1 16-13.1 9.9 0 16.3 3.8 16.3 13.1v2.2c0 5.3-2.1 8.7-5.6 10.8l4.8 2.4c3.4-2.8 5.5-7 5.5-13.2zM168 215.6h5.1L156 179.7h-4.8l17 36zM143 205l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.8-3.7H143zm133.7 10.7h5.2l-17.3-35.9h-4.8l17 36zm-25-10.7l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.7-3.7h-14.8zm73.8-2.5c6-1.2 9-5.4 9-11.4 0-8-4.5-10.9-12.9-10.9h-21.4v35.5h4.6v-31.3h16.5c5 0 8.5 1.4 8.5 6.7 0 5.2-3.5 7.7-8.5 7.7h-11.4v4.1h10.7l9.3 12.8h5.5l-9.9-13.2zm-117.4 9.9c-9.7 0-14.7-2.5-18.6-6.3l-2.2 3.8c5.1 5 11 6.7 21 6.7 1.6 0 3.1-.1 4.6-.3l-1.9-4h-3zm18.4-7c0-6.4-4.7-8.6-13.8-9.4l-10.1-1c-6.7-.7-9.3-2.2-9.3-5.6 0-2.5 1.4-4 4.6-5l-1.8-3.8c-4.7 1.4-7.5 4.2-7.5 8.9 0 5.2 3.4 8.7 13 9.6l11.3 1.2c6.4.6 8.9 2 8.9 5.4 0 2.7-2.1 4.7-6 5.8l1.8 3.9c5.3-1.6 8.9-4.7 8.9-10zm-20.3-21.9c7.9 0 13.3 1.8 18.1 5.7l1.8-3.9a30 30 0 00-19.6-5.9c-2 0-4 .1-5.7.3l1.9 4 3.5-.2z" />
<path fill="#00B4FF"
d="M.5 251.9c29.6-.5 59.2-.8 88.8-1l88.7-.3 88.7.3 44.4.4 44.4.6-44.4.6-44.4.4-88.7.3-88.7-.3a7981 7981 0 01-88.8-1z" />
<path fill="none" d="M-565.2 324H-252v15.8h-313.2z" />
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

@ -0,0 +1,107 @@
<script setup>
import {ref} from "vue";
import {EventsOn} from "app/wailsjs/runtime";
import {GetDetail, StartCapture, StopCapture} from "app/wailsjs/go/main/App";
import {useQuasar} from "quasar";
const columns = [
{name: 'id', label: '序号', field: 'id', align: 'left'},
{name: 'time', label: '时间', field: 'time', align: 'left'},
{name: 'source', label: '来源', field: 'source', align: 'left'},
{name: 'dest', label: '目的地', field: 'dest', align: 'left'},
{name: 'proto', label: '协议', field: 'proto', align: 'left'},
{name: 'length', label: '包长度', field: 'length', align: 'left'},
]
const packets = ref([])
const networkDevice = ref({Name: "", Description: "", Flags: 0, Addresses: [""]})
const pagination = ref({
rowsPerPage: 10000
})
const colorScheme = {
"TCP": '#c8e4fb',
"UDP": '#d0d4ed',
"ARP": '#d3ebd3',
"ETH": '#dacfed'
}
const detailDialogStatus = ref(false)
EventsOn("onNewDataPacket", data => {
packets.value.push(data)
})
EventsOn("onNewDataPacketBatch", data => {
packets.value.push(...data)
})
EventsOn("onErrOccurrence", err => {
console.warn(err)
})
const packetDetailView = ref()
function startCapture(device) {
networkDevice.value = device
packets.value.splice(0, packets.value.length)
StartCapture(networkDevice.value.Name)
.then(() => console.log("capture started"))
.catch(e => console.warn(e))
}
function stopCapture() {
// packets.value.splice(0, packets.value.length)
StopCapture()
.then(() => console.log("capture stopped"))
.catch(e => console.warn(e))
}
function showDetail(id) {
GetDetail(id)
.then(data => {
console.log(data)
packetDetailView.value = data
detailDialogStatus.value = true
})
.catch(e => console.warn(e))
}
defineExpose({
startCapture,
stopCapture
})
function formattedText(text) {
if (text === '[]') {
return '无'
}
return text
}
const quasar = useQuasar()
function viewRowHeader(name, text) {
console.log(text)
quasar.dialog({
title: `${name}层原始头部数据`,
message: text.replaceAll('\n', '<br>').replaceAll(' ', '&nbsp;'),
style: 'font-family: Consolas, Cascadia Code, Cascadia Mono, monospace',
fullWidth: true,
ok: true,
html: true,
})
}
</script>
<template>
<div></div>
</template>
<style lang="sass">
</style>

@ -0,0 +1,12 @@
// app global css in SCSS form
@tailwind base;
@tailwind components;
@tailwind utilities;
//
//* {
// user-select: none;
//}
body, #app, #q-app {
height: 100vh;
}

@ -0,0 +1,25 @@
// Quasar SCSS (& Sass) Variables
// --------------------------------------------------
// To customize the look and feel of this app, you can override
// the Sass/SCSS variables found in Quasar's source Sass/SCSS files.
// Check documentation for full list of Quasar variables
// Your own variables (that are declared here) and Quasar's own
// ones will be available out of the box in your .vue/.scss/.sass files
// It's highly recommended to change the default colors
// to match your app's branding.
// Tip: Use the "Theme Builder" on Quasar's documentation website.
$primary : #1976D2;
$secondary : #26A69A;
$accent : #9C27B0;
$dark : #1D1D1D;
$dark-page : #121212;
$positive : #21BA45;
$negative : #C10015;
$info : #31CCEC;
$warning : #F2C037;

@ -0,0 +1,323 @@
<script setup>
import {ref, watch} from "vue";
import {GetNetworkDeviceList} from "app/wailsjs/go/main/App";
import {useQuasar} from "quasar";
const dialogOpen = ref(false)
const quasar = useQuasar()
let started = ref(false)
const selectedDevice = ref('')
const selectedNetworkDevice = ref({Name: "", Description: "", Flags: 0, Addresses: [""]})
const deviceMap = new Map()
const devices = ref([{
Name: "",
Description: "",
Flags: 0,
Addresses: [""]
}])
watch(selectedDevice, (n, old) => {
console.log("new value", n)
console.log('device', deviceMap.get(n))
selectedNetworkDevice.value = deviceMap.get(n)
console.log('selectedNetworkDevice.value', selectedNetworkDevice.value)
})
import {EventsOn} from "app/wailsjs/runtime";
import {GetDetail, StartCapture, StopCapture} from "app/wailsjs/go/main/App";
const columns = [
{name: 'id', label: '序号', field: 'id', align: 'left'},
{name: 'time', label: '时间', field: 'time', align: 'left'},
{name: 'source', label: '来源', field: 'source', align: 'left'},
{name: 'dest', label: '目的地', field: 'dest', align: 'left'},
{name: 'proto', label: '协议', field: 'proto', align: 'left'},
{name: 'length', label: '包长度', field: 'length', align: 'left'},
]
const packets = ref([])
GetNetworkDeviceList()
.then(deviceList => {
devices.value = deviceList;
for (const device of deviceList) {
deviceMap.set(device.Name, device)
}
if (selectedDevice.value === '') {
selectedDevice.value = deviceList?.[0].Name;
}
dialogOpen.value = !dialogOpen.value;
})
.catch(e => quasar.dialog({
title: '出错了...',
message: e,
ok: true
}))
const pagination = ref({
rowsPerPage: 10000
})
const colorScheme = {
"TCP": '#c8e4fb',
"UDP": '#d0d4ed',
"ARP": '#d3ebd3',
"ETH": '#dacfed'
}
const detailDialogStatus = ref(false)
EventsOn("onNewDataPacket", data => {
packets.value.push(data)
})
EventsOn("onNewDataPacketBatch", data => {
packets.value.push(...data)
})
EventsOn("onErrOccurrence", err => {
console.warn(err)
})
const packetDetailView = ref()
function startCapture() {
if (started.value) {
return
}
packets.value.splice(0, packets.value.length)
StartCapture(selectedNetworkDevice.value.Name)
.then(() => {
console.log("capture started");
started.value = true;
})
.catch(e => {
console.warn(e);
started.value = false;
})
}
function stopCapture() {
// packets.value.splice(0, packets.value.length)
StopCapture()
.then(() => {
console.log("capture stopped");
started.value = false;
})
.catch(e => console.warn(e))
}
function showDetail(id) {
GetDetail(id)
.then(data => {
console.log(data)
packetDetailView.value = data
detailDialogStatus.value = true
})
.catch(e => console.warn(e))
}
function formattedText(text) {
if (text === '[]') {
return '无'
}
return text
}
function viewRowHeader(name, text) {
console.log(text)
quasar.dialog({
title: `${name}层原始头部数据`,
message: text.replaceAll('\n', '<br>').replaceAll(' ', '&nbsp;'),
style: 'font-family: Consolas, Cascadia Code, Cascadia Mono, monospace',
fullWidth: true,
ok: true,
html: true,
})
}
</script>
<template>
<q-layout class="h-full select-none" view="hHh lpR fFf">
<q-header class="bg-primary text-white">
<q-toolbar>
<q-toolbar-title>
eruhs
</q-toolbar-title>
<div class="flex flex-row gap-3">
<q-btn :outline=!started :unelevated=started
:icon="started ? 'stop_circle' : 'play_arrow'"
:color="started ? 'red' : 'white' "
@click="started ? stopCapture() : startCapture()">
<span class="ml-1.5">{{ !started ? 'Start' : 'Stop' }}</span>
</q-btn>
</div>
</q-toolbar>
</q-header>
<q-drawer show-if-above side="left" bordered>
<q-card-section class="z-10 sticky top-0 bg-white" >
<div class="text-h6 ">选择侦听网卡</div>
</q-card-section>
<q-list dense>
<div class="" v-for="device in devices" :key="device">
<q-item :disable="started" tag="label" v-ripple>
<q-item-section avatar>
<q-radio v-model="selectedDevice" :val="device.Name" color="primary"></q-radio>
</q-item-section>
<q-item-section>
<q-item-label>{{ device.Description }}</q-item-label>
<q-item-label caption>
设备ID: {{ device.Name }} <br>
地址<span v-for="(addr, idx) in device.Addresses" :key="addr"> {{ idx === 0 ? '' : ', ' }}{{
addr
}}</span> <br>
Flag: {{ device.Flags }}
</q-item-label>
</q-item-section>
</q-item>
</div>
</q-list>
</q-drawer>
<q-page-container class="h-full">
<div class="text-center h-full">
<div class="h-full">
<q-table
class="sticky-header-table h-full"
dense flat
:title="`网卡${selectedNetworkDevice.Description}上的包信息`"
:columns="columns"
:rows="packets"
row-key="id"
virtual-scroll
v-model:pagination="pagination"
:rows-per-page-options="[0]"
>
<template v-slot:body="props">
<q-tr :props="props" @click="showDetail(props.row.id)"
:style="`background-color: ${colorScheme[props.row.proto]}`">
<q-td key="id" :props="props">{{ props.row.id }}</q-td>
<q-td key="time" :props="props">{{ props.row.time / 1000.0 }}s</q-td>
<q-td key="source" :props="props">{{ props.row.source }}</q-td>
<q-td key="dest" :props="props">{{ props.row.dest }}</q-td>
<q-td key="proto" :props="props">{{ props.row.proto }}</q-td>
<q-td key="length" :props="props">{{ props.row.length }}</q-td>
</q-tr>
</template>
</q-table>
<q-dialog v-model="detailDialogStatus" persistent maximized transition-show="slide-up" transition-hide="slide-down">
<q-card class="bg-primary text-white select-none">
<q-bar class="sticky top-0 z-10">
<q-space/>
<q-btn dense flat icon="close" v-close-popup>
<q-tooltip class="bg-white text-primary">关闭</q-tooltip>
</q-btn>
</q-bar>
<div class="p-4" >
<div class="text-4xl">数据包详情</div>
<div v-for="layer in packetDetailView" :key="layer">
<br>
<div class="my-2 h-0.5 bg-white"></div>
<div class="flex flex-row">
<span class="text-3xl">{{ layer.name }}</span>
<q-space></q-space>
<q-btn outline @click="viewRowHeader(layer.name, layer.header)">查看原始头部</q-btn>
</div>
<br>
<table class="describe-table">
<tr v-for="(desc, item) in layer.describe" :key="item">
<td class="text-center">{{ item }}</td>
<td class="min-w-28">{{ desc.Name }}</td>
<td class="select-text">{{ formattedText(desc.Describe) }}<br></td>
</tr>
</table>
<div v-if="layer.payload !== ''">
<br>
<div class="my-2 h-0.5 bg-white"></div>
<div class="flex flex-row">
<span class="text-3xl">上层数据</span>
</div>
<br>
<!-- <pre>-->
<!-- <code>-->
<!-- {{ layer.payload }}-->
<!-- </code>-->
<!-- </pre>-->
<p style="font-family: Consolas, Cascadia Code, Cascadia Mono, monospace"
class="select-text text-base"
v-for="line in layer.payload.split('\n')" :key="line">{{ line.replaceAll(' ', '&nbsp;') }}</p>
</div>
</div>
</div>
</q-card>
</q-dialog>
</div>
</div>
</q-page-container>
</q-layout>
</template>
<style lang="sass">
.describe-table
td:first-child
padding: 0
th, td
padding: 0.25rem 0.5rem
.sticky-header-table
user-select: none
transition: all 0.2s linear 0.2s
th, td
font-size: 1rem !important
th
font-weight: bold
tr th
background-color: white
//
//thead tr:first-child th:first-child
// background-color: white
//
//tbody tr:nth-child(odd)
// background-color: rgb(240, 240, 240)
//
//tbody tr:nth-child(even)
// background-color: white
th:first-child,
td:first-child
position: sticky
left: 0
z-index: 1
th:first-child
z-index: 3 !important
thead tr th
position: sticky
z-index: 2
thead tr:first-child
height: 48px !important
thead tr:first-child th
top: 0
</style>

@ -0,0 +1,30 @@
import { route } from 'quasar/wrappers'
import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router'
import routes from './routes'
/*
* If not building with SSR mode, you can
* directly export the Router instantiation;
*
* The function below can be async too; either use
* async/await or return a Promise which resolves
* with the Router instance.
*/
export default route(function (/* { store, ssrContext } */) {
const createHistory = process.env.SERVER
? createMemoryHistory
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory)
const Router = createRouter({
scrollBehavior: () => ({ left: 0, top: 0 }),
routes,
// Leave this as is and make changes in quasar.conf.js instead!
// quasar.conf.js -> build -> vueRouterMode
// quasar.conf.js -> build -> publicPath
history: createHistory(process.env.VUE_ROUTER_BASE)
})
return Router
})

@ -0,0 +1,8 @@
const routes = [
{
path: '/',
component: () => import('pages/IndexPage.vue'),
},
]
export default routes

@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

@ -0,0 +1,14 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
import {main} from '../models';
import {core} from '../models';
export function GetDetail(arg1:number):Promise<{[key: number]: main.LayerDescribe}>;
export function GetNetworkDeviceList():Promise<Array<core.Device>>;
export function PauseCapture():Promise<void>;
export function StartCapture(arg1:string):Promise<void>;
export function StopCapture():Promise<void>;

@ -0,0 +1,23 @@
// @ts-check
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export function GetDetail(arg1) {
return window['go']['main']['App']['GetDetail'](arg1);
}
export function GetNetworkDeviceList() {
return window['go']['main']['App']['GetNetworkDeviceList']();
}
export function PauseCapture() {
return window['go']['main']['App']['PauseCapture']();
}
export function StartCapture(arg1) {
return window['go']['main']['App']['StartCapture'](arg1);
}
export function StopCapture() {
return window['go']['main']['App']['StopCapture']();
}

@ -0,0 +1,23 @@
export namespace core {
export class Device {
Name: string;
Description: string;
Flags: number;
Addresses: string[];
static createFrom(source: any = {}) {
return new Device(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.Name = source["Name"];
this.Description = source["Description"];
this.Flags = source["Flags"];
this.Addresses = source["Addresses"];
}
}
}

@ -0,0 +1,24 @@
{
"name": "@wailsapp/runtime",
"version": "2.0.0",
"description": "Wails Javascript runtime library",
"main": "runtime.js",
"types": "runtime.d.ts",
"scripts": {
},
"repository": {
"type": "git",
"url": "git+https://github.com/wailsapp/wails.git"
},
"keywords": [
"Wails",
"Javascript",
"Go"
],
"author": "Lea Anthony <lea.anthony@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/wailsapp/wails/issues"
},
"homepage": "https://github.com/wailsapp/wails#readme"
}

@ -0,0 +1,235 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
export interface Position {
x: number;
y: number;
}
export interface Size {
w: number;
h: number;
}
export interface Screen {
isCurrent: boolean;
isPrimary: boolean;
width : number
height : number
}
// Environment information such as platform, buildtype, ...
export interface EnvironmentInfo {
buildType: string;
platform: string;
arch: string;
}
// [EventsEmit](https://wails.io/docs/reference/runtime/events#eventsemit)
// emits the given event. Optional data may be passed with the event.
// This will trigger any event listeners.
export function EventsEmit(eventName: string, ...data: any): void;
// [EventsOn](https://wails.io/docs/reference/runtime/events#eventson) sets up a listener for the given event name.
export function EventsOn(eventName: string, callback: (...data: any) => void): () => void;
// [EventsOnMultiple](https://wails.io/docs/reference/runtime/events#eventsonmultiple)
// sets up a listener for the given event name, but will only trigger a given number times.
export function EventsOnMultiple(eventName: string, callback: (...data: any) => void, maxCallbacks: number): () => void;
// [EventsOnce](https://wails.io/docs/reference/runtime/events#eventsonce)
// sets up a listener for the given event name, but will only trigger once.
export function EventsOnce(eventName: string, callback: (...data: any) => void): () => void;
// [EventsOff](https://wails.io/docs/reference/runtime/events#eventsoff)
// unregisters the listener for the given event name.
export function EventsOff(eventName: string, ...additionalEventNames: string[]): void;
// [EventsOffAll](https://wails.io/docs/reference/runtime/events#eventsoffall)
// unregisters all listeners.
export function EventsOffAll(): void;
// [LogPrint](https://wails.io/docs/reference/runtime/log#logprint)
// logs the given message as a raw message
export function LogPrint(message: string): void;
// [LogTrace](https://wails.io/docs/reference/runtime/log#logtrace)
// logs the given message at the `trace` log level.
export function LogTrace(message: string): void;
// [LogDebug](https://wails.io/docs/reference/runtime/log#logdebug)
// logs the given message at the `debug` log level.
export function LogDebug(message: string): void;
// [LogError](https://wails.io/docs/reference/runtime/log#logerror)
// logs the given message at the `error` log level.
export function LogError(message: string): void;
// [LogFatal](https://wails.io/docs/reference/runtime/log#logfatal)
// logs the given message at the `fatal` log level.
// The application will quit after calling this method.
export function LogFatal(message: string): void;
// [LogInfo](https://wails.io/docs/reference/runtime/log#loginfo)
// logs the given message at the `info` log level.
export function LogInfo(message: string): void;
// [LogWarning](https://wails.io/docs/reference/runtime/log#logwarning)
// logs the given message at the `warning` log level.
export function LogWarning(message: string): void;
// [WindowReload](https://wails.io/docs/reference/runtime/window#windowreload)
// Forces a reload by the main application as well as connected browsers.
export function WindowReload(): void;
// [WindowReloadApp](https://wails.io/docs/reference/runtime/window#windowreloadapp)
// Reloads the application frontend.
export function WindowReloadApp(): void;
// [WindowSetAlwaysOnTop](https://wails.io/docs/reference/runtime/window#windowsetalwaysontop)
// Sets the window AlwaysOnTop or not on top.
export function WindowSetAlwaysOnTop(b: boolean): void;
// [WindowSetSystemDefaultTheme](https://wails.io/docs/next/reference/runtime/window#windowsetsystemdefaulttheme)
// *Windows only*
// Sets window theme to system default (dark/light).
export function WindowSetSystemDefaultTheme(): void;
// [WindowSetLightTheme](https://wails.io/docs/next/reference/runtime/window#windowsetlighttheme)
// *Windows only*
// Sets window to light theme.
export function WindowSetLightTheme(): void;
// [WindowSetDarkTheme](https://wails.io/docs/next/reference/runtime/window#windowsetdarktheme)
// *Windows only*
// Sets window to dark theme.
export function WindowSetDarkTheme(): void;
// [WindowCenter](https://wails.io/docs/reference/runtime/window#windowcenter)
// Centers the window on the monitor the window is currently on.
export function WindowCenter(): void;
// [WindowSetTitle](https://wails.io/docs/reference/runtime/window#windowsettitle)
// Sets the text in the window title bar.
export function WindowSetTitle(title: string): void;
// [WindowFullscreen](https://wails.io/docs/reference/runtime/window#windowfullscreen)
// Makes the window full screen.
export function WindowFullscreen(): void;
// [WindowUnfullscreen](https://wails.io/docs/reference/runtime/window#windowunfullscreen)
// Restores the previous window dimensions and position prior to full screen.
export function WindowUnfullscreen(): void;
// [WindowIsFullscreen](https://wails.io/docs/reference/runtime/window#windowisfullscreen)
// Returns the state of the window, i.e. whether the window is in full screen mode or not.
export function WindowIsFullscreen(): Promise<boolean>;
// [WindowSetSize](https://wails.io/docs/reference/runtime/window#windowsetsize)
// Sets the width and height of the window.
export function WindowSetSize(width: number, height: number): Promise<Size>;
// [WindowGetSize](https://wails.io/docs/reference/runtime/window#windowgetsize)
// Gets the width and height of the window.
export function WindowGetSize(): Promise<Size>;
// [WindowSetMaxSize](https://wails.io/docs/reference/runtime/window#windowsetmaxsize)
// Sets the maximum window size. Will resize the window if the window is currently larger than the given dimensions.
// Setting a size of 0,0 will disable this constraint.
export function WindowSetMaxSize(width: number, height: number): void;
// [WindowSetMinSize](https://wails.io/docs/reference/runtime/window#windowsetminsize)
// Sets the minimum window size. Will resize the window if the window is currently smaller than the given dimensions.
// Setting a size of 0,0 will disable this constraint.
export function WindowSetMinSize(width: number, height: number): void;
// [WindowSetPosition](https://wails.io/docs/reference/runtime/window#windowsetposition)
// Sets the window position relative to the monitor the window is currently on.
export function WindowSetPosition(x: number, y: number): void;
// [WindowGetPosition](https://wails.io/docs/reference/runtime/window#windowgetposition)
// Gets the window position relative to the monitor the window is currently on.
export function WindowGetPosition(): Promise<Position>;
// [WindowHide](https://wails.io/docs/reference/runtime/window#windowhide)
// Hides the window.
export function WindowHide(): void;
// [WindowShow](https://wails.io/docs/reference/runtime/window#windowshow)
// Shows the window, if it is currently hidden.
export function WindowShow(): void;
// [WindowMaximise](https://wails.io/docs/reference/runtime/window#windowmaximise)
// Maximises the window to fill the screen.
export function WindowMaximise(): void;
// [WindowToggleMaximise](https://wails.io/docs/reference/runtime/window#windowtogglemaximise)
// Toggles between Maximised and UnMaximised.
export function WindowToggleMaximise(): void;
// [WindowUnmaximise](https://wails.io/docs/reference/runtime/window#windowunmaximise)
// Restores the window to the dimensions and position prior to maximising.
export function WindowUnmaximise(): void;
// [WindowIsMaximised](https://wails.io/docs/reference/runtime/window#windowismaximised)
// Returns the state of the window, i.e. whether the window is maximised or not.
export function WindowIsMaximised(): Promise<boolean>;
// [WindowMinimise](https://wails.io/docs/reference/runtime/window#windowminimise)
// Minimises the window.
export function WindowMinimise(): void;
// [WindowUnminimise](https://wails.io/docs/reference/runtime/window#windowunminimise)
// Restores the window to the dimensions and position prior to minimising.
export function WindowUnminimise(): void;
// [WindowIsMinimised](https://wails.io/docs/reference/runtime/window#windowisminimised)
// Returns the state of the window, i.e. whether the window is minimised or not.
export function WindowIsMinimised(): Promise<boolean>;
// [WindowIsNormal](https://wails.io/docs/reference/runtime/window#windowisnormal)
// Returns the state of the window, i.e. whether the window is normal or not.
export function WindowIsNormal(): Promise<boolean>;
// [WindowSetBackgroundColour](https://wails.io/docs/reference/runtime/window#windowsetbackgroundcolour)
// Sets the background colour of the window to the given RGBA colour definition. This colour will show through for all transparent pixels.
export function WindowSetBackgroundColour(R: number, G: number, B: number, A: number): void;
// [ScreenGetAll](https://wails.io/docs/reference/runtime/window#screengetall)
// Gets the all screens. Call this anew each time you want to refresh data from the underlying windowing system.
export function ScreenGetAll(): Promise<Screen[]>;
// [BrowserOpenURL](https://wails.io/docs/reference/runtime/browser#browseropenurl)
// Opens the given URL in the system browser.
export function BrowserOpenURL(url: string): void;
// [Environment](https://wails.io/docs/reference/runtime/intro#environment)
// Returns information about the environment
export function Environment(): Promise<EnvironmentInfo>;
// [Quit](https://wails.io/docs/reference/runtime/intro#quit)
// Quits the application.
export function Quit(): void;
// [Hide](https://wails.io/docs/reference/runtime/intro#hide)
// Hides the application.
export function Hide(): void;
// [Show](https://wails.io/docs/reference/runtime/intro#show)
// Shows the application.
export function Show(): void;
// [ClipboardGetText](https://wails.io/docs/reference/runtime/clipboard#clipboardgettext)
// Returns the current text stored on clipboard
export function ClipboardGetText(): Promise<string>;
// [ClipboardSetText](https://wails.io/docs/reference/runtime/clipboard#clipboardsettext)
// Sets a text on the clipboard
export function ClipboardSetText(text: string): Promise<boolean>;

@ -0,0 +1,202 @@
/*
_ __ _ __
| | / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__ )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
export function LogPrint(message) {
window.runtime.LogPrint(message);
}
export function LogTrace(message) {
window.runtime.LogTrace(message);
}
export function LogDebug(message) {
window.runtime.LogDebug(message);
}
export function LogInfo(message) {
window.runtime.LogInfo(message);
}
export function LogWarning(message) {
window.runtime.LogWarning(message);
}
export function LogError(message) {
window.runtime.LogError(message);
}
export function LogFatal(message) {
window.runtime.LogFatal(message);
}
export function EventsOnMultiple(eventName, callback, maxCallbacks) {
return window.runtime.EventsOnMultiple(eventName, callback, maxCallbacks);
}
export function EventsOn(eventName, callback) {
return EventsOnMultiple(eventName, callback, -1);
}
export function EventsOff(eventName, ...additionalEventNames) {
return window.runtime.EventsOff(eventName, ...additionalEventNames);
}
export function EventsOnce(eventName, callback) {
return EventsOnMultiple(eventName, callback, 1);
}
export function EventsEmit(eventName) {
let args = [eventName].slice.call(arguments);
return window.runtime.EventsEmit.apply(null, args);
}
export function WindowReload() {
window.runtime.WindowReload();
}
export function WindowReloadApp() {
window.runtime.WindowReloadApp();
}
export function WindowSetAlwaysOnTop(b) {
window.runtime.WindowSetAlwaysOnTop(b);
}
export function WindowSetSystemDefaultTheme() {
window.runtime.WindowSetSystemDefaultTheme();
}
export function WindowSetLightTheme() {
window.runtime.WindowSetLightTheme();
}
export function WindowSetDarkTheme() {
window.runtime.WindowSetDarkTheme();
}
export function WindowCenter() {
window.runtime.WindowCenter();
}
export function WindowSetTitle(title) {
window.runtime.WindowSetTitle(title);
}
export function WindowFullscreen() {
window.runtime.WindowFullscreen();
}
export function WindowUnfullscreen() {
window.runtime.WindowUnfullscreen();
}
export function WindowIsFullscreen() {
return window.runtime.WindowIsFullscreen();
}
export function WindowGetSize() {
return window.runtime.WindowGetSize();
}
export function WindowSetSize(width, height) {
window.runtime.WindowSetSize(width, height);
}
export function WindowSetMaxSize(width, height) {
window.runtime.WindowSetMaxSize(width, height);
}
export function WindowSetMinSize(width, height) {
window.runtime.WindowSetMinSize(width, height);
}
export function WindowSetPosition(x, y) {
window.runtime.WindowSetPosition(x, y);
}
export function WindowGetPosition() {
return window.runtime.WindowGetPosition();
}
export function WindowHide() {
window.runtime.WindowHide();
}
export function WindowShow() {
window.runtime.WindowShow();
}
export function WindowMaximise() {
window.runtime.WindowMaximise();
}
export function WindowToggleMaximise() {
window.runtime.WindowToggleMaximise();
}
export function WindowUnmaximise() {
window.runtime.WindowUnmaximise();
}
export function WindowIsMaximised() {
return window.runtime.WindowIsMaximised();
}
export function WindowMinimise() {
window.runtime.WindowMinimise();
}
export function WindowUnminimise() {
window.runtime.WindowUnminimise();
}
export function WindowSetBackgroundColour(R, G, B, A) {
window.runtime.WindowSetBackgroundColour(R, G, B, A);
}
export function ScreenGetAll() {
return window.runtime.ScreenGetAll();
}
export function WindowIsMinimised() {
return window.runtime.WindowIsMinimised();
}
export function WindowIsNormal() {
return window.runtime.WindowIsNormal();
}
export function BrowserOpenURL(url) {
window.runtime.BrowserOpenURL(url);
}
export function Environment() {
return window.runtime.Environment();
}
export function Quit() {
window.runtime.Quit();
}
export function Hide() {
window.runtime.Hide();
}
export function Show() {
window.runtime.Show();
}
export function ClipboardGetText() {
return window.runtime.ClipboardGetText();
}
export function ClipboardSetText(text) {
return window.runtime.ClipboardSetText(text);
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,41 @@
module eruhs
go 1.21
toolchain go1.22.0
require (
github.com/dustin/go-humanize v1.0.1
github.com/google/gopacket v1.1.19
github.com/wailsapp/wails/v2 v2.8.0
)
require (
github.com/bep/debounce v1.2.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect
github.com/labstack/echo/v4 v4.10.2 // indirect
github.com/labstack/gommon v0.4.0 // indirect
github.com/leaanthony/go-ansi-parser v1.6.0 // indirect
github.com/leaanthony/gosod v1.0.3 // indirect
github.com/leaanthony/slicer v1.6.0 // indirect
github.com/leaanthony/u v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/samber/lo v1.38.1 // indirect
github.com/tkrajina/go-reflector v0.5.6 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wailsapp/go-webview2 v1.0.10 // indirect
github.com/wailsapp/mimetype v1.4.1 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sys v0.16.0 // indirect
golang.org/x/text v0.14.0 // indirect
)

110
go.sum

@ -0,0 +1,110 @@
github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY=
github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN2M=
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/leaanthony/debme v1.2.1 h1:9Tgwf+kjcrbMQ4WnPcEIUcQuIZYqdWftzZkBr+i/oOc=
github.com/leaanthony/debme v1.2.1/go.mod h1:3V+sCm5tYAgQymvSOfYQ5Xx2JCr+OXiD9Jkw3otUjiA=
github.com/leaanthony/go-ansi-parser v1.6.0 h1:T8TuMhFB6TUMIUm0oRrSbgJudTFw9csT3ZK09w0t4Pg=
github.com/leaanthony/go-ansi-parser v1.6.0/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
github.com/leaanthony/gosod v1.0.3 h1:Fnt+/B6NjQOVuCWOKYRREZnjGyvg+mEhd1nkkA04aTQ=
github.com/leaanthony/gosod v1.0.3/go.mod h1:BJ2J+oHsQIyIQpnLPjnqFGTMnOZXDbvWtRCSG7jGxs4=
github.com/leaanthony/slicer v1.5.0/go.mod h1:FwrApmf8gOrpzEWM2J/9Lh79tyq8KTX5AzRtwV7m4AY=
github.com/leaanthony/slicer v1.6.0 h1:1RFP5uiPJvT93TAHi+ipd3NACobkW53yUiBqZheE/Js=
github.com/leaanthony/slicer v1.6.0/go.mod h1:o/Iz29g7LN0GqH3aMjWAe90381nyZlDNquK+mtH2Fj8=
github.com/leaanthony/u v1.1.0 h1:2n0d2BwPVXSUq5yhe8lJPHdxevE2qK5G99PMStMZMaI=
github.com/leaanthony/u v1.1.0/go.mod h1:9+o6hejoRljvZ3BzdYlVL0JYCwtnAsVuN9pVTQcaRfI=
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/wailsapp/go-webview2 v1.0.10 h1:PP5Hug6pnQEAhfRzLCoOh2jJaPdrqeRgJKZhyYyDV/w=
github.com/wailsapp/go-webview2 v1.0.10/go.mod h1:Uk2BePfCRzttBBjFrBmqKGJd41P6QIHeV9kTgIeOZNo=
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
github.com/wailsapp/wails/v2 v2.8.0 h1:b2NNn99uGPiN6P5bDsnPwOJZWtAOUhNLv7Vl+YxMTr4=
github.com/wailsapp/wails/v2 v2.8.0/go.mod h1:EFUGWkUX3KofO4fmKR/GmsLy3HhPH7NbyOEaMt8lBF0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200810151505-1b9f1253b3ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -0,0 +1,37 @@
package main
import (
"embed"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
_ "net/http/pprof"
)
//go:embed all:frontend/dist/spa
var assets embed.FS
func main() {
// Create an instance of the app structure
app := NewApp()
// Create application with options
err := wails.Run(&options.App{
Title: "eruhs",
Width: 1480,
Height: 940,
AssetServer: &assetserver.Options{
Assets: assets,
},
BackgroundColour: &options.RGBA{R: 255, G: 255, B: 255, A: 1},
OnStartup: app.startup,
Bind: []interface{}{
app, &UIDataPackView{},
},
})
if err != nil {
println("Error:", err.Error())
}
}

@ -0,0 +1,22 @@
package main
import (
"eruhs/describer"
"time"
)
type UIDataPackView struct {
Id int `json:"id"`
Time time.Duration `json:"time"`
Source string `json:"source"`
Dest string `json:"dest"`
Proto string `json:"proto"`
Length int `json:"length"`
}
type LayerDescribe struct {
HeaderRaw string `json:"header"`
ApplicationPayloadRaw string `json:"payload"`
LayerName string `json:"name"`
Describe describer.Describe `json:"describe"`
}

@ -0,0 +1,13 @@
{
"$schema": "https://wails.io/schemas/config.v2.json",
"name": "eruhs",
"outputfilename": "eruhs",
"frontend:install": "npm install",
"frontend:build": "npm run build",
"frontend:dev:watcher": "npm run dev",
"frontend:dev:serverUrl": "http://127.0.0.1:9000",
"author": {
"name": "lensfrex",
"email": "lensferno@outlook.com"
}
}
Loading…
Cancel
Save