Compare commits
No commits in common. "master" and "t0.0.1" have entirely different histories.
|
@ -1,8 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
result="$(gofmt -s -l . | grep -v '^vendor/' )"
|
|
||||||
if [ -n "$result" ]; then
|
|
||||||
echo "Go code is not formatted, run 'gofmt -s -w .'" >&2
|
|
||||||
echo "$result"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
|
@ -1,25 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# checks if every desired package has test files
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
source_re = re.compile(".*\.go")
|
|
||||||
test_re = re.compile(".*_test\.go")
|
|
||||||
missing = False
|
|
||||||
|
|
||||||
for root, dirs, files in os.walk("."):
|
|
||||||
# ignore some paths
|
|
||||||
if root == "." or root.startswith("./vendor") or root.startswith("./."):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# source files but not test files?
|
|
||||||
if len(filter(source_re.match, files)) > 0 and len(filter(test_re.match, files)) == 0:
|
|
||||||
print("no test files for {}".format(root))
|
|
||||||
missing = True
|
|
||||||
|
|
||||||
if missing:
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
|
||||||
print("every package has test files")
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
workspace:
|
||||||
|
base: /go
|
||||||
|
path: src/dev.sum7.eu/wifictld/analyzer
|
||||||
|
|
||||||
|
pipeline:
|
||||||
|
build:
|
||||||
|
image: golang:latest
|
||||||
|
commands:
|
||||||
|
- go get ./...
|
||||||
|
- go build
|
||||||
|
codestyle:
|
||||||
|
image: golang:latest
|
||||||
|
commands:
|
||||||
|
- go get github.com/client9/misspell/cmd/misspell
|
||||||
|
- misspell -error .
|
||||||
|
- if [ -n "$(gofmt -s -l .)" ]; then echo "Go code is not formatted, run 'gofmt -s -w .'" >&2; exit 1; fi
|
||||||
|
test:
|
||||||
|
image: golang:latest
|
||||||
|
commands:
|
||||||
|
- go get github.com/stretchr/testify/assert
|
||||||
|
- go test ./... -v -cover
|
||||||
|
test-race:
|
||||||
|
image: golang:latest
|
||||||
|
commands:
|
||||||
|
- go get github.com/stretchr/testify/assert
|
||||||
|
- go test ./... -v -race
|
||||||
|
release:
|
||||||
|
image: plugins/gitea-release
|
||||||
|
base_url: https://dev.sum7.eu
|
||||||
|
secrets: [ gitea_token ]
|
||||||
|
files: /go/bin/analyzer
|
||||||
|
draft: true
|
||||||
|
when:
|
||||||
|
event: tag
|
|
@ -1,53 +0,0 @@
|
||||||
image: golang:latest
|
|
||||||
stages:
|
|
||||||
- build
|
|
||||||
- test
|
|
||||||
- deploy
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- mkdir -p "/go/src/dev.sum7.eu/$CI_PROJECT_NAMESPACE/"
|
|
||||||
- cp -R "/builds/$CI_PROJECT_PATH" "/go/src/dev.sum7.eu/$CI_PROJECT_NAMESPACE/"
|
|
||||||
- cd "/go/src/dev.sum7.eu/$CI_PROJECT_PATH"
|
|
||||||
- go get -d -t ./...
|
|
||||||
|
|
||||||
build-my-project:
|
|
||||||
stage: build
|
|
||||||
script:
|
|
||||||
- go install "dev.sum7.eu/$CI_PROJECT_PATH"
|
|
||||||
- mv "/go/bin/$CI_PROJECT_NAME" "/builds/$CI_PROJECT_PATH"
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- config_example.conf
|
|
||||||
- "$CI_PROJECT_NAME"
|
|
||||||
|
|
||||||
test-my-project:
|
|
||||||
stage: test
|
|
||||||
script:
|
|
||||||
- go get github.com/client9/misspell/cmd/misspell
|
|
||||||
- find . -type f | grep -v webroot/assets | xargs misspell -error
|
|
||||||
- ./.ci/check-gofmt
|
|
||||||
- ./.ci/check-testfiles
|
|
||||||
- go test $(go list ./... | grep -v /vendor/) -v -coverprofile .testCoverage.txt
|
|
||||||
artifacts:
|
|
||||||
paths:
|
|
||||||
- .testCoverage.txt
|
|
||||||
|
|
||||||
test-race-my-project:
|
|
||||||
stage: test
|
|
||||||
script:
|
|
||||||
- go test -race ./...
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
stage: deploy
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
script:
|
|
||||||
- go install "dev.sum7.eu/$CI_PROJECT_PATH"
|
|
||||||
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
|
|
||||||
- 'which rsync || ( apt-get update -y && apt-get install rsync -y )'
|
|
||||||
- eval $(ssh-agent -s)
|
|
||||||
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
|
|
||||||
- rsync -e "ssh -6 -o StrictHostKeyChecking=no -p $SSH_PORT" -a --delete "/builds/$CI_PROJECT_PATH/webroot/" "$CI_PROJECT_NAME@$SSH_HOST":/opt/$CI_PROJECT_NAME/webroot/
|
|
||||||
- ssh -6 -o StrictHostKeyChecking=no -p $SSH_PORT "$CI_PROJECT_NAME@$SSH_HOST" sudo /usr/bin/systemctl stop $CI_PROJECT_NAME
|
|
||||||
- scp -6 -o StrictHostKeyChecking=no -P $SSH_PORT "/go/bin/$CI_PROJECT_NAME" "$CI_PROJECT_NAME@$SSH_HOST":/opt/$CI_PROJECT_NAME/bin
|
|
||||||
- ssh -6 -o StrictHostKeyChecking=no -p $SSH_PORT "$CI_PROJECT_NAME@$SSH_HOST" sudo /usr/bin/systemctl start $CI_PROJECT_NAME
|
|
47
README.md
47
README.md
|
@ -1,47 +0,0 @@
|
||||||
# wifictld-analyzer
|
|
||||||
[](https://dev.sum7.eu/genofire/wifictld-analyzer/pipelines)
|
|
||||||
[](https://goreportcard.com/report/dev.sum7.eu/genofire/wifictld-analyzer)
|
|
||||||
[](https://godoc.org/dev.sum7.eu/genofire/wifictld-analyzer)
|
|
||||||
|
|
||||||
|
|
||||||
## Get wifictld-analyzer
|
|
||||||
|
|
||||||
#### Download
|
|
||||||
|
|
||||||
Latest Build binary from ci here:
|
|
||||||
|
|
||||||
[Download All](https://dev.sum7.eu/wifictld/wifictld-analyzer/-/jobs/artifacts/master/download/?job=build-my-project) (with config example)
|
|
||||||
|
|
||||||
[Download Binary](https://dev.sum7.eu/wifictld/wifictld-analyzer/-/jobs/artifacts/master/raw/wifictld-analyzer?inline=false&job=build-my-project)
|
|
||||||
|
|
||||||
#### Build
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get -u dev.sum7.eu/wifictld/wifictld-analyzer
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configure
|
|
||||||
|
|
||||||
see `config_example.conf`
|
|
||||||
|
|
||||||
## Start / Boot
|
|
||||||
|
|
||||||
_/lib/systemd/system/wifictld-analyzer.service_ :
|
|
||||||
```
|
|
||||||
[Unit]
|
|
||||||
Description=wifictld-analyzer
|
|
||||||
After=network.target
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
# User=notRoot
|
|
||||||
ExecStart=/opt/go/bin/wifictld-analyzer controller /etc/wifictld-analyzer.conf
|
|
||||||
Restart=always
|
|
||||||
RestartSec=5sec
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
```
|
|
||||||
|
|
||||||
Start: `systemctl start wifictld-analyzer`
|
|
||||||
Autostart: `systemctl enable wifictld-analyzer`
|
|
|
@ -3,19 +3,20 @@ package capture
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/bdlm/log"
|
"dev.sum7.eu/wifictld/analyzer/data"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
//Collector for capture
|
//Collector for capture
|
||||||
type Collector struct {
|
type Collector struct {
|
||||||
connections map[string]*net.UDPConn
|
connections map[string]*net.UDPConn
|
||||||
handler Handler
|
handler data.Handler
|
||||||
queue chan *Packet
|
queue chan *Packet
|
||||||
stop chan interface{}
|
stop chan interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCollector creates a Collector struct
|
// NewCollector creates a Collector struct
|
||||||
func NewCollector(handler Handler, ifaces []*IFaceConfig) *Collector {
|
func NewCollector(handler data.Handler, ifaces []IFaceConfig) *Collector {
|
||||||
|
|
||||||
coll := &Collector{
|
coll := &Collector{
|
||||||
handler: handler,
|
handler: handler,
|
||||||
|
@ -25,12 +26,6 @@ func NewCollector(handler Handler, ifaces []*IFaceConfig) *Collector {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, iface := range ifaces {
|
for _, iface := range ifaces {
|
||||||
if iface.Port == 0 {
|
|
||||||
iface.Port = Port
|
|
||||||
}
|
|
||||||
if iface.IPAddress == "" {
|
|
||||||
iface.IPAddress = MulticastAddressDefault
|
|
||||||
}
|
|
||||||
coll.listenUDP(iface)
|
coll.listenUDP(iface)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +43,7 @@ func (coll *Collector) Close() {
|
||||||
close(coll.queue)
|
close(coll.queue)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (coll *Collector) listenUDP(iface *IFaceConfig) {
|
func (coll *Collector) listenUDP(iface IFaceConfig) {
|
||||||
ip := net.ParseIP(iface.IPAddress)
|
ip := net.ParseIP(iface.IPAddress)
|
||||||
var conn *net.UDPConn
|
var conn *net.UDPConn
|
||||||
var err error
|
var err error
|
||||||
|
@ -95,7 +90,7 @@ type Packet struct {
|
||||||
|
|
||||||
func (coll *Collector) parser() {
|
func (coll *Collector) parser() {
|
||||||
for obj := range coll.queue {
|
for obj := range coll.queue {
|
||||||
msg, err := NewSocketMSG(obj.Raw)
|
msg, err := data.NewSocketMSG(obj.Raw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("unable to unmarshal request from %s: %s", obj.Address.String(), err)
|
log.Warnf("unable to unmarshal request from %s: %s", obj.Address.String(), err)
|
||||||
continue
|
continue
|
||||||
|
@ -113,7 +108,7 @@ func (coll *Collector) parser() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTo a specifical address
|
// SendTo a specifical address
|
||||||
func (coll *Collector) SendTo(addr *net.UDPAddr, msg *SocketMSG) {
|
func (coll *Collector) SendTo(addr *net.UDPAddr, msg *data.SocketMSG) {
|
||||||
log.Debugf("send[%s]: %s", addr, msg.String())
|
log.Debugf("send[%s]: %s", addr, msg.String())
|
||||||
data, err := msg.Marshal()
|
data, err := msg.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -131,7 +126,7 @@ func (coll *Collector) SendTo(addr *net.UDPAddr, msg *SocketMSG) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send to every connection to default address
|
// Send to every connection to default address
|
||||||
func (coll *Collector) Send(msg *SocketMSG) {
|
func (coll *Collector) Send(msg *data.SocketMSG) {
|
||||||
log.Debugf("send: %s", msg.String())
|
log.Debugf("send: %s", msg.String())
|
||||||
data, err := msg.Marshal()
|
data, err := msg.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
package capture
|
|
|
@ -1,20 +0,0 @@
|
||||||
package capture
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
// WifiClient datatype of wifictld
|
|
||||||
type WifiClient struct {
|
|
||||||
Addr data.HardwareAddr `json:"addr"`
|
|
||||||
Time time.Time `json:"time"`
|
|
||||||
TryProbe uint16 `json:"try_probe"`
|
|
||||||
TryAuth uint16 `json:"try_auth"`
|
|
||||||
Connected bool `json:"connected"`
|
|
||||||
Authed bool `json:"authed"`
|
|
||||||
FreqHighest uint16 `json:"freq_highest"`
|
|
||||||
SignalLowFreq int16 `json:"signal_low_freq"`
|
|
||||||
SignalHighFreq int16 `json:"signal_high_freq"`
|
|
||||||
}
|
|
|
@ -1,67 +1,50 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/database"
|
log "github.com/sirupsen/logrus"
|
||||||
"dev.sum7.eu/genofire/golang-lib/file"
|
|
||||||
"github.com/bdlm/log"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/capture"
|
"dev.sum7.eu/wifictld/analyzer/capture"
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/controller"
|
"dev.sum7.eu/wifictld/analyzer/controller"
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/web"
|
"dev.sum7.eu/wifictld/analyzer/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ControllerConfig struct {
|
var (
|
||||||
Database database.Config `toml:"database"`
|
central bool
|
||||||
Answer bool `toml:"answer"`
|
)
|
||||||
Webserver *web.Config `toml:"webserver"`
|
|
||||||
Interfaces []*capture.IFaceConfig `toml:"interfaces"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// queryCmd represents the query command
|
// queryCmd represents the query command
|
||||||
var controllerCmd = &cobra.Command{
|
var controllerCmd = &cobra.Command{
|
||||||
Use: "controller <interfaces>",
|
Use: "controller <interfaces>",
|
||||||
Short: "simulate a wifictld controller",
|
Short: "simulate a wifictld controller",
|
||||||
Example: `analyzer controller "/etc/wifictld.conf"`,
|
Example: `analyzer controller "eth0,wlan0"`,
|
||||||
Args: cobra.ExactArgs(1),
|
Args: cobra.ExactArgs(1),
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
config := &ControllerConfig{}
|
ifaces := strings.Split(args[0], ",")
|
||||||
|
|
||||||
file.ReadTOML(args[0], config)
|
log.Infof("listen on: %s", ifaces)
|
||||||
|
|
||||||
if err := database.Open(config.Database); err != nil {
|
var ifacesConfigs []capture.IFaceConfig
|
||||||
log.Panicf("no database connection: %s", err)
|
for _, iface := range ifaces {
|
||||||
|
ifaceConfig := capture.IFaceConfig{
|
||||||
|
InterfaceName: iface,
|
||||||
|
Port: port,
|
||||||
|
IPAddress: ipAddress,
|
||||||
|
}
|
||||||
|
ifacesConfigs = append(ifacesConfigs, ifaceConfig)
|
||||||
}
|
}
|
||||||
defer database.Close()
|
|
||||||
|
|
||||||
ctr := controller.NewController()
|
db := database.NewDB()
|
||||||
|
|
||||||
|
ctr := controller.NewController(db, central)
|
||||||
defer ctr.Close()
|
defer ctr.Close()
|
||||||
|
|
||||||
var handlers []capture.Handler
|
coll := capture.NewCollector(ctr.Handler, ifacesConfigs)
|
||||||
|
|
||||||
if config.Webserver.Enable {
|
|
||||||
log.Infof("starting webserver on %s", config.Webserver.Bind)
|
|
||||||
srv := web.New(config.Webserver)
|
|
||||||
go srv.Start()
|
|
||||||
handlers = append(handlers, srv.Handler)
|
|
||||||
defer srv.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
coll := capture.NewCollector(func(addr *net.UDPAddr, msg *capture.SocketMSG) (*capture.SocketMSG, error) {
|
|
||||||
for _, a := range handlers {
|
|
||||||
a(addr, msg)
|
|
||||||
}
|
|
||||||
if !config.Answer {
|
|
||||||
ctr.Handler(addr, msg)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return ctr.Handler(addr, msg)
|
|
||||||
}, config.Interfaces)
|
|
||||||
defer coll.Close()
|
defer coll.Close()
|
||||||
|
|
||||||
ctr.Send = coll.Send
|
ctr.Send = coll.Send
|
||||||
|
@ -77,5 +60,8 @@ var controllerCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RootCMD.AddCommand(controllerCmd)
|
RootCmd.AddCommand(controllerCmd)
|
||||||
|
controllerCmd.Flags().IntVar(&port, "port", capture.Port, "define a port to listen (if not set or set to 0 the kernel will use a random free port at its own)")
|
||||||
|
controllerCmd.Flags().StringVar(&ipAddress, "listen", capture.MulticastAddressDefault, "")
|
||||||
|
controllerCmd.Flags().BoolVar(¢ral, "central", false, "")
|
||||||
}
|
}
|
||||||
|
|
15
cmd/dump.go
15
cmd/dump.go
|
@ -7,10 +7,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/bdlm/log"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/capture"
|
"dev.sum7.eu/wifictld/analyzer/capture"
|
||||||
|
"dev.sum7.eu/wifictld/analyzer/data"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -29,9 +30,9 @@ var dumpCmd = &cobra.Command{
|
||||||
|
|
||||||
log.Infof("listen on: %s", ifaces)
|
log.Infof("listen on: %s", ifaces)
|
||||||
|
|
||||||
var ifacesConfigs []*capture.IFaceConfig
|
var ifacesConfigs []capture.IFaceConfig
|
||||||
for _, iface := range ifaces {
|
for _, iface := range ifaces {
|
||||||
ifaceConfig := &capture.IFaceConfig{
|
ifaceConfig := capture.IFaceConfig{
|
||||||
InterfaceName: iface,
|
InterfaceName: iface,
|
||||||
Port: port,
|
Port: port,
|
||||||
IPAddress: ipAddress,
|
IPAddress: ipAddress,
|
||||||
|
@ -39,9 +40,9 @@ var dumpCmd = &cobra.Command{
|
||||||
ifacesConfigs = append(ifacesConfigs, ifaceConfig)
|
ifacesConfigs = append(ifacesConfigs, ifaceConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
capture.DEBUG = debug
|
data.DEBUG = debug
|
||||||
|
|
||||||
coll := capture.NewCollector(func(addr *net.UDPAddr, msg *capture.SocketMSG) (*capture.SocketMSG, error) {
|
coll := capture.NewCollector(func(addr *net.UDPAddr, msg *data.SocketMSG) (*data.SocketMSG, error) {
|
||||||
log.Infof("recv[%s]: %s", addr, msg.String())
|
log.Infof("recv[%s]: %s", addr, msg.String())
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}, ifacesConfigs)
|
}, ifacesConfigs)
|
||||||
|
@ -57,7 +58,7 @@ var dumpCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RootCMD.AddCommand(dumpCmd)
|
RootCmd.AddCommand(dumpCmd)
|
||||||
dumpCmd.Flags().IntVar(&port, "port", capture.Port, "define a port to listen (if not set or set to 0 the kernel will use a random free port at its own)")
|
dumpCmd.Flags().IntVar(&port, "port", capture.Port, "define a port to listen (if not set or set to 0 the kernel will use a random free port at its own)")
|
||||||
dumpCmd.Flags().StringVar(&ipAddress, "listen", capture.MulticastAddressDefault, "")
|
dumpCmd.Flags().StringVar(&ipAddress, "listen", capture.MulticastAddressDefault, "")
|
||||||
}
|
}
|
||||||
|
|
21
cmd/root.go
21
cmd/root.go
|
@ -1,27 +1,24 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bdlm/log"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var debug bool
|
||||||
debug bool
|
|
||||||
timestamps bool
|
|
||||||
)
|
|
||||||
|
|
||||||
// RootCMD represents the base command when called without any subcommands
|
// RootCmd represents the base command when called without any subcommands
|
||||||
var RootCMD = &cobra.Command{
|
var RootCmd = &cobra.Command{
|
||||||
Use: "analyzer",
|
Use: "analyzer",
|
||||||
Short: "wifictld analyzer",
|
Short: "wifictld analyzer",
|
||||||
Long: `capture wifictld traffic and display thus`,
|
Long: `capture wifictld traffic and display thus`,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
// This is called by main.main(). It only needs to happen once to the RootCMD.
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
func Execute() {
|
func Execute() {
|
||||||
if err := RootCMD.Execute(); err != nil {
|
if err := RootCmd.Execute(); err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,11 +27,7 @@ func init() {
|
||||||
if debug {
|
if debug {
|
||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
}
|
}
|
||||||
log.SetFormatter(&log.TextFormatter{
|
|
||||||
DisableTimestamp: timestamps,
|
|
||||||
})
|
|
||||||
log.Debug("show debug")
|
log.Debug("show debug")
|
||||||
})
|
})
|
||||||
RootCMD.PersistentFlags().BoolVar(&debug, "v", false, "show debug log")
|
RootCmd.PersistentFlags().BoolVar(&debug, "v", false, "show debug log")
|
||||||
RootCMD.PersistentFlags().BoolVar(×tamps, "timestamps", false, "Enables timestamps for log output")
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
package cmd
|
|
|
@ -1,18 +0,0 @@
|
||||||
answer = false
|
|
||||||
|
|
||||||
[database]
|
|
||||||
type = "sqlite3"
|
|
||||||
logging = true
|
|
||||||
connection = "/tmp/wifictld.db"
|
|
||||||
# For Master-Slave cluster
|
|
||||||
# read_connection = ""
|
|
||||||
|
|
||||||
[webserver]
|
|
||||||
enable = true
|
|
||||||
bind = ":8080"
|
|
||||||
webroot = "./webroot/"
|
|
||||||
|
|
||||||
[[interfaces]]
|
|
||||||
ifname = "wlp4s0"
|
|
||||||
#port = 1000
|
|
||||||
#ip_address = "ff02::31f1"
|
|
|
@ -2,41 +2,27 @@ package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"time"
|
|
||||||
|
|
||||||
// "github.com/bdlm/log"
|
// log "github.com/sirupsen/logrus"
|
||||||
"dev.sum7.eu/genofire/golang-lib/database"
|
|
||||||
|
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/capture"
|
"dev.sum7.eu/wifictld/analyzer/data"
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/data"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Controller) Handler(addr *net.UDPAddr, msg *capture.SocketMSG) (*capture.SocketMSG, error) {
|
func (c *Controller) Handler(addr *net.UDPAddr, msg *data.SocketMSG) (*data.SocketMSG, error) {
|
||||||
ignore := false
|
ignore := false
|
||||||
if msg.Types.Is(capture.SocketMSGTypeClient) && msg.Client != nil {
|
if msg.Types.Is(data.SocketMSGTypeClient) && msg.Client != nil {
|
||||||
ignore = c.LearnClient(addr.IP, msg.Client)
|
ignore = c.db.LearnClient(addr.IP, msg.Client)
|
||||||
}
|
}
|
||||||
if !msg.Types.Is(capture.SocketMSGTypeRequest) {
|
if !msg.Types.Is(data.SocketMSGTypeRequest) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &data.Client{Addr: msg.Client.Addr}
|
msg = &data.SocketMSG{
|
||||||
|
Types: (data.SocketMSGTypeResponse | data.SocketMSGTypeClient),
|
||||||
if result := database.Read.Select([]string{"try_probe", "try_auth"}).First(client); result.Error != nil {
|
Client: c.db.GetClient(msg.Client.Addr),
|
||||||
return nil, result.Error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = &capture.SocketMSG{
|
if c.central || !ignore {
|
||||||
Types: (capture.SocketMSGTypeResponse | capture.SocketMSGTypeClient),
|
|
||||||
Client: &capture.WifiClient{
|
|
||||||
Addr: msg.Client.Addr,
|
|
||||||
Time: time.Now(),
|
|
||||||
TryProbe: client.TryProbe,
|
|
||||||
TryAuth: client.TryAuth,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if !ignore {
|
|
||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
// "github.com/bdlm/log"
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/database"
|
|
||||||
|
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/capture"
|
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Controller) LearnClient(apIP net.IP, clientWifictl *capture.WifiClient) bool {
|
|
||||||
ret := false
|
|
||||||
|
|
||||||
// learn ap
|
|
||||||
ap := &data.AP{
|
|
||||||
IP: apIP,
|
|
||||||
Lastseen: time.Now(),
|
|
||||||
}
|
|
||||||
result := database.Read.First(ap)
|
|
||||||
if result.RowsAffected > 0 {
|
|
||||||
database.Write.Save(ap)
|
|
||||||
} else {
|
|
||||||
database.Write.Create(ap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// learn client
|
|
||||||
client := &data.Client{
|
|
||||||
Addr: clientWifictl.Addr,
|
|
||||||
Lastseen: time.Now(),
|
|
||||||
APAddr: apIP,
|
|
||||||
Connected: clientWifictl.Connected,
|
|
||||||
SignalLowFreq: clientWifictl.SignalLowFreq,
|
|
||||||
SignalHighFreq: clientWifictl.SignalHighFreq,
|
|
||||||
}
|
|
||||||
|
|
||||||
database.Write.FirstOrCreate(client)
|
|
||||||
|
|
||||||
if clientWifictl.TryAuth > client.TryAuth {
|
|
||||||
client.TryAuth = clientWifictl.TryAuth
|
|
||||||
}
|
|
||||||
if clientWifictl.TryProbe > client.TryProbe {
|
|
||||||
client.TryProbe = clientWifictl.TryProbe
|
|
||||||
}
|
|
||||||
|
|
||||||
if client.FreqHighest < clientWifictl.FreqHighest {
|
|
||||||
ret = (client.FreqHighest != 0)
|
|
||||||
client.FreqHighest = clientWifictl.FreqHighest
|
|
||||||
}
|
|
||||||
if clientWifictl.Authed {
|
|
||||||
client.Authed = clientWifictl.Authed
|
|
||||||
}
|
|
||||||
database.Write.Save(client)
|
|
||||||
return ret
|
|
||||||
}
|
|
|
@ -4,22 +4,25 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/database"
|
log "github.com/sirupsen/logrus"
|
||||||
"github.com/bdlm/log"
|
|
||||||
|
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/capture"
|
"dev.sum7.eu/wifictld/analyzer/data"
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/data"
|
"dev.sum7.eu/wifictld/analyzer/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
SendTo func(addr *net.UDPAddr, msg *capture.SocketMSG)
|
SendTo func(addr *net.UDPAddr, msg *data.SocketMSG)
|
||||||
Send func(msg *capture.SocketMSG)
|
Send func(msg *data.SocketMSG)
|
||||||
ticker *time.Ticker
|
db *database.DB
|
||||||
|
ticker *time.Ticker
|
||||||
|
central bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewController() *Controller {
|
func NewController(db *database.DB, central bool) *Controller {
|
||||||
ctl := &Controller{
|
ctl := &Controller{
|
||||||
ticker: time.NewTicker(time.Minute),
|
ticker: time.NewTicker(60 * time.Second),
|
||||||
|
db: db,
|
||||||
|
central: central,
|
||||||
}
|
}
|
||||||
go ctl.Repeated()
|
go ctl.Repeated()
|
||||||
return ctl
|
return ctl
|
||||||
|
@ -30,12 +33,7 @@ func (c *Controller) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) Repeated() {
|
func (c *Controller) Repeated() {
|
||||||
aps := 0
|
|
||||||
clients := 0
|
|
||||||
|
|
||||||
for range c.ticker.C {
|
for range c.ticker.C {
|
||||||
database.Read.Model(&data.AP{}).Count(&aps)
|
log.Infof("lerned: %d APs, %d Clients", len(c.db.APs), len(c.db.Clients))
|
||||||
database.Read.Model(&data.Client{}).Count(&clients)
|
|
||||||
log.Debugf("learned: %d APs, %d Clients", aps, clients)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
package controller
|
|
20
data/ap.go
20
data/ap.go
|
@ -1,20 +0,0 @@
|
||||||
package data
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AP struct {
|
|
||||||
IP net.IP `json:"ip" gorm:"PRIMARY_KEY"`
|
|
||||||
Lastseen time.Time `json:"lastseen"`
|
|
||||||
Clients []Client `gorm:"foreignkey:APAddr" json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to initialize the database
|
|
||||||
func init() {
|
|
||||||
database.AddModel(&AP{})
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
package data
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/database"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
Addr HardwareAddr `gorm:"PRIMARY_KEY" json:"addr"`
|
|
||||||
APAddr net.IP `gorm:"column:ap" json:"ap"`
|
|
||||||
TryProbe uint16 `json:"try_probe"`
|
|
||||||
TryAuth uint16 `json:"try_auth"`
|
|
||||||
Connected bool `json:"connected"`
|
|
||||||
Authed bool `json:"authed"`
|
|
||||||
FreqHighest uint16 `json:"freq_highest"`
|
|
||||||
SignalLowFreq int16 `json:"signal_low_freq"`
|
|
||||||
SignalHighFreq int16 `json:"signal_high_freq"`
|
|
||||||
Lastseen time.Time `json:"lastseen"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to initialize the database
|
|
||||||
func init() {
|
|
||||||
database.AddModel(&Client{})
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
package capture
|
package data
|
||||||
|
|
||||||
import "net"
|
import "net"
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package data
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
type HardwareAddr net.HardwareAddr
|
|
||||||
|
|
||||||
//MarshalJSON to bytearray
|
|
||||||
func (a HardwareAddr) String() string {
|
|
||||||
return net.HardwareAddr(a).String()
|
|
||||||
}
|
|
||||||
|
|
||||||
//MarshalJSON to bytearray
|
|
||||||
func (a HardwareAddr) MarshalText() ([]byte, error) {
|
|
||||||
return []byte(a.String()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON from bytearray
|
|
||||||
func (a HardwareAddr) UnmarshalText(data []byte) error {
|
|
||||||
b, err := net.ParseMAC(string(data))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a = HardwareAddr(b)
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
package data
|
|
|
@ -1,13 +1,12 @@
|
||||||
package capture
|
package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bdlm/log"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/data"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var DEBUG = false
|
var DEBUG = false
|
||||||
|
@ -24,15 +23,16 @@ const (
|
||||||
|
|
||||||
func (a SocketMSGType) Is(b SocketMSGType) bool {
|
func (a SocketMSGType) Is(b SocketMSGType) bool {
|
||||||
if DEBUG {
|
if DEBUG {
|
||||||
log.Debugf("SocketType: %x & %x = %x -> %t", a, b, (a & b), (a&b) > 0)
|
|
||||||
|
log.Debugf("SocketType: %x & %x = %x -> %b", a, b, (a & b), (a&b) > 0)
|
||||||
}
|
}
|
||||||
return (a & b) > 0
|
return (a & b) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SocketMSG package of wifictld format
|
// SocketMSG package of wifictld format
|
||||||
type SocketMSG struct {
|
type SocketMSG struct {
|
||||||
Types SocketMSGType `json:"types"`
|
Types SocketMSGType
|
||||||
Client *WifiClient `json:"client,omitempty"`
|
Client *WifiClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSocketMSG(obj []byte) (*SocketMSG, error) {
|
func NewSocketMSG(obj []byte) (*SocketMSG, error) {
|
||||||
|
@ -100,7 +100,7 @@ func (msg *SocketMSG) Unmarshal(obj []byte) error {
|
||||||
|
|
||||||
if msg.Types.Is(SocketMSGTypeClient) {
|
if msg.Types.Is(SocketMSGTypeClient) {
|
||||||
msg.Client = &WifiClient{
|
msg.Client = &WifiClient{
|
||||||
Addr: data.HardwareAddr(obj[pos:(pos + 6)]),
|
Addr: net.HardwareAddr(obj[pos:(pos + 6)]),
|
||||||
Time: time.Unix(int64(binary.BigEndian.Uint32(obj[(pos+6):(pos+10)])), 0),
|
Time: time.Unix(int64(binary.BigEndian.Uint32(obj[(pos+6):(pos+10)])), 0),
|
||||||
TryProbe: binary.BigEndian.Uint16(obj[(pos + 10):(pos + 12)]),
|
TryProbe: binary.BigEndian.Uint16(obj[(pos + 10):(pos + 12)]),
|
||||||
TryAuth: binary.BigEndian.Uint16(obj[(pos + 12):(pos + 14)]),
|
TryAuth: binary.BigEndian.Uint16(obj[(pos + 12):(pos + 14)]),
|
|
@ -1,21 +1,14 @@
|
||||||
package capture
|
package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"log"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/bdlm/log"
|
|
||||||
"github.com/bdlm/std/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
DEBUG = true
|
|
||||||
log.SetLevel(logger.Debug)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMsgIsTypes(t *testing.T) {
|
func TestMsgIsTypes(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
types := SocketMSGType(5)
|
types := SocketMSGType(5)
|
|
@ -0,0 +1,30 @@
|
||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// default multicast group used by announced
|
||||||
|
MulticastAddressDefault = "ff02::31f1"
|
||||||
|
|
||||||
|
// default udp port used by announced
|
||||||
|
Port = 1000
|
||||||
|
|
||||||
|
// maximum receivable size
|
||||||
|
MaxDataGramSize = 256
|
||||||
|
)
|
||||||
|
|
||||||
|
// WifiClient datatype of wifictld
|
||||||
|
type WifiClient struct {
|
||||||
|
Addr net.HardwareAddr
|
||||||
|
Time time.Time
|
||||||
|
TryProbe uint16
|
||||||
|
TryAuth uint16
|
||||||
|
Connected bool
|
||||||
|
Authed bool
|
||||||
|
FreqHighest uint16
|
||||||
|
SignalLowFreq int16
|
||||||
|
SignalHighFreq int16
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
type AP struct {
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
// log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"dev.sum7.eu/wifictld/analyzer/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
Addr net.HardwareAddr
|
||||||
|
Time time.Time
|
||||||
|
TryProbe uint16
|
||||||
|
TryAuth uint16
|
||||||
|
Connected bool
|
||||||
|
Authed bool
|
||||||
|
FreqHighest uint16
|
||||||
|
SignalLowFreq int16
|
||||||
|
SignalHighFreq int16
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) LearnClient(apIP net.IP, clientWifictl *data.WifiClient) bool {
|
||||||
|
ret := false
|
||||||
|
|
||||||
|
apAddr := apIP.String()
|
||||||
|
ap, ok := db.APs[apAddr]
|
||||||
|
if !ok {
|
||||||
|
ap = &AP{}
|
||||||
|
db.APs[apAddr] = ap
|
||||||
|
}
|
||||||
|
clientAddr := clientWifictl.Addr.String()
|
||||||
|
client, ok := db.Clients[clientAddr]
|
||||||
|
if !ok {
|
||||||
|
client = &Client{
|
||||||
|
Addr: clientWifictl.Addr,
|
||||||
|
}
|
||||||
|
db.Clients[clientAddr] = client
|
||||||
|
}
|
||||||
|
client.Time = time.Now()
|
||||||
|
|
||||||
|
if client.FreqHighest < clientWifictl.FreqHighest {
|
||||||
|
ret = (client.FreqHighest != 0)
|
||||||
|
client.FreqHighest = clientWifictl.FreqHighest
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) GetClient(addr net.HardwareAddr) *data.WifiClient {
|
||||||
|
client, ok := db.Clients[addr.String()]
|
||||||
|
wClient := &data.WifiClient{
|
||||||
|
Addr: addr,
|
||||||
|
Time: time.Now(),
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
wClient.TryProbe = client.TryProbe
|
||||||
|
}
|
||||||
|
return wClient
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
Clients map[string]*Client `json:"client"`
|
||||||
|
APs map[string]*AP `json:"ap"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDB() *DB {
|
||||||
|
return &DB{
|
||||||
|
Clients: make(map[string]*Client),
|
||||||
|
APs: make(map[string]*AP),
|
||||||
|
}
|
||||||
|
}
|
2
main.go
2
main.go
|
@ -1,6 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "dev.sum7.eu/wifictld/wifictld-analyzer/cmd"
|
import "dev.sum7.eu/wifictld/analyzer/cmd"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cmd.Execute()
|
cmd.Execute()
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package web
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
Enable bool `toml:"enable"`
|
|
||||||
Bind string `toml:"bind"`
|
|
||||||
Webroot string `toml:"webroot"`
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package web
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/NYTimes/gziphandler"
|
|
||||||
"github.com/bdlm/log"
|
|
||||||
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/database"
|
|
||||||
lib "dev.sum7.eu/genofire/golang-lib/http"
|
|
||||||
"dev.sum7.eu/genofire/golang-lib/websocket"
|
|
||||||
|
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/capture"
|
|
||||||
"dev.sum7.eu/wifictld/wifictld-analyzer/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Server struct {
|
|
||||||
web *http.Server
|
|
||||||
ws *websocket.WebsocketHandlerService
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new webserver and starts it
|
|
||||||
func New(config *Config) *Server {
|
|
||||||
ws := websocket.NewWebsocketHandlerService()
|
|
||||||
ws.Listen("/ws")
|
|
||||||
|
|
||||||
http.HandleFunc("/data.json", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
type dataResponse struct {
|
|
||||||
APs []data.AP `json:"aps"`
|
|
||||||
Clients []data.Client `json:"clients"`
|
|
||||||
}
|
|
||||||
data := &dataResponse{}
|
|
||||||
if result := database.Read.Find(&data.APs); result.Error != nil {
|
|
||||||
log.WithField("error", result.Error.Error()).Warn("not possible to read APs")
|
|
||||||
http.Error(w, "not possible to read APs", http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if result := database.Read.Find(&data.Clients); result.Error != nil {
|
|
||||||
log.WithField("error", result.Error.Error()).Warn("not possible to read Clients")
|
|
||||||
http.Error(w, "not possible to read Clients", http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
lib.Write(w, data)
|
|
||||||
log.Info("fetch data")
|
|
||||||
})
|
|
||||||
|
|
||||||
http.Handle("/", gziphandler.GzipHandler(http.FileServer(http.Dir(config.Webroot))))
|
|
||||||
return &Server{
|
|
||||||
web: &http.Server{
|
|
||||||
Addr: config.Bind,
|
|
||||||
},
|
|
||||||
ws: ws,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *Server) Handler(addr *net.UDPAddr, msg *capture.SocketMSG) (*capture.SocketMSG, error) {
|
|
||||||
srv.ws.SendAll(&websocket.Message{
|
|
||||||
Subject: "wifictld_pkg",
|
|
||||||
Body: map[string]interface{}{
|
|
||||||
"ip": addr.IP,
|
|
||||||
"msg": msg,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return msg, nil
|
|
||||||
}
|
|
||||||
func (srv *Server) Start() {
|
|
||||||
// service connections
|
|
||||||
if err := srv.web.ListenAndServe(); err != http.ErrServerClosed {
|
|
||||||
log.Panicf("webserver crashed: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (srv *Server) Close() {
|
|
||||||
srv.web.Close()
|
|
||||||
srv.ws.Close()
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package web
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestWebserver(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
srv := New(&Config{
|
|
||||||
Bind: ":12345",
|
|
||||||
Webroot: "/tmp",
|
|
||||||
})
|
|
||||||
assert.NotNil(srv)
|
|
||||||
|
|
||||||
go srv.Start()
|
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * 200)
|
|
||||||
|
|
||||||
assert.Panics(func() {
|
|
||||||
srv.Start()
|
|
||||||
}, "not allowed to listen twice")
|
|
||||||
|
|
||||||
srv.Close()
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
wget -O vanilla-framework.css https://assets.ubuntu.com/v1/vanilla-framework-version-1.8.1.min.css
|
|
||||||
wget -O vue.js https://vuejs.org/js/vue.min.js
|
|
||||||
wget -O vue-route.js https://unpkg.com/vue-router/dist/vue-router.js
|
|
||||||
wget -O vuex.js https://unpkg.com/vuex@2.0.0/dist/vuex.min.js
|
|
||||||
wget -O vue-websocket.js https://raw.githubusercontent.com/nathantsoi/vue-native-websocket/master/dist/build.js
|
|
||||||
wget -O vue-websocket.js.map https://raw.githubusercontent.com/nathantsoi/vue-native-websocket/master/dist/build.js.map
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
11907
webroot/assets/vue.js
11907
webroot/assets/vue.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -1,10 +0,0 @@
|
||||||
.p-navigation__logo {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
line-height: 3rem;
|
|
||||||
}
|
|
||||||
.p-navigation .p-navigation__logo .p-navigation__link {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
.p-navigation .p-navigation__logo.online .p-navigation__link {
|
|
||||||
color: black;
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8"/>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
||||||
<title>Wifictld</title>
|
|
||||||
<link rel="stylesheet" href="assets/vanilla-framework.css" />
|
|
||||||
<link rel="stylesheet" href="css/main.css" />
|
|
||||||
<script src="assets/vue.js"></script>
|
|
||||||
<script src="assets/vue-route.js"></script>
|
|
||||||
<script src="assets/vuex.js"></script>
|
|
||||||
<script src="assets/vue-websocket.js"></script>
|
|
||||||
<script src="js/utils.js"></script>
|
|
||||||
</head>
|
|
||||||
<body role="document">
|
|
||||||
<div id="app">
|
|
||||||
<header id="navigation" class="p-navigation">
|
|
||||||
<div class="p-navigation__banner">
|
|
||||||
<navbar-logo></navbar-logo>
|
|
||||||
<a href="#navigation" class="p-navigation__toggle--open" title="menu">Menu</a>
|
|
||||||
<a href="#navigation-closed" class="p-navigation__toggle--close" title="close menu">Close menu</a>
|
|
||||||
</div>
|
|
||||||
<nav class="p-navigation__nav" role="menubar">
|
|
||||||
<span class="u-off-screen">
|
|
||||||
<a href="#main-content">Jump to main content</a>
|
|
||||||
</span>
|
|
||||||
<ul class="p-navigation__links" role="menu">
|
|
||||||
<router-link :to="{name: 'aps' }" tag="li" class="p-navigation__link" active-class="is-selected">
|
|
||||||
<a>Access Points</a>
|
|
||||||
</router-link>
|
|
||||||
<router-link :to="{name: 'clients' }" tag="li" class="p-navigation__link" active-class="is-selected">
|
|
||||||
<a>Clients</a>
|
|
||||||
</router-link>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
<div class="content">
|
|
||||||
<router-view></router-view>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script src="js/view/accesspoint.js"></script>
|
|
||||||
<script src="js/view/clients.js"></script>
|
|
||||||
<script src="js/store.js"></script>
|
|
||||||
<script src="js/view.js"></script>
|
|
||||||
<script src="js/data.js"></script>
|
|
||||||
<script src="js/main.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,32 +0,0 @@
|
||||||
store.commit('setEvent', {
|
|
||||||
subject:"wifictld_pkg",
|
|
||||||
callback: (state, msg) => {
|
|
||||||
|
|
||||||
// add to ap list
|
|
||||||
const ip = msg.body.ip;
|
|
||||||
if(state.controller._ap[ip] === undefined){
|
|
||||||
state.controller.ap.push(ip)
|
|
||||||
state.controller._ap[ip] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add clients
|
|
||||||
const client = msg.body.msg.client;
|
|
||||||
if (state.controller.clients[client.addr] === undefined) {
|
|
||||||
state.controller._clients.push(client.addr)
|
|
||||||
}
|
|
||||||
client.ap = ip;
|
|
||||||
state.controller.clients[client.addr] = client;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
VueNativeSock.default.install(Vue, `//${location.host}${location.pathname}ws`, {
|
|
||||||
store: store,
|
|
||||||
reconnection: true,
|
|
||||||
reconnectionDelay: 5000,
|
|
||||||
format: 'json',
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
getJSON(`//${location.host}${location.pathname}data.json`).then(function(data){
|
|
||||||
store.commit('initData',data);
|
|
||||||
})
|
|
|
@ -1,6 +0,0 @@
|
||||||
const app = new Vue({
|
|
||||||
el: '#app',
|
|
||||||
store,
|
|
||||||
router,
|
|
||||||
components: { NavbarLogo },
|
|
||||||
})
|
|
|
@ -1,118 +0,0 @@
|
||||||
|
|
||||||
function newUUID () {
|
|
||||||
/* eslint-disable */
|
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
||||||
const r = Math.random() * 16 | 0,
|
|
||||||
v = c === 'x' ? r : r & 0x3 | 0x8;
|
|
||||||
return v.toString(16);
|
|
||||||
});
|
|
||||||
/* eslint-enable */
|
|
||||||
}
|
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
|
||||||
state: {
|
|
||||||
socket: {
|
|
||||||
_session: localStorage.getItem('session'),
|
|
||||||
isConnected: false,
|
|
||||||
reconnectError: false,
|
|
||||||
eventMSGID: {},
|
|
||||||
eventTo: {},
|
|
||||||
},
|
|
||||||
controller: {
|
|
||||||
_ap : {},
|
|
||||||
ap: [],
|
|
||||||
_clients: [],
|
|
||||||
clients: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
mutations:{
|
|
||||||
SOCKET_ONOPEN (state, event) {
|
|
||||||
state.socket.isConnected = true
|
|
||||||
},
|
|
||||||
SOCKET_ONCLOSE (state, event) {
|
|
||||||
state.socket.isConnected = false
|
|
||||||
},
|
|
||||||
SOCKET_ONERROR (state, event) {
|
|
||||||
console.error(state, event)
|
|
||||||
},
|
|
||||||
// default handler called for all methods
|
|
||||||
SOCKET_ONMESSAGE (state, msg) {
|
|
||||||
if (msg.subject === 'session_init') {
|
|
||||||
if (state.socket._session === null) {
|
|
||||||
state.socket._session = newUUID();
|
|
||||||
localStorage.setItem('session', state.socket._session);
|
|
||||||
}
|
|
||||||
msg.id = state.socket._session;
|
|
||||||
Vue.prototype.$socket.sendObj(msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const msgFunc = state.socket.eventMSGID[msg.id];
|
|
||||||
if (msgFunc) {
|
|
||||||
msgFunc(state, msg);
|
|
||||||
delete state.socket.eventMSGID[msg.id];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventFuncs = state.socket.eventTo[msg.subject];
|
|
||||||
if (typeof eventFuncs === 'object' && eventFuncs.length > 0) {
|
|
||||||
for (const key in eventFuncs) {
|
|
||||||
const func = eventFuncs[key];
|
|
||||||
if (func) {
|
|
||||||
func(state, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(`unable to identify message: ${msg.subject}`);
|
|
||||||
|
|
||||||
},
|
|
||||||
// mutations for reconnect methods
|
|
||||||
SOCKET_RECONNECT(state, count) {
|
|
||||||
console.info(state, "reconnect:", count)
|
|
||||||
},
|
|
||||||
SOCKET_RECONNECT_ERROR(state) {
|
|
||||||
state.socket.reconnectError = true;
|
|
||||||
},
|
|
||||||
setEvent (state, data) {
|
|
||||||
state.socket.eventTo[data.subject] = [data.callback];
|
|
||||||
},
|
|
||||||
addEvent (state, data) {
|
|
||||||
if (typeof state.socket.eventTo[data.subject] !== 'object') {
|
|
||||||
state.socket.eventTo[data.subject] = [];
|
|
||||||
}
|
|
||||||
state.socket.eventTo[data.subject].push(data.callback);
|
|
||||||
},
|
|
||||||
delEvent (state, data) {
|
|
||||||
if (typeof state.socket.eventTo[data.subject] === 'object' && state.socket.eventTo[data.subject].length > 1) {
|
|
||||||
state.socket.eventTo[data.subject].pop(data.callback);
|
|
||||||
} else {
|
|
||||||
state.socket.eventTo[data.subject] = [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
call (state, data) {
|
|
||||||
if (!data.msg.id) {
|
|
||||||
data.msg.id = newUUID();
|
|
||||||
}
|
|
||||||
const ret = Vue.prototype.$socket.sendObj(data.msg);
|
|
||||||
if (typeof data.callback === 'function') {
|
|
||||||
state.socket.eventMSGID[data.msg.id] = data.callback;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
},
|
|
||||||
initData (state, data) {
|
|
||||||
data.aps.forEach((ap) => {
|
|
||||||
if(state.controller._ap[ap.ip] === undefined){
|
|
||||||
state.controller.ap.push(ap.ip)
|
|
||||||
state.controller._ap[ap.ip] = null;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
data.clients.forEach((client) => {
|
|
||||||
if (state.controller.clients[client.addr] === undefined) {
|
|
||||||
state.controller._clients.push(client.addr)
|
|
||||||
}
|
|
||||||
state.controller.clients[client.addr] = client;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
|
@ -1,23 +0,0 @@
|
||||||
function get(url) {
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
var req = new XMLHttpRequest();
|
|
||||||
req.open('GET', url);
|
|
||||||
|
|
||||||
req.onload = function onload() {
|
|
||||||
if (req.status === 200) {
|
|
||||||
resolve(req.response);
|
|
||||||
} else {
|
|
||||||
reject(Error(req.statusText));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
req.onerror = function onerror() {
|
|
||||||
reject(Error('Network Error'));
|
|
||||||
};
|
|
||||||
|
|
||||||
req.send();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function getJSON(url) {
|
|
||||||
return get(url).then(JSON.parse);
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
const NavbarLogo = {
|
|
||||||
template: `<div class="p-navigation__logo" v-bind:class="{ online: isOnline }">
|
|
||||||
<a class="p-navigation__link" href="/">Wifictld</a>
|
|
||||||
</div>`,
|
|
||||||
computed: {
|
|
||||||
isOnline () {
|
|
||||||
return this.$store.state.socket.isConnected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const router = new VueRouter({
|
|
||||||
store,
|
|
||||||
routes: [
|
|
||||||
{ path: '/ap', component: ViewAccessPoints, name: "aps" },
|
|
||||||
{ path: '/ap/clients/:ip', component: ViewClients, name: "ap.clients"},
|
|
||||||
{ path: '/clients', component: ViewClients, name: "clients" },
|
|
||||||
{ path: '/', redirect: '/ap' }
|
|
||||||
]
|
|
||||||
})
|
|
|
@ -1,37 +0,0 @@
|
||||||
const ViewAccessPoints = {
|
|
||||||
template: `<div class="row">
|
|
||||||
<p><span class="p-heading--one">AccessPoints</span></p>
|
|
||||||
<table class="p-table--mobile-card" role="grid">
|
|
||||||
<thead>
|
|
||||||
<tr role="row">
|
|
||||||
<th scope="col" role="columnheader" aria-sort="none">IP</th>
|
|
||||||
<th scope="col" role="columnheader" aria-sort="none" class="u-align--right">Users</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr role="row" v-for="item in getAPs">
|
|
||||||
<td role="rowheader" aria-label="IP">{{ item.ip.substring(21) }}</td>
|
|
||||||
<td role="gridcell" aria-label="Users" class="u-align--right">
|
|
||||||
<router-link :to="{ name: 'ap.clients', params: { ip: item.ip }}">
|
|
||||||
{{ item.clients }}
|
|
||||||
</router-link>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>`,
|
|
||||||
computed: {
|
|
||||||
getAPs () {
|
|
||||||
const state = this.$store.state;
|
|
||||||
return state.controller.ap.map(function (ip) {
|
|
||||||
return {
|
|
||||||
ip: ip,
|
|
||||||
clients: state.controller._clients.filter(function (hwaddr) {
|
|
||||||
const client = state.controller.clients[hwaddr];
|
|
||||||
return client.ap === ip;
|
|
||||||
}).length,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
const ViewClients = {
|
|
||||||
template: `<div class="row">
|
|
||||||
<p>
|
|
||||||
<span class="p-heading--one">Clients</span>
|
|
||||||
<sub v-if="$route.params.ip">of Router {{ $route.params.ip.substring(21) }}</sub>
|
|
||||||
</p>
|
|
||||||
<table class="p-table--mobile-card" role="grid">
|
|
||||||
<thead>
|
|
||||||
<tr role="row">
|
|
||||||
<th scope="col" role="columnheader" aria-sort="none">Addr</th>
|
|
||||||
<th scope="col" role="columnheader" aria-sort="none">AP</th>
|
|
||||||
<th scope="col" role="columnheader" aria-sort="none" class="u-align--right">FreqHighs</th>
|
|
||||||
<th scope="col" role="columnheader" aria-sort="none" class="u-align--right">SignalLow</th>
|
|
||||||
<th scope="col" role="columnheader" aria-sort="none" class="u-align--right">SignalHigh</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr role="row" v-for="item in getClients">
|
|
||||||
<td role="rowheader" aria-label="Addr">{{ item.addr.substring(12) }}</td>
|
|
||||||
<td role="gridcell" aria-label="AP">
|
|
||||||
<router-link :to="{ name: 'ap.clients', params: { ip: item.ap }}">
|
|
||||||
{{ item.ap.substring(21) }}
|
|
||||||
</router-link>
|
|
||||||
</td>
|
|
||||||
<td role="gridcell" aria-label="Freq Highs" class="u-align--right">{{ item.freq_highest }}</td>
|
|
||||||
<td role="gridcell" aria-label="Signal LowF" class="u-align--right">{{ item.signal_low_freq }}</td>
|
|
||||||
<td role="gridcell" aria-label="Signal HighF" class="u-align--right">{{ item.signal_high_freq }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>`,
|
|
||||||
computed: {
|
|
||||||
getClients () {
|
|
||||||
const state = this.$store.state,
|
|
||||||
apIP = this.$route.params.ip;
|
|
||||||
|
|
||||||
return state.controller._clients.map(function(addr) {
|
|
||||||
return state.controller.clients[addr];
|
|
||||||
}).filter((client)=> (apIP === undefined || client.ap === apIP));
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue