Compare commits
13 Commits
Author | SHA1 | Date |
---|---|---|
|
f07298c40c | |
|
fca7f5353c | |
|
35e6446036 | |
|
3b98fb36cb | |
|
39ff9a43b0 | |
|
16fdeb85d4 | |
|
d20e749038 | |
|
948fa0ddec | |
|
bfb3da9338 | |
|
40ea97bc0c | |
|
4643af5259 | |
|
17ac739753 | |
|
e7499dbae5 |
|
@ -0,0 +1,8 @@
|
|||
#!/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
|
|
@ -0,0 +1,25 @@
|
|||
#!/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")
|
34
.drone.yml
34
.drone.yml
|
@ -1,34 +0,0 @@
|
|||
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
|
|
@ -0,0 +1,53 @@
|
|||
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
|
|
@ -0,0 +1,47 @@
|
|||
# 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,20 +3,19 @@ package capture
|
|||
import (
|
||||
"net"
|
||||
|
||||
"dev.sum7.eu/wifictld/analyzer/data"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/bdlm/log"
|
||||
)
|
||||
|
||||
//Collector for capture
|
||||
type Collector struct {
|
||||
connections map[string]*net.UDPConn
|
||||
handler data.Handler
|
||||
handler Handler
|
||||
queue chan *Packet
|
||||
stop chan interface{}
|
||||
}
|
||||
|
||||
// NewCollector creates a Collector struct
|
||||
func NewCollector(handler data.Handler, ifaces []IFaceConfig) *Collector {
|
||||
func NewCollector(handler Handler, ifaces []*IFaceConfig) *Collector {
|
||||
|
||||
coll := &Collector{
|
||||
handler: handler,
|
||||
|
@ -26,6 +25,12 @@ func NewCollector(handler data.Handler, ifaces []IFaceConfig) *Collector {
|
|||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
if iface.Port == 0 {
|
||||
iface.Port = Port
|
||||
}
|
||||
if iface.IPAddress == "" {
|
||||
iface.IPAddress = MulticastAddressDefault
|
||||
}
|
||||
coll.listenUDP(iface)
|
||||
}
|
||||
|
||||
|
@ -43,7 +48,7 @@ func (coll *Collector) Close() {
|
|||
close(coll.queue)
|
||||
}
|
||||
|
||||
func (coll *Collector) listenUDP(iface IFaceConfig) {
|
||||
func (coll *Collector) listenUDP(iface *IFaceConfig) {
|
||||
ip := net.ParseIP(iface.IPAddress)
|
||||
var conn *net.UDPConn
|
||||
var err error
|
||||
|
@ -90,7 +95,7 @@ type Packet struct {
|
|||
|
||||
func (coll *Collector) parser() {
|
||||
for obj := range coll.queue {
|
||||
msg, err := data.NewSocketMSG(obj.Raw)
|
||||
msg, err := NewSocketMSG(obj.Raw)
|
||||
if err != nil {
|
||||
log.Warnf("unable to unmarshal request from %s: %s", obj.Address.String(), err)
|
||||
continue
|
||||
|
@ -108,7 +113,7 @@ func (coll *Collector) parser() {
|
|||
}
|
||||
|
||||
// SendTo a specifical address
|
||||
func (coll *Collector) SendTo(addr *net.UDPAddr, msg *data.SocketMSG) {
|
||||
func (coll *Collector) SendTo(addr *net.UDPAddr, msg *SocketMSG) {
|
||||
log.Debugf("send[%s]: %s", addr, msg.String())
|
||||
data, err := msg.Marshal()
|
||||
if err != nil {
|
||||
|
@ -126,7 +131,7 @@ func (coll *Collector) SendTo(addr *net.UDPAddr, msg *data.SocketMSG) {
|
|||
}
|
||||
|
||||
// Send to every connection to default address
|
||||
func (coll *Collector) Send(msg *data.SocketMSG) {
|
||||
func (coll *Collector) Send(msg *SocketMSG) {
|
||||
log.Debugf("send: %s", msg.String())
|
||||
data, err := msg.Marshal()
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
package capture
|
|
@ -1,4 +1,4 @@
|
|||
package data
|
||||
package capture
|
||||
|
||||
import "net"
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
package data
|
||||
package capture
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/bdlm/log"
|
||||
|
||||
"dev.sum7.eu/wifictld/wifictld-analyzer/data"
|
||||
)
|
||||
|
||||
var DEBUG = false
|
||||
|
@ -23,16 +24,15 @@ const (
|
|||
|
||||
func (a SocketMSGType) Is(b SocketMSGType) bool {
|
||||
if DEBUG {
|
||||
|
||||
log.Debugf("SocketType: %x & %x = %x -> %b", a, b, (a & b), (a&b) > 0)
|
||||
log.Debugf("SocketType: %x & %x = %x -> %t", a, b, (a & b), (a&b) > 0)
|
||||
}
|
||||
return (a & b) > 0
|
||||
}
|
||||
|
||||
// SocketMSG package of wifictld format
|
||||
type SocketMSG struct {
|
||||
Types SocketMSGType
|
||||
Client *WifiClient
|
||||
Types SocketMSGType `json:"types"`
|
||||
Client *WifiClient `json:"client,omitempty"`
|
||||
}
|
||||
|
||||
func NewSocketMSG(obj []byte) (*SocketMSG, error) {
|
||||
|
@ -100,7 +100,7 @@ func (msg *SocketMSG) Unmarshal(obj []byte) error {
|
|||
|
||||
if msg.Types.Is(SocketMSGTypeClient) {
|
||||
msg.Client = &WifiClient{
|
||||
Addr: net.HardwareAddr(obj[pos:(pos + 6)]),
|
||||
Addr: data.HardwareAddr(obj[pos:(pos + 6)]),
|
||||
Time: time.Unix(int64(binary.BigEndian.Uint32(obj[(pos+6):(pos+10)])), 0),
|
||||
TryProbe: binary.BigEndian.Uint16(obj[(pos + 10):(pos + 12)]),
|
||||
TryAuth: binary.BigEndian.Uint16(obj[(pos + 12):(pos + 14)]),
|
|
@ -1,14 +1,21 @@
|
|||
package data
|
||||
package capture
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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) {
|
||||
assert := assert.New(t)
|
||||
types := SocketMSGType(5)
|
|
@ -0,0 +1,20 @@
|
|||
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,50 +1,67 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"dev.sum7.eu/genofire/golang-lib/database"
|
||||
"dev.sum7.eu/genofire/golang-lib/file"
|
||||
"github.com/bdlm/log"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"dev.sum7.eu/wifictld/analyzer/capture"
|
||||
"dev.sum7.eu/wifictld/analyzer/controller"
|
||||
"dev.sum7.eu/wifictld/analyzer/database"
|
||||
"dev.sum7.eu/wifictld/wifictld-analyzer/capture"
|
||||
"dev.sum7.eu/wifictld/wifictld-analyzer/controller"
|
||||
"dev.sum7.eu/wifictld/wifictld-analyzer/web"
|
||||
)
|
||||
|
||||
var (
|
||||
central bool
|
||||
)
|
||||
type ControllerConfig struct {
|
||||
Database database.Config `toml:"database"`
|
||||
Answer bool `toml:"answer"`
|
||||
Webserver *web.Config `toml:"webserver"`
|
||||
Interfaces []*capture.IFaceConfig `toml:"interfaces"`
|
||||
}
|
||||
|
||||
// queryCmd represents the query command
|
||||
var controllerCmd = &cobra.Command{
|
||||
Use: "controller <interfaces>",
|
||||
Short: "simulate a wifictld controller",
|
||||
Example: `analyzer controller "eth0,wlan0"`,
|
||||
Example: `analyzer controller "/etc/wifictld.conf"`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
ifaces := strings.Split(args[0], ",")
|
||||
config := &ControllerConfig{}
|
||||
|
||||
log.Infof("listen on: %s", ifaces)
|
||||
file.ReadTOML(args[0], config)
|
||||
|
||||
var ifacesConfigs []capture.IFaceConfig
|
||||
for _, iface := range ifaces {
|
||||
ifaceConfig := capture.IFaceConfig{
|
||||
InterfaceName: iface,
|
||||
Port: port,
|
||||
IPAddress: ipAddress,
|
||||
}
|
||||
ifacesConfigs = append(ifacesConfigs, ifaceConfig)
|
||||
if err := database.Open(config.Database); err != nil {
|
||||
log.Panicf("no database connection: %s", err)
|
||||
}
|
||||
defer database.Close()
|
||||
|
||||
db := database.NewDB()
|
||||
|
||||
ctr := controller.NewController(db, central)
|
||||
ctr := controller.NewController()
|
||||
defer ctr.Close()
|
||||
|
||||
coll := capture.NewCollector(ctr.Handler, ifacesConfigs)
|
||||
var handlers []capture.Handler
|
||||
|
||||
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()
|
||||
|
||||
ctr.Send = coll.Send
|
||||
|
@ -60,8 +77,5 @@ var controllerCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
func init() {
|
||||
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, "")
|
||||
RootCMD.AddCommand(controllerCmd)
|
||||
}
|
||||
|
|
15
cmd/dump.go
15
cmd/dump.go
|
@ -7,11 +7,10 @@ import (
|
|||
"strings"
|
||||
"syscall"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/bdlm/log"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"dev.sum7.eu/wifictld/analyzer/capture"
|
||||
"dev.sum7.eu/wifictld/analyzer/data"
|
||||
"dev.sum7.eu/wifictld/wifictld-analyzer/capture"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -30,9 +29,9 @@ var dumpCmd = &cobra.Command{
|
|||
|
||||
log.Infof("listen on: %s", ifaces)
|
||||
|
||||
var ifacesConfigs []capture.IFaceConfig
|
||||
var ifacesConfigs []*capture.IFaceConfig
|
||||
for _, iface := range ifaces {
|
||||
ifaceConfig := capture.IFaceConfig{
|
||||
ifaceConfig := &capture.IFaceConfig{
|
||||
InterfaceName: iface,
|
||||
Port: port,
|
||||
IPAddress: ipAddress,
|
||||
|
@ -40,9 +39,9 @@ var dumpCmd = &cobra.Command{
|
|||
ifacesConfigs = append(ifacesConfigs, ifaceConfig)
|
||||
}
|
||||
|
||||
data.DEBUG = debug
|
||||
capture.DEBUG = debug
|
||||
|
||||
coll := capture.NewCollector(func(addr *net.UDPAddr, msg *data.SocketMSG) (*data.SocketMSG, error) {
|
||||
coll := capture.NewCollector(func(addr *net.UDPAddr, msg *capture.SocketMSG) (*capture.SocketMSG, error) {
|
||||
log.Infof("recv[%s]: %s", addr, msg.String())
|
||||
return nil, nil
|
||||
}, ifacesConfigs)
|
||||
|
@ -58,7 +57,7 @@ var dumpCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
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().StringVar(&ipAddress, "listen", capture.MulticastAddressDefault, "")
|
||||
}
|
||||
|
|
21
cmd/root.go
21
cmd/root.go
|
@ -1,24 +1,27 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/bdlm/log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var debug bool
|
||||
var (
|
||||
debug bool
|
||||
timestamps bool
|
||||
)
|
||||
|
||||
// RootCmd represents the base command when called without any subcommands
|
||||
var RootCmd = &cobra.Command{
|
||||
// RootCMD represents the base command when called without any subcommands
|
||||
var RootCMD = &cobra.Command{
|
||||
Use: "analyzer",
|
||||
Short: "wifictld analyzer",
|
||||
Long: `capture wifictld traffic and display thus`,
|
||||
}
|
||||
|
||||
// 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() {
|
||||
if err := RootCmd.Execute(); err != nil {
|
||||
if err := RootCMD.Execute(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +30,11 @@ func init() {
|
|||
if debug {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
log.SetFormatter(&log.TextFormatter{
|
||||
DisableTimestamp: timestamps,
|
||||
})
|
||||
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")
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
package cmd
|
|
@ -0,0 +1,18 @@
|
|||
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,27 +2,41 @@ package controller
|
|||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
// log "github.com/sirupsen/logrus"
|
||||
// "github.com/bdlm/log"
|
||||
"dev.sum7.eu/genofire/golang-lib/database"
|
||||
|
||||
"dev.sum7.eu/wifictld/analyzer/data"
|
||||
"dev.sum7.eu/wifictld/wifictld-analyzer/capture"
|
||||
"dev.sum7.eu/wifictld/wifictld-analyzer/data"
|
||||
)
|
||||
|
||||
func (c *Controller) Handler(addr *net.UDPAddr, msg *data.SocketMSG) (*data.SocketMSG, error) {
|
||||
func (c *Controller) Handler(addr *net.UDPAddr, msg *capture.SocketMSG) (*capture.SocketMSG, error) {
|
||||
ignore := false
|
||||
if msg.Types.Is(data.SocketMSGTypeClient) && msg.Client != nil {
|
||||
ignore = c.db.LearnClient(addr.IP, msg.Client)
|
||||
if msg.Types.Is(capture.SocketMSGTypeClient) && msg.Client != nil {
|
||||
ignore = c.LearnClient(addr.IP, msg.Client)
|
||||
}
|
||||
if !msg.Types.Is(data.SocketMSGTypeRequest) {
|
||||
if !msg.Types.Is(capture.SocketMSGTypeRequest) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
msg = &data.SocketMSG{
|
||||
Types: (data.SocketMSGTypeResponse | data.SocketMSGTypeClient),
|
||||
Client: c.db.GetClient(msg.Client.Addr),
|
||||
client := &data.Client{Addr: msg.Client.Addr}
|
||||
|
||||
if result := database.Read.Select([]string{"try_probe", "try_auth"}).First(client); result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
if c.central || !ignore {
|
||||
msg = &capture.SocketMSG{
|
||||
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 nil, nil
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
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,25 +4,22 @@ import (
|
|||
"net"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"dev.sum7.eu/genofire/golang-lib/database"
|
||||
"github.com/bdlm/log"
|
||||
|
||||
"dev.sum7.eu/wifictld/analyzer/data"
|
||||
"dev.sum7.eu/wifictld/analyzer/database"
|
||||
"dev.sum7.eu/wifictld/wifictld-analyzer/capture"
|
||||
"dev.sum7.eu/wifictld/wifictld-analyzer/data"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
SendTo func(addr *net.UDPAddr, msg *data.SocketMSG)
|
||||
Send func(msg *data.SocketMSG)
|
||||
db *database.DB
|
||||
SendTo func(addr *net.UDPAddr, msg *capture.SocketMSG)
|
||||
Send func(msg *capture.SocketMSG)
|
||||
ticker *time.Ticker
|
||||
central bool
|
||||
}
|
||||
|
||||
func NewController(db *database.DB, central bool) *Controller {
|
||||
func NewController() *Controller {
|
||||
ctl := &Controller{
|
||||
ticker: time.NewTicker(60 * time.Second),
|
||||
db: db,
|
||||
central: central,
|
||||
ticker: time.NewTicker(time.Minute),
|
||||
}
|
||||
go ctl.Repeated()
|
||||
return ctl
|
||||
|
@ -33,7 +30,12 @@ func (c *Controller) Close() {
|
|||
}
|
||||
|
||||
func (c *Controller) Repeated() {
|
||||
aps := 0
|
||||
clients := 0
|
||||
|
||||
for range c.ticker.C {
|
||||
log.Infof("lerned: %d APs, %d Clients", len(c.db.APs), len(c.db.Clients))
|
||||
database.Read.Model(&data.AP{}).Count(&aps)
|
||||
database.Read.Model(&data.Client{}).Count(&clients)
|
||||
log.Debugf("learned: %d APs, %d Clients", aps, clients)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
package controller
|
|
@ -0,0 +1,20 @@
|
|||
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{})
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
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{})
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
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
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package data
|
|
@ -1,30 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
package database
|
||||
|
||||
type AP struct {
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
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
|
||||
|
||||
import "dev.sum7.eu/wifictld/analyzer/cmd"
|
||||
import "dev.sum7.eu/wifictld/wifictld-analyzer/cmd"
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package web
|
||||
|
||||
type Config struct {
|
||||
Enable bool `toml:"enable"`
|
||||
Bind string `toml:"bind"`
|
||||
Webroot string `toml:"webroot"`
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
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()
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
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()
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
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
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,10 @@
|
|||
.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;
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<!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>
|
|
@ -0,0 +1,32 @@
|
|||
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);
|
||||
})
|
|
@ -0,0 +1,6 @@
|
|||
const app = new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
router,
|
||||
components: { NavbarLogo },
|
||||
})
|
|
@ -0,0 +1,118 @@
|
|||
|
||||
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;
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,23 @@
|
|||
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);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
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' }
|
||||
]
|
||||
})
|
|
@ -0,0 +1,37 @@
|
|||
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,
|
||||
};
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
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