Skip to content

Commit

Permalink
[software] added mr.duppl cmd (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
buglloc authored Dec 3, 2024
1 parent b341a3d commit 057f86f
Show file tree
Hide file tree
Showing 10 changed files with 320 additions and 18 deletions.
1 change: 1 addition & 0 deletions software/cmd/mr.duppl/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mr.duppl
119 changes: 119 additions & 0 deletions software/cmd/mr.duppl/commands/dump.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package commands

import (
"errors"
"fmt"
"io"
"os"
"runtime"

"github.com/gopacket/gopacket"
"github.com/gopacket/gopacket/layers"
"github.com/gopacket/gopacket/pcapgo"
"github.com/spf13/cobra"

"github.com/buglloc/mr.duppl/software/pkg/dupplcap"
"github.com/buglloc/mr.duppl/software/pkg/usbp"
)

var dumpArgs struct {
Interface string
NoFolding bool
Output string
}

var dumpCmd = &cobra.Command{
Use: "dump",
SilenceUsage: true,
SilenceErrors: true,
Short: "Dump Mr.Duppl capture",
RunE: func(_ *cobra.Command, _ []string) error {
dev, err := dupplcap.NewDevice(dumpArgs.Interface)
if err != nil {
return fmt.Errorf("opening device: %w", err)
}
defer func() { _ = dev.Close() }()

if err := dev.StartCapture(!dumpArgs.NoFolding); err != nil {
return fmt.Errorf("start capture: %w", err)
}
defer func() { _ = dev.StopCapture() }()

if len(dumpArgs.Output) != 0 {
out, err := os.Create(dumpArgs.Output)
if err != nil {
return fmt.Errorf("creating output file: %w", err)
}
defer func() { _ = out.Close() }()

return dumpCapture(dev, out)
}

return printCapture(dev)
},
}

func dumpCapture(dev *dupplcap.Device, out io.Writer) error {
w, err := pcapgo.NewNgWriterInterface(
out,
pcapgo.NgInterface{
Name: dev.Iface(),
OS: runtime.GOOS,
LinkType: layers.LinkType(dupplcap.LinkTypeUSBFullSpeed.Int()),
SnapLength: 0, //unlimited
// TimestampResolution: 9,
},
pcapgo.NgWriterOptions{
SectionInfo: pcapgo.NgSectionInfo{
Hardware: runtime.GOARCH,
OS: runtime.GOOS,
Application: "Mr.Duppl", //spread the word
},
},
)
if err != nil {
return fmt.Errorf("open pcapng writer: %w", err)
}
defer func() { _ = w.Flush() }()

for {
packet, err := dev.Packet()
if err != nil {
if errors.Is(err, io.EOF) {
return nil
}

return fmt.Errorf("read packet: %w", err)
}

ci := gopacket.CaptureInfo{
Length: len(packet),
CaptureLength: len(packet),
InterfaceIndex: 0,
}
err = w.WritePacket(ci, packet)
if err != nil {
return fmt.Errorf("write packet: %w", err)
}

usbp.Print(packet, os.Stdout)
}
}

func printCapture(dev *dupplcap.Device) error {
for {
packet, err := dev.Packet()
if err != nil {
return fmt.Errorf("read packet: %w", err)
}

usbp.Print(packet, os.Stdout)
}
}

func init() {
flags := dumpCmd.PersistentFlags()
flags.StringVarP(&dumpArgs.Interface, "iface", "i", "", "interface to use")
flags.StringVarP(&dumpArgs.Output, "out", "o", "", "write PcapNG file")
flags.BoolVar(&dumpArgs.NoFolding, "no-packet-folding", false, "disable packet folding")
}
51 changes: 51 additions & 0 deletions software/cmd/mr.duppl/commands/ls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package commands

import (
"fmt"

"github.com/spf13/cobra"

"github.com/buglloc/mr.duppl/software/pkg/dupplcap"
)

var lsArgs struct {
Name bool
Path bool
}

var lsCmd = &cobra.Command{
Use: "ls",
SilenceUsage: true,
SilenceErrors: true,
Short: "List devices",
RunE: func(_ *cobra.Command, _ []string) error {
if !lsArgs.Name && !lsArgs.Path {
lsArgs.Name = true
lsArgs.Path = true
}

ifaces, err := dupplcap.Ifaces()
if err != nil {
return fmt.Errorf("unable to get information about interfaces: %w", err)
}

for _, iface := range ifaces {
switch {
case lsArgs.Name && lsArgs.Path:
fmt.Println(iface.Name, iface.Path)
case lsArgs.Name:
fmt.Println(iface.Name)
case lsArgs.Path:
fmt.Println(iface.Path)
}
}

return nil
},
}

func init() {
flags := lsCmd.PersistentFlags()
flags.BoolVarP(&lsArgs.Name, "name", "n", false, "display device name")
flags.BoolVarP(&lsArgs.Path, "path", "p", false, "display device path")
}
22 changes: 22 additions & 0 deletions software/cmd/mr.duppl/commands/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package commands

import (
"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
Use: "mr.duppl",
SilenceUsage: true,
SilenceErrors: true,
}

func Execute() error {
return rootCmd.Execute()
}

func init() {
rootCmd.AddCommand(
lsCmd,
dumpCmd,
)
}
15 changes: 15 additions & 0 deletions software/cmd/mr.duppl/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package main

import (
"fmt"
"os"

"github.com/buglloc/mr.duppl/software/cmd/mr.duppl/commands"
)

func main() {
if err := commands.Execute(); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}
5 changes: 4 additions & 1 deletion software/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ go 1.23.1
require (
github.com/gopacket/gopacket v1.3.0
github.com/kor44/extcap v0.0.1
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.8.4
go.bug.st/serial v1.6.2
)

require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/creack/goselect v0.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/urfave/cli/v2 v2.25.7 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
golang.org/x/net v0.28.0 // indirect
Expand Down
10 changes: 8 additions & 2 deletions software/go.sum
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
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/gopacket/gopacket v1.3.0 h1:MouZCc+ej0vnqzB0WeiaO/6+tGvb+KU7UczxoQ+X0Yc=
github.com/gopacket/gopacket v1.3.0/go.mod h1:WnFrU1Xkf5lWKV38uKNR9+yYtppn+ZYzOyNqMeH4oNE=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kor44/extcap v0.0.1 h1:fcNWKzd25nN9sD+mY9z/GWsTfNXNnuIVlJRKpSkeHpQ=
github.com/kor44/extcap v0.0.1/go.mod h1:+Pl5vUaEZv3VWQ7EC9eksa+XsLGU0ZUVXGYEjAuC+9k=
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/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
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/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
Expand Down
60 changes: 50 additions & 10 deletions software/pkg/dupplcap/device.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package dupplcap

import (
"errors"
"fmt"
"io"
"strings"

"go.bug.st/serial"
)
Expand All @@ -12,20 +15,29 @@ const (
)

type Device struct {
port serial.Port
iface string
port serial.Port
}

func NewDeviceByIface(iface string) (*Device, error) {
serialPort, err := serial.Open(iface, &serial.Mode{
BaudRate: 115200,
})
func NewDevice(nameOrIface string) (*Device, error) {
if len(nameOrIface) != 0 {
if strings.HasPrefix(nameOrIface, Name) {
return NewDeviceByName(nameOrIface)
}

return NewDeviceByIface(nameOrIface)
}

ifaces, err := Ifaces()
if err != nil {
return nil, fmt.Errorf("open serial port %s: %w", iface, err)
return nil, fmt.Errorf("list ifaces: %w", err)
}

return &Device{
port: serialPort,
}, nil
if len(ifaces) == 0 {
return nil, errors.New("device was not found")
}

return NewDeviceByIface(ifaces[0].Path)
}

func NewDeviceByName(name string) (*Device, error) {
Expand All @@ -45,8 +57,36 @@ func NewDeviceByName(name string) (*Device, error) {
return nil, fmt.Errorf("device with name %s not found", name)
}

func NewDeviceByIface(iface string) (*Device, error) {
serialPort, err := serial.Open(iface, &serial.Mode{
BaudRate: 115200,
})
if err != nil {
return nil, fmt.Errorf("open serial port %s: %w", iface, err)
}

return &Device{
iface: iface,
port: serialPort,
}, nil
}

func (r *Device) Iface() string {
return r.iface
}

func (r *Device) Packet() ([]byte, error) {
return readSlipPacket(r.port)
packet, err := readSlipPacket(r.port)
if err != nil {
var portErr *serial.PortError
if errors.As(err, &portErr) && portErr.Code() == serial.PortClosed {
return nil, io.EOF
}

return nil, fmt.Errorf("read packet: %w", err)
}

return packet, nil
}

func (r *Device) StartCapture(withPacketFolding bool) error {
Expand Down
11 changes: 6 additions & 5 deletions software/pkg/dupplcap/ifaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import (
)

const (
targetVID = "2E8A"
targetPID = "5052"
VID = "2E8A"
PID = "5052"
Name = "Mr.Duppl"
)

type Iface struct {
Expand All @@ -29,17 +30,17 @@ func Ifaces() ([]Iface, error) {
continue
}

if !strings.EqualFold(port.VID, targetVID) {
if !strings.EqualFold(port.VID, VID) {
continue
}

if !strings.EqualFold(port.PID, targetPID) {
if !strings.EqualFold(port.PID, PID) {
continue
}

out = append(out, Iface{
Path: port.Name,
Name: fmt.Sprintf("Mr.Duppl:%s", port.SerialNumber),
Name: fmt.Sprintf("%s:%s", Name, port.SerialNumber),
})
}

Expand Down
Loading

0 comments on commit 057f86f

Please sign in to comment.