├── .gitignore ├── README.md ├── web ├── index.html └── index.js ├── relay.go ├── bluetooth_plugin.go └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | src 2 | pkg 3 | bin 4 | main 5 | bluetooth_plugin 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scyther 2 | 3 | Native where native doesn't work 4 | 5 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Scyther Test 4 | 5 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /relay.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/gorilla/websocket" 6 | "log" 7 | "net/rpc" 8 | ) 9 | 10 | type Args struct{} 11 | 12 | type BLAdvertisement struct { 13 | ID string 14 | Name string 15 | LocalName string 16 | TxPowerLevel int 17 | ManufacturerData []byte 18 | } 19 | 20 | func relay(ws *websocket.Conn, message string, messageType int) { 21 | log.Printf("Relaying data\n") 22 | res := ScytherMessage{"response", "Bad"} 23 | ret, _ := json.Marshal(&res) 24 | err := ws.WriteMessage(messageType, ret) 25 | if err != nil { 26 | log.Println("write:", err) 27 | } 28 | } 29 | 30 | func get_privilege(ws *websocket.Conn, message string, messageType int, rpcClient *rpc.Client) { 31 | log.Printf("Getting privileges\n") 32 | reply := new([]BLAdvertisement) 33 | args := Args{} 34 | err := rpcClient.Call("Bluetooth.Peripheral", args, &reply) 35 | // log.Println(*reply) 36 | d, _ := json.Marshal(reply) 37 | n := len(d) 38 | res := ScytherMessage{"response", string(d[:n])} 39 | d, _ = json.Marshal(res) 40 | err = ws.WriteMessage(messageType, d) 41 | if err != nil { 42 | log.Println("write:", err) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bluetooth_plugin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "net/rpc" 8 | 9 | "github.com/paypal/gatt" 10 | "github.com/paypal/gatt/examples/option" 11 | ) 12 | 13 | type Args struct{} 14 | type BLAdvertisement struct { 15 | ID string 16 | Name string 17 | LocalName string 18 | TxPowerLevel int 19 | ManufacturerData []byte 20 | } 21 | type Bluetooth int 22 | 23 | var blAds []BLAdvertisement 24 | 25 | func (t *Bluetooth) Peripheral(args *Args, reply *[]BLAdvertisement) error { 26 | *reply = blAds 27 | return nil 28 | } 29 | 30 | func onStateChanged(d gatt.Device, s gatt.State) { 31 | fmt.Println("State:", s) 32 | switch s { 33 | case gatt.StatePoweredOn: 34 | fmt.Println("scanning...") 35 | d.Scan([]gatt.UUID{}, false) 36 | return 37 | default: 38 | d.StopScanning() 39 | } 40 | } 41 | 42 | func onPeriphDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) { 43 | blAd := BLAdvertisement{p.ID(), p.Name(), a.LocalName, a.TxPowerLevel, a.ManufacturerData} 44 | blAds = append(blAds, blAd) 45 | fmt.Printf("\nPeripheral ID:%s, NAME:(%s)\n", p.ID(), p.Name()) 46 | fmt.Println(" Local Name =", a.LocalName) 47 | fmt.Println(" TX Power Level =", a.TxPowerLevel) 48 | fmt.Println(" Manufacturer Data =", a.ManufacturerData) 49 | fmt.Println(" Service Data =", a.ServiceData) 50 | } 51 | 52 | func main() { 53 | blAds = make([]BLAdvertisement, 0, 20) 54 | d, err := gatt.NewDevice(option.DefaultClientOptions...) 55 | if err != nil { 56 | log.Fatalf("Failed to open device, err: %s\n", err) 57 | return 58 | } 59 | // Register handlers. 60 | d.Handle(gatt.PeripheralDiscovered(onPeriphDiscovered)) 61 | d.Init(onStateChanged) 62 | 63 | bl := new(Bluetooth) 64 | rpc.Register(bl) 65 | rpc.HandleHTTP() 66 | l, e := net.Listen("tcp", ":13922") 67 | if e != nil { 68 | log.Fatal("listen error:", e) 69 | } 70 | go rpc.Accept(l) 71 | fmt.Println("loaded bluetooth plugin") 72 | 73 | select {} 74 | } 75 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "flag" 6 | "github.com/gorilla/websocket" 7 | "log" 8 | "net/http" 9 | "net/rpc" 10 | ) 11 | 12 | type ScytherMessage struct { 13 | Type string `json:"type"` 14 | Value string `json:"value"` 15 | } 16 | 17 | var HttpAddr = flag.String("http", ":13921", "Host:Port") 18 | var RcpAddr = flag.String("rcp", ":13922", "RcpHost:Port") 19 | 20 | var upgrader = websocket.Upgrader{ 21 | ReadBufferSize: 1024, 22 | WriteBufferSize: 1024, 23 | CheckOrigin: func(r *http.Request) bool { 24 | return true 25 | }, 26 | } 27 | 28 | func authorize(w http.ResponseWriter, r *http.Request) { 29 | var rpcClient *rpc.Client 30 | isConnected := false 31 | conn, err := upgrader.Upgrade(w, r, nil) 32 | if err != nil { 33 | log.Println(err) 34 | return 35 | } 36 | defer conn.Close() 37 | for { 38 | mt, message, err := conn.ReadMessage() 39 | if err != nil { 40 | log.Println("read:", err) 41 | break 42 | } 43 | 44 | res := ScytherMessage{} 45 | _ = json.Unmarshal(message, &res) 46 | log.Printf("Received message type %s, with data %s\n", res.Type, res.Value) 47 | 48 | if res.Type == "handshake" && res.Value == "hello scyther native" { 49 | isConnected = true 50 | data := ScytherMessage{"handshake", "hello scyther web"} 51 | ret, _ := json.Marshal(&data) 52 | err = conn.WriteMessage(mt, ret) 53 | if err != nil { 54 | log.Println("write:", err) 55 | } 56 | continue 57 | } 58 | 59 | if isConnected { 60 | log.Printf("I am connected %s", res.Type) 61 | switch res.Type { 62 | case "request": 63 | go relay(conn, res.Value, mt) 64 | case "privilege": 65 | rpcClient, err = rpc.Dial("tcp", *RcpAddr) 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | go get_privilege(conn, res.Value, mt, rpcClient) 70 | default: 71 | data := ScytherMessage{"response", "No method implemented"} 72 | ret, _ := json.Marshal(&data) 73 | err = conn.WriteMessage(mt, ret) 74 | if err != nil { 75 | log.Println("write:", err) 76 | } 77 | } 78 | } else { 79 | data := ScytherMessage{"response", "Unauthorized"} 80 | ret, _ := json.Marshal(&data) 81 | err = conn.WriteMessage(mt, ret) 82 | if err != nil { 83 | log.Println("write:", err) 84 | } 85 | } 86 | } 87 | } 88 | 89 | func main() { 90 | flag.Parse() 91 | http.HandleFunc("/auth", authorize) 92 | log.Printf("Server started on %s\n", *HttpAddr) 93 | log.Fatal(http.ListenAndServe(*HttpAddr, nil)) 94 | } 95 | -------------------------------------------------------------------------------- /web/index.js: -------------------------------------------------------------------------------- 1 | var nativeSocket; 2 | 3 | try { 4 | nativeSocket = new WebSocket('ws://localhost:13921/auth') 5 | } catch (e) { 6 | console.log("Error: ", err); 7 | } 8 | 9 | var scytherReady = false; 10 | 11 | var handshake = function() { 12 | nativeSocket.send(JSON.stringify({ 13 | type: 'handshake', 14 | value: 'hello scyther native' 15 | })); 16 | }; 17 | 18 | var validateHandshake = function(message) { 19 | if (message.type === 'handshake' && message.value === 'hello scyther web') { 20 | // We have a Scyther server running, yay! 21 | scytherReady = true; 22 | console.log('Scyther is ready! \\m/') 23 | } else { 24 | console.log('Not a Scyther server :('); 25 | } 26 | }; 27 | 28 | var populateDiscovered = function(message) { 29 | message = JSON.parse(message); 30 | var tagTotal = ""; 31 | var deviceData = {}; 32 | for (var bt of message) { 33 | deviceData[bt.ID] = bt.ManufacturerData; 34 | } 35 | for (var bt in deviceData) { 36 | tagTotal += "" + bt + "" + deviceData[bt] + ""; 37 | } 38 | document.getElementById("placeholder").innerHTML = "" + tagTotal + "
Device IDManufacturer Data
"; 39 | }; 40 | 41 | var handleMessage = function(message) { 42 | message = JSON.parse(message.data); 43 | // console.log(message); 44 | switch(message.type) { 45 | case 'handshake': 46 | validateHandshake(message); 47 | break; 48 | case 'response': 49 | populateDiscovered(message.value); 50 | break; 51 | default: 52 | console.log('Unsupported message'); 53 | } 54 | } 55 | 56 | nativeSocket.addEventListener('open', function() { 57 | // ready to start handshake 58 | handshake(); 59 | }); 60 | 61 | nativeSocket.addEventListener('error', function(err) { 62 | // oops! something went wrong 63 | scytherReady = false; 64 | console.log("Error: ", err); 65 | }); 66 | 67 | nativeSocket.addEventListener('close', function() { 68 | // server closed the connection 69 | scytherReady = false; 70 | console.log("Connection closed") 71 | }); 72 | 73 | nativeSocket.addEventListener('message', function(msg) { 74 | // received a message 75 | handleMessage(msg); 76 | }); 77 | 78 | var btn = document.getElementById("discover"); 79 | btn.addEventListener('click', function() { 80 | nativeSocket.send(JSON.stringify({type: "privilege", value: ""})); 81 | }); 82 | --------------------------------------------------------------------------------