From 5fe3ef3d525f54e5c39b2358866c593db40971d0 Mon Sep 17 00:00:00 2001 From: evilsocket Date: Fri, 9 Aug 2024 15:42:03 +0200 Subject: [PATCH] new: new can module for CAN-bus --- go.mod | 3 +- go.sum | 32 ++++ modules/can/can.go | 84 +++++++++ modules/can/can_recon.go | 131 +++++++++++++ modules/can/can_show.go | 50 +++++ modules/can/dbc.go | 227 +++++++++++++++++++++++ modules/events_stream/events_view.go | 2 + modules/events_stream/events_view_can.go | 51 +++++ modules/modules.go | 2 + network/can.go | 110 +++++++++++ network/can_device.go | 57 ++++++ session/session.go | 9 +- 12 files changed, 755 insertions(+), 3 deletions(-) create mode 100644 modules/can/can.go create mode 100644 modules/can/can_recon.go create mode 100644 modules/can/can_show.go create mode 100644 modules/can/dbc.go create mode 100644 modules/events_stream/events_view_can.go create mode 100644 network/can.go create mode 100644 network/can_device.go diff --git a/go.mod b/go.mod index e2ebc8c4..6c2455f6 100644 --- a/go.mod +++ b/go.mod @@ -27,8 +27,6 @@ require ( github.com/jpillora/go-tld v1.2.1 github.com/kr/binarydist v0.1.0 // indirect github.com/malfunkt/iprange v0.9.0 - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect github.com/mdlayher/dhcp6 v0.0.0-20190311162359-2a67805d7d0b github.com/miekg/dns v1.1.61 github.com/mitchellh/go-homedir v1.1.0 @@ -37,6 +35,7 @@ require ( github.com/stratoberry/go-gpsd v1.3.0 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64 + go.einride.tech/can v0.12.0 golang.org/x/net v0.28.0 golang.org/x/tools v0.24.0 // indirect ) diff --git a/go.sum b/go.sum index d6a3406b..d3608d7b 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpH github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/adrianmo/go-nmea v1.9.0 h1:kCuerWLDIppltHNZ2HGdCGkqbmupYJYfE6indcGkcp8= github.com/adrianmo/go-nmea v1.9.0/go.mod h1:u8bPnpKt/D/5rll/5l9f6iDfeq5WZW0+/SXdkwix6Tg= +github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/antchfx/jsonquery v1.3.5 h1:243OSaQh02EfmASa3w3weKC9UaiD8RRzJhgfvq3q408= github.com/antchfx/jsonquery v1.3.5/go.mod h1:qH23yX2Jsj1/k378Yu/EOgPCNgJ35P9tiGOeQdt/GWc= github.com/antchfx/xpath v1.3.1 h1:PNbFuUqHwWl0xRjvUPjJ95Agbmdj2uzzIwmQKgu4oCk= @@ -31,12 +33,15 @@ github.com/elazarl/goproxy/ext v0.0.0-20210110162100-a92cc753f88e h1:CQn2/8fi3km github.com/elazarl/goproxy/ext v0.0.0-20210110162100-a92cc753f88e/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8= github.com/evilsocket/islazy v1.11.0 h1:B5w6uuS6ki6iDG+aH/RFeoMb8ijQh/pGabewqp2UeJ0= github.com/evilsocket/islazy v1.11.0/go.mod h1:muYH4x5MB5YRdkxnrOtrXLIBX6LySj1uFIqys94LKdo= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/florianl/go-nfqueue/v2 v2.0.0 h1:NTCxS9b0GSbHkWv1a7oOvZn679fsyDkaSkRvOYpQ9Oo= github.com/florianl/go-nfqueue/v2 v2.0.0/go.mod h1:M2tBLIj62QpwqjwV0qfcjqGOqP3qiTuXr2uSRBXH9Qk= github.com/gobwas/glob v0.0.0-20181002190808-e7a84e9525fe h1:8P+/htb3mwwpeGdJg69yBF/RofK7c6Fjz5Ypa/bTqbY= github.com/gobwas/glob v0.0.0-20181002190808-e7a84e9525fe/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -63,6 +68,7 @@ github.com/jpillora/go-tld v1.2.1 h1:kDKOkmXLlskqjcvNs7w5XHLep7c8WM7Xd4HQjxllVMk github.com/jpillora/go-tld v1.2.1/go.mod h1:plzIl7xr5UWKGy7R+giuv+L/nOjrPjsoWxy/ST9OBUk= github.com/kr/binarydist v0.1.0 h1:6kAoLA9FMMnNGSehX0s1PdjbEaACznAv/W219j2uvyo= github.com/kr/binarydist v0.1.0/go.mod h1:DY7S//GCoz1BCd0B0EVrinCKAZN3pXe+MDaIZbXQVgM= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -99,26 +105,37 @@ github.com/robertkrimen/otto v0.2.1/go.mod h1:UPwtJ1Xu7JrLcZjNWN8orJaM5n5YEtqL// github.com/robertkrimen/otto v0.4.0 h1:/c0GRrK1XDPcgIasAsnlpBT5DelIeB9U/Z/JCQsgr7E= github.com/robertkrimen/otto v0.4.0/go.mod h1:uW9yN1CYflmUQYvAMS0m+ZiNo3dMzRUDQJX0jWbzgxw= github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc= +github.com/shurcooL/go v0.0.0-20190704215121-7189cc372560/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/stratoberry/go-gpsd v1.3.0 h1:JxJOEC4SgD0QY65AE7B1CtJtweP73nqJghZeLNU9J+c= github.com/stratoberry/go-gpsd v1.3.0/go.mod h1:nVf/vTgfYxOMxiQdy9BtJjojbFRtG8H3wNula++VgkU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 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/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64 h1:l/T7dYuJEQZOwVOpjIXr1180aM9PZL/d1MnMVIxefX4= github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64/go.mod h1:Q1NAJOuRdQCqN/VIWdnaaEhV8LpeO2rtlBP7/iDJNII= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.einride.tech/can v0.12.0 h1:6MW9TKycSovWqJxcYHpZEiuFCGuAfpqApCzTS15KrPk= +go.einride.tech/can v0.12.0/go.mod h1:5n3+AonCfUso6PfjD9l2d0W2LxTFjjHOnHAm+UMS9Ws= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= @@ -126,7 +143,9 @@ golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5D golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= 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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= @@ -138,10 +157,12 @@ golang.org/x/net v0.0.0-20190310074541-c10a0554eabf/go.mod h1:mL1N/T3taQHkDXs73r 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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= @@ -169,17 +190,20 @@ golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= @@ -188,6 +212,7 @@ golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXct golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= @@ -212,9 +237,12 @@ golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= @@ -222,7 +250,9 @@ golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375/go.mod h1:lNEQeAhU009zbRxng+XOj5ITVgY24WcbNnQopyfKoYQ= gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= @@ -232,3 +262,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C 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= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/modules/can/can.go b/modules/can/can.go new file mode 100644 index 00000000..370163eb --- /dev/null +++ b/modules/can/can.go @@ -0,0 +1,84 @@ +package can + +import ( + "net" + + "github.com/bettercap/bettercap/session" + "go.einride.tech/can/pkg/descriptor" + "go.einride.tech/can/pkg/socketcan" +) + +type CANModule struct { + session.SessionModule + + deviceName string + transport string + dbcPath string + dbc *descriptor.Database + + conn net.Conn + recv *socketcan.Receiver +} + +func NewCanModule(s *session.Session) *CANModule { + mod := &CANModule{ + SessionModule: session.NewSessionModule("can", s), + dbcPath: "", + transport: "can", + deviceName: "can0", + } + + mod.AddParam(session.NewStringParameter("can.device", + mod.deviceName, + "", + "CAN-bus device.")) + + mod.AddParam(session.NewStringParameter("can.transport", + mod.transport, + "", + "Network type, can be 'can' for SocketCAN or 'udp'.")) + + mod.AddParam(session.NewStringParameter("can.dbc_path", + mod.dbcPath, + "", + "Optional path to DBC file for decoding.")) + + mod.AddHandler(session.NewModuleHandler("can.recon on", "", + "Start CAN-bus discovery.", + func(args []string) error { + return mod.Start() + })) + + mod.AddHandler(session.NewModuleHandler("can.recon off", "", + "Stop CAN-bus discovery.", + func(args []string) error { + return mod.Stop() + })) + + mod.AddHandler(session.NewModuleHandler("can.clear", "", + "Clear everything collected by the discovery module.", + func(args []string) error { + mod.Session.CAN.Clear() + return nil + })) + + mod.AddHandler(session.NewModuleHandler("can.show", "", + "Show a list of detected CAN devices.", + func(args []string) error { + return mod.Show() + })) + + return mod +} + +func (mod *CANModule) Name() string { + return "can" +} + +func (mod *CANModule) Description() string { + return "A scanner and frames injection module for CAN-bus." +} + +func (mod *CANModule) Author() string { + return "Simone Margaritelli " +} diff --git a/modules/can/can_recon.go b/modules/can/can_recon.go new file mode 100644 index 00000000..75d0c355 --- /dev/null +++ b/modules/can/can_recon.go @@ -0,0 +1,131 @@ +package can + +import ( + "context" + "errors" + "fmt" + "os" + + "github.com/bettercap/bettercap/network" + "github.com/bettercap/bettercap/session" + "github.com/evilsocket/islazy/str" + "go.einride.tech/can" + "go.einride.tech/can/pkg/socketcan" +) + +type Message struct { + Frame can.Frame + Name string + Source *network.CANDevice + Signals map[string]string +} + +func (mod *CANModule) Configure() error { + var err error + + if mod.Running() { + return session.ErrAlreadyStarted(mod.Name()) + } else if err, mod.deviceName = mod.StringParam("can.device"); err != nil { + return err + } else if err, mod.transport = mod.StringParam("can.transport"); err != nil { + return err + } else if mod.transport != "can" && mod.transport != "udp" { + return errors.New("invalid transport") + } else if err, mod.dbcPath = mod.StringParam("can.dbc_path"); err != nil { + return err + } + + if mod.dbcPath != "" { + input, err := os.ReadFile(mod.dbcPath) + if err != nil { + return fmt.Errorf("can't read %s: %v", mod.dbcPath, err) + } + + mod.Info("compiling %s ...", mod.dbcPath) + + result, err := dbcCompile(mod.dbcPath, input) + if err != nil { + return fmt.Errorf("can't compile %s: %v", mod.dbcPath, err) + } + + for _, warning := range result.Warnings { + mod.Warning("%v", warning) + } + + mod.dbc = result.Database + } else { + mod.Warning("no can.dbc_path specified, messages won't be parsed") + } + + if mod.conn, err = socketcan.DialContext(context.Background(), mod.transport, mod.deviceName); err != nil { + return err + } + + mod.recv = socketcan.NewReceiver(mod.conn) + + return nil +} + +func (mod *CANModule) Start() error { + if err := mod.Configure(); err != nil { + return err + } + + return mod.SetRunning(true, func() { + mod.Info("started on %s ...", mod.deviceName) + + for mod.recv.Receive() { + frame := mod.recv.Frame() + msg := Message{ + Frame: frame, + } + + if mod.dbc != nil { + if message, found := mod.dbc.Message(frame.ID); found { + msg.Name = message.Name + + sourceName := message.SenderNode + sourceDesc := "" + if sender, found := mod.dbc.Node(message.SenderNode); found { + sourceName = sender.Name + sourceDesc = sender.Description + } + + _, msg.Source = mod.Session.CAN.AddIfNew(sourceName, sourceDesc, frame.Data[:]) + + msg.Signals = make(map[string]string) + + for _, signal := range message.Signals { + var value string + + if signal.Length <= 32 && signal.IsFloat { + value = fmt.Sprintf("%f", signal.UnmarshalFloat(frame.Data)) + } else if signal.Length == 1 { + value = fmt.Sprintf("%v", signal.UnmarshalBool(frame.Data)) + } else if signal.IsSigned { + value = fmt.Sprintf("%d", signal.UnmarshalSigned(frame.Data)) + } else { + value = fmt.Sprintf("%d", signal.UnmarshalUnsigned(frame.Data)) + } + + msg.Signals[signal.Name] = str.Trim(fmt.Sprintf("%s %s", value, signal.Unit)) + } + } + } + + mod.Session.Events.Add("can.message", msg) + } + }) +} + +func (mod *CANModule) Stop() error { + if mod.conn != nil { + mod.recv.Close() + mod.conn.Close() + mod.conn = nil + mod.recv = nil + mod.dbc = nil + mod.dbcPath = "" + } + return nil +} diff --git a/modules/can/can_show.go b/modules/can/can_show.go new file mode 100644 index 00000000..b21fa78b --- /dev/null +++ b/modules/can/can_show.go @@ -0,0 +1,50 @@ +package can + +import ( + "time" + + "github.com/bettercap/bettercap/network" + "github.com/dustin/go-humanize" + "github.com/evilsocket/islazy/tui" +) + +var ( + AliveTimeInterval = time.Duration(5) * time.Minute + PresentTimeInterval = time.Duration(1) * time.Minute + JustJoinedTimeInterval = time.Duration(10) * time.Second +) + +func (mod *CANModule) getRow(dev *network.CANDevice) []string { + sinceLastSeen := time.Since(dev.LastSeen) + seen := dev.LastSeen.Format("15:04:05") + + if sinceLastSeen <= JustJoinedTimeInterval { + seen = tui.Bold(seen) + } else if sinceLastSeen > PresentTimeInterval { + seen = tui.Dim(seen) + } + + return []string{ + dev.Name, + dev.Description, + humanize.Bytes(dev.Read), + seen, + } +} + +func (mod *CANModule) Show() (err error) { + devices := mod.Session.CAN.Devices() + + rows := make([][]string, 0) + for _, dev := range devices { + rows = append(rows, mod.getRow(dev)) + } + + tui.Table(mod.Session.Events.Stdout, []string{"Name", "Description", "Data", "Seen"}, rows) + + if len(rows) > 0 { + mod.Session.Refresh() + } + + return nil +} diff --git a/modules/can/dbc.go b/modules/can/dbc.go new file mode 100644 index 00000000..530abcd3 --- /dev/null +++ b/modules/can/dbc.go @@ -0,0 +1,227 @@ +package can + +import ( + "fmt" + "sort" + "time" + + "go.einride.tech/can/pkg/dbc" + "go.einride.tech/can/pkg/descriptor" +) + +type CompileResult struct { + Database *descriptor.Database + Warnings []error +} + +func dbcCompile(sourceFile string, data []byte) (result *CompileResult, err error) { + p := dbc.NewParser(sourceFile, data) + if err := p.Parse(); err != nil { + return nil, fmt.Errorf("failed to parse DBC source file: %w", err) + } + defs := p.Defs() + c := &compiler{ + db: &descriptor.Database{SourceFile: sourceFile}, + defs: defs, + } + c.collectDescriptors() + c.addMetadata() + c.sortDescriptors() + return &CompileResult{Database: c.db, Warnings: c.warnings}, nil +} + +type compileError struct { + def dbc.Def + reason string +} + +func (e *compileError) Error() string { + return fmt.Sprintf("failed to compile: %v (%v)", e.reason, e.def) +} + +type compiler struct { + db *descriptor.Database + defs []dbc.Def + warnings []error +} + +func (c *compiler) addWarning(warning error) { + c.warnings = append(c.warnings, warning) +} + +func (c *compiler) collectDescriptors() { + for _, def := range c.defs { + switch def := def.(type) { + case *dbc.VersionDef: + c.db.Version = def.Version + case *dbc.MessageDef: + if def.MessageID == dbc.IndependentSignalsMessageID { + continue // don't compile + } + message := &descriptor.Message{ + Name: string(def.Name), + ID: def.MessageID.ToCAN(), + IsExtended: def.MessageID.IsExtended(), + Length: uint8(def.Size), + SenderNode: string(def.Transmitter), + } + for _, signalDef := range def.Signals { + signal := &descriptor.Signal{ + Name: string(signalDef.Name), + IsBigEndian: signalDef.IsBigEndian, + IsSigned: signalDef.IsSigned, + IsMultiplexer: signalDef.IsMultiplexerSwitch, + IsMultiplexed: signalDef.IsMultiplexed, + MultiplexerValue: uint(signalDef.MultiplexerSwitch), + Start: uint8(signalDef.StartBit), + Length: uint8(signalDef.Size), + Scale: signalDef.Factor, + Offset: signalDef.Offset, + Min: signalDef.Minimum, + Max: signalDef.Maximum, + Unit: signalDef.Unit, + } + for _, receiver := range signalDef.Receivers { + signal.ReceiverNodes = append(signal.ReceiverNodes, string(receiver)) + } + message.Signals = append(message.Signals, signal) + } + c.db.Messages = append(c.db.Messages, message) + case *dbc.NodesDef: + for _, node := range def.NodeNames { + c.db.Nodes = append(c.db.Nodes, &descriptor.Node{Name: string(node)}) + } + } + } +} + +func (c *compiler) addMetadata() { + for _, def := range c.defs { + switch def := def.(type) { + case *dbc.SignalValueTypeDef: + signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared signal"}) + continue + } + switch def.SignalValueType { + case dbc.SignalValueTypeInt: + signal.IsFloat = false + case dbc.SignalValueTypeFloat32: + if signal.Length == 32 { + signal.IsFloat = true + } else { + reason := fmt.Sprintf("incorrect float signal length: %d", signal.Length) + c.addWarning(&compileError{def: def, reason: reason}) + } + default: + reason := fmt.Sprintf("unsupported signal value type: %v", def.SignalValueType) + c.addWarning(&compileError{def: def, reason: reason}) + } + case *dbc.CommentDef: + switch def.ObjectType { + case dbc.ObjectTypeMessage: + if def.MessageID == dbc.IndependentSignalsMessageID { + continue // don't compile + } + message, ok := c.db.Message(def.MessageID.ToCAN()) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared message"}) + continue + } + message.Description = def.Comment + case dbc.ObjectTypeSignal: + if def.MessageID == dbc.IndependentSignalsMessageID { + continue // don't compile + } + signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared signal"}) + continue + } + signal.Description = def.Comment + case dbc.ObjectTypeNetworkNode: + node, ok := c.db.Node(string(def.NodeName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared node"}) + continue + } + node.Description = def.Comment + } + case *dbc.ValueDescriptionsDef: + if def.MessageID == dbc.IndependentSignalsMessageID { + continue // don't compile + } + if def.ObjectType != dbc.ObjectTypeSignal { + continue // don't compile + } + signal, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared signal"}) + continue + } + for _, valueDescription := range def.ValueDescriptions { + signal.ValueDescriptions = append(signal.ValueDescriptions, &descriptor.ValueDescription{ + Description: valueDescription.Description, + Value: int64(valueDescription.Value), + }) + } + case *dbc.AttributeValueForObjectDef: + switch def.ObjectType { + case dbc.ObjectTypeMessage: + msg, ok := c.db.Message(def.MessageID.ToCAN()) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared message"}) + continue + } + switch def.AttributeName { + case "GenMsgSendType": + if err := msg.SendType.UnmarshalString(def.StringValue); err != nil { + c.addWarning(&compileError{def: def, reason: err.Error()}) + continue + } + case "GenMsgCycleTime": + msg.CycleTime = time.Duration(def.IntValue) * time.Millisecond + case "GenMsgDelayTime": + msg.DelayTime = time.Duration(def.IntValue) * time.Millisecond + } + case dbc.ObjectTypeSignal: + sig, ok := c.db.Signal(def.MessageID.ToCAN(), string(def.SignalName)) + if !ok { + c.addWarning(&compileError{def: def, reason: "no declared signal"}) + } + if def.AttributeName == "GenSigStartValue" { + sig.DefaultValue = int(def.IntValue) + } + } + } + } +} + +func (c *compiler) sortDescriptors() { + // Sort nodes by name + sort.Slice(c.db.Nodes, func(i, j int) bool { + return c.db.Nodes[i].Name < c.db.Nodes[j].Name + }) + // Sort messages by ID + sort.Slice(c.db.Messages, func(i, j int) bool { + return c.db.Messages[i].ID < c.db.Messages[j].ID + }) + for _, m := range c.db.Messages { + m := m + // Sort signals by start (and multiplexer value) + sort.Slice(m.Signals, func(j, k int) bool { + if m.Signals[j].MultiplexerValue < m.Signals[k].MultiplexerValue { + return true + } + return m.Signals[j].Start < m.Signals[k].Start + }) + // Sort value descriptions by value + for _, s := range m.Signals { + s := s + sort.Slice(s.ValueDescriptions, func(k, l int) bool { + return s.ValueDescriptions[k].Value < s.ValueDescriptions[l].Value + }) + } + } +} diff --git a/modules/events_stream/events_view.go b/modules/events_stream/events_view.go index cc000949..360ceb10 100644 --- a/modules/events_stream/events_view.go +++ b/modules/events_stream/events_view.go @@ -120,6 +120,8 @@ func (mod *EventsStream) Render(output io.Writer, e session.Event) { mod.viewBLEEvent(output, e) } else if strings.HasPrefix(e.Tag, "hid.") { mod.viewHIDEvent(output, e) + } else if strings.HasPrefix(e.Tag, "can.") { + mod.viewCANEvent(output, e) } else if strings.HasPrefix(e.Tag, "gps.") { mod.viewGPSEvent(output, e) } else if strings.HasPrefix(e.Tag, "mod.") { diff --git a/modules/events_stream/events_view_can.go b/modules/events_stream/events_view_can.go new file mode 100644 index 00000000..b3f9c2c1 --- /dev/null +++ b/modules/events_stream/events_view_can.go @@ -0,0 +1,51 @@ +package events_stream + +import ( + "encoding/hex" + "fmt" + "io" + + "github.com/bettercap/bettercap/modules/can" + "github.com/bettercap/bettercap/network" + "github.com/bettercap/bettercap/session" + "github.com/dustin/go-humanize" + + "github.com/evilsocket/islazy/tui" +) + +func (mod *EventsStream) viewCANEvent(output io.Writer, e session.Event) { + if e.Tag == "can.device.new" { + dev := e.Data.(*network.CANDevice) + fmt.Fprintf(output, "[%s] [%s] new CAN device %s (%s) detected.\n", + e.Time.Format(mod.timeFormat), + tui.Green(e.Tag), + tui.Bold(dev.Name), + tui.Dim(dev.Description)) + } else if e.Tag == "can.message" { + msg := e.Data.(can.Message) + + // unparsed + if msg.Name == "" { + fmt.Fprintf(output, "[%s] [%s] (%s): %s\n", + e.Time.Format(mod.timeFormat), + tui.Green(e.Tag), + msg.Frame.ID, + tui.Dim(humanize.Bytes(uint64(msg.Frame.Length))), + hex.EncodeToString(msg.Frame.Data[:msg.Frame.Length])) + } else { + fmt.Fprintf(output, "[%s] [%s] %s (%s) from %s:\n", + e.Time.Format(mod.timeFormat), + tui.Green(e.Tag), + msg.Frame.ID, + msg.Name, + tui.Dim(humanize.Bytes(uint64(msg.Frame.Length))), + tui.Bold(msg.Source.Name)) + + for name, value := range msg.Signals { + fmt.Fprintf(output, " %s : %s\n", name, value) + } + } + } else { + fmt.Fprintf(output, "[%s] [%s] %v\n", e.Time.Format(mod.timeFormat), tui.Green(e.Tag), e) + } +} diff --git a/modules/modules.go b/modules/modules.go index 0578b07f..251e9772 100644 --- a/modules/modules.go +++ b/modules/modules.go @@ -6,6 +6,7 @@ import ( "github.com/bettercap/bettercap/modules/arp_spoof" "github.com/bettercap/bettercap/modules/ble" "github.com/bettercap/bettercap/modules/c2" + "github.com/bettercap/bettercap/modules/can" "github.com/bettercap/bettercap/modules/caplets" "github.com/bettercap/bettercap/modules/dhcp6_spoof" "github.com/bettercap/bettercap/modules/dns_spoof" @@ -41,6 +42,7 @@ func LoadModules(sess *session.Session) { sess.Register(arp_spoof.NewArpSpoofer(sess)) sess.Register(api_rest.NewRestAPI(sess)) sess.Register(ble.NewBLERecon(sess)) + sess.Register(can.NewCanModule(sess)) sess.Register(dhcp6_spoof.NewDHCP6Spoofer(sess)) sess.Register(net_recon.NewDiscovery(sess)) sess.Register(dns_spoof.NewDNSSpoofer(sess)) diff --git a/network/can.go b/network/can.go new file mode 100644 index 00000000..77e7a652 --- /dev/null +++ b/network/can.go @@ -0,0 +1,110 @@ +package network + +import ( + "encoding/json" + "sync" + "time" + + "github.com/evilsocket/islazy/data" +) + +type CANDevNewCallback func(dev *CANDevice) +type CANDevLostCallback func(dev *CANDevice) + +type CAN struct { + sync.RWMutex + devices map[string]*CANDevice + newCb CANDevNewCallback + lostCb CANDevLostCallback +} + +type canJSON struct { + Devices []*CANDevice `json:"devices"` +} + +func NewCAN(aliases *data.UnsortedKV, newcb CANDevNewCallback, lostcb CANDevLostCallback) *CAN { + return &CAN{ + devices: make(map[string]*CANDevice), + newCb: newcb, + lostCb: lostcb, + } +} + +func (h *CAN) MarshalJSON() ([]byte, error) { + doc := canJSON{ + Devices: make([]*CANDevice, 0), + } + + for _, dev := range h.devices { + doc.Devices = append(doc.Devices, dev) + } + + return json.Marshal(doc) +} + +func (b *CAN) Get(id string) (dev *CANDevice, found bool) { + b.RLock() + defer b.RUnlock() + dev, found = b.devices[id] + return +} + +func (b *CAN) AddIfNew(name string, description string, payload []byte) (bool, *CANDevice) { + b.Lock() + defer b.Unlock() + + id := name + + if dev, found := b.devices[id]; found { + dev.LastSeen = time.Now() + dev.AddPayload(payload) + return false, dev + } + + newDev := NewCANDevice(name, description, payload) + b.devices[id] = newDev + + if b.newCb != nil { + b.newCb(newDev) + } + + return true, newDev +} + +func (b *CAN) Remove(id string) { + b.Lock() + defer b.Unlock() + + if dev, found := b.devices[id]; found { + delete(b.devices, id) + if b.lostCb != nil { + b.lostCb(dev) + } + } +} + +func (b *CAN) Devices() (devices []*CANDevice) { + b.Lock() + defer b.Unlock() + + devices = make([]*CANDevice, 0) + for _, dev := range b.devices { + devices = append(devices, dev) + } + return +} + +func (b *CAN) EachDevice(cb func(mac string, d *CANDevice)) { + b.Lock() + defer b.Unlock() + + for m, dev := range b.devices { + cb(m, dev) + } +} + +func (b *CAN) Clear() { + b.Lock() + defer b.Unlock() + b.devices = make(map[string]*CANDevice) +} diff --git a/network/can_device.go b/network/can_device.go new file mode 100644 index 00000000..f1894f69 --- /dev/null +++ b/network/can_device.go @@ -0,0 +1,57 @@ +package network + +import ( + "encoding/json" + "sync" + "time" +) + +type CANDevice struct { + sync.Mutex + LastSeen time.Time + Name string + Description string + Read uint64 +} + +type canDeviceJSON struct { + LastSeen time.Time `json:"last_seen"` + Name string `json:"name"` + Description string `json:"description"` + Read uint64 `json:"description"` +} + +func NewCANDevice(name string, description string, payload []byte) *CANDevice { + dev := &CANDevice{ + LastSeen: time.Now(), + Name: name, + Description: description, + Read: uint64(len(payload)), + } + + return dev +} + +func (dev *CANDevice) MarshalJSON() ([]byte, error) { + dev.Lock() + defer dev.Unlock() + + doc := canDeviceJSON{ + LastSeen: dev.LastSeen, + Name: dev.Name, + Description: dev.Description, + Read: dev.Read, + } + + return json.Marshal(doc) +} + +func (dev *CANDevice) AddPayload(payload []byte) { + dev.Lock() + defer dev.Unlock() + + sz := len(payload) + if payload != nil && sz > 0 { + dev.Read += uint64(sz) + } +} diff --git a/session/session.go b/session/session.go index 0e9b1008..48249821 100644 --- a/session/session.go +++ b/session/session.go @@ -76,6 +76,7 @@ type Session struct { WiFi *network.WiFi BLE *network.BLE HID *network.HID + CAN *network.CAN Queue *packets.Queue StartedAt time.Time Active bool @@ -124,7 +125,7 @@ func New() (*Session, error) { if err != nil { return nil, err } - defer f.Close() + defer f.Close() if err := pprof.StartCPUProfile(f); err != nil { return nil, err } @@ -272,6 +273,12 @@ func (s *Session) Start() error { s.Firewall = firewall.Make(s.Interface) + s.CAN = network.NewCAN(s.Aliases, func(dev *network.CANDevice) { + s.Events.Add("can.device.new", dev) + }, func(dev *network.CANDevice) { + s.Events.Add("can.device.lost", dev) + }) + s.HID = network.NewHID(s.Aliases, func(dev *network.HIDDevice) { s.Events.Add("hid.device.new", dev) }, func(dev *network.HIDDevice) {