Added xdotool modifier key support and refactored server documents
This commit is contained in:
parent
50a46e4c42
commit
9f44a9cffd
@ -3,13 +3,20 @@
|
||||
|
||||
## Always use xdotool
|
||||
|
||||
Some users may always want xdotool, see the [Streaming.md](Streaming.md) file for more information.
|
||||
Some users may always want xdotool, see the [Streaming.md](Streaming.md) file for more information
|
||||
|
||||
``` go
|
||||
--- always use xdotool environment variable
|
||||
|
||||
alwaysUseXdotool, alwaysUseXdotoolExists := os.LookupEnv("KEYBOARD_ALWAYS_USE_XDOTOOL")
|
||||
var alwaysUseXdotool := false
|
||||
alwaysUseXdotoolEnv, alwaysUseXdotoolExists := os.LookupEnv("KEYBOARD_ALWAYS_USE_XDOTOOL")
|
||||
if alwaysUseXdotoolExists {
|
||||
if alwaysUseXdotoolEnv == "true" || alwaysUseXdotoolEnv == "1" {
|
||||
alwaysUseXdotool = true
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
|
||||
## Authentication token file
|
||||
|
6
Makefile
6
Makefile
@ -1,8 +1,10 @@
|
||||
weave:
|
||||
srcweave --formatter srcweave-format --weave docs/ ReadMe.md security/Authentication.md EnvironmentVariables.md Dependencies.md Server.md Streaming.md ThreatModel.md Client.md
|
||||
srcweave --formatter srcweave-format --weave docs/ ReadMe.md security/Authentication.md EnvironmentVariables.md Dependencies.md server/Server.md server/Streaming.md \
|
||||
server/XdotoolCommands.md ThreatModel.md Client.md
|
||||
util/removefencedcode.py
|
||||
tangle:
|
||||
srcweave --formatter srcweave-format --tangle smartkeyboard/ ReadMe.md security/Authentication.md EnvironmentVariables.md Dependencies.md Server.md Streaming.md ThreatModel.md Client.md tools/Tools.md tools/Editor.md tools/Input.md
|
||||
srcweave --formatter srcweave-format --tangle smartkeyboard/ ReadMe.md security/Authentication.md EnvironmentVariables.md Dependencies.md \
|
||||
server/Server.md server/Streaming.md server/Sendkeys.md server/XdotoolCommands.md ThreatModel.md Client.md tools/Tools.md tools/Editor.md tools/Input.md
|
||||
clean:
|
||||
rm -rf docs
|
||||
find smartkeyboard/ -type f -not -name "*_test.go" -delete
|
||||
|
170
Streaming.md
170
Streaming.md
@ -1,170 +0,0 @@
|
||||
# Streaming keyboard input
|
||||
|
||||
We use the Gorilla websocket library to handle the websocket connection.
|
||||
|
||||
Most of the time, we can use sendkeys (which uses libinput) to effeciently press keys. However, if we need to send a character that sendkeys doesn't know about, we can use the xdotool command. xdotool is also useful if one does not want to use root.
|
||||
|
||||
xdotool spawns a new process for each keypress, so it's not as effecient as sendkeys.
|
||||
|
||||
To specify xdotool usage, the client should send a message with the format `{kb_cmd:xdotool}:message` where message is a utf-8 string.
|
||||
|
||||
``` go
|
||||
--- streaming keyboard input
|
||||
|
||||
func clientConnected(w http.ResponseWriter, r *http.Request) {
|
||||
keyboard, err := sendkeys.NewKBWrapWithOptions(sendkeys.Noisy)
|
||||
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Print("upgrade:", err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
// get auth token
|
||||
_, message, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
log.Println("read:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if auth.CheckAuthToken(string(message)) != nil {
|
||||
log.Println("invalid token")
|
||||
c.WriteMessage(websocket.TextMessage, []byte("invalid token"))
|
||||
return
|
||||
}
|
||||
c.WriteMessage(websocket.TextMessage, []byte("authenticated"))
|
||||
|
||||
for {
|
||||
time.Sleep(25 * time.Millisecond)
|
||||
_, message, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
log.Println("read:", err)
|
||||
break
|
||||
}
|
||||
log.Printf("recv: %s", message)
|
||||
message_string := string(message)
|
||||
if message_string == "" {
|
||||
message_string = "\n"
|
||||
}
|
||||
|
||||
@{send keys to system}
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
# Sending the keys
|
||||
|
||||
Sending the keys is a bit tricky as we need to manually convert backspace, tab, enter and modifier keys.
|
||||
|
||||
``` go
|
||||
|
||||
--- send keys to system
|
||||
|
||||
|
||||
// regex if string has characters we need to convert to key presses
|
||||
characterRegex, _ := regexp.Compile(`[^\x08]\x08|\t|\n`)
|
||||
|
||||
doXDoTool := func(command string, keys string)(err error) {
|
||||
var cmd *exec.Cmd
|
||||
if command == "type" {
|
||||
cmd = exec.Command("xdotool", command, "--delay", "25", keys)
|
||||
} else {
|
||||
cmd = exec.Command("xdotool", command, keys)
|
||||
}
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message_string, "{kb_cmd:xdotool}:") {
|
||||
message_string = strings.TrimPrefix(message_string, "{kb_cmd:xdotool}:")
|
||||
if message_string == "" {
|
||||
message_string = "\n"
|
||||
}
|
||||
|
||||
|
||||
if characterRegex.MatchString(message_string) {
|
||||
for _, character := range message_string {
|
||||
charString := string(character)
|
||||
if charString == "\n" {
|
||||
charString = "Enter"
|
||||
} else if charString == "\t" {
|
||||
charString = "Tab"
|
||||
} else if charString == "\b" {
|
||||
charString = "BackSpace"
|
||||
} else{
|
||||
doXDoTool("type", charString)
|
||||
continue
|
||||
}
|
||||
// key is required for special characters
|
||||
err = doXDoTool("key", charString)
|
||||
continue
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
doXDoTool("type", message_string)
|
||||
}
|
||||
continue
|
||||
} else if strings.HasPrefix(message_string, "{kb_cmd:xdotoolk}:") {
|
||||
message_string = strings.TrimPrefix(message_string, "{kb_cmd:xdotoolk}:")
|
||||
if message_string == "" {
|
||||
message_string = "\n"
|
||||
}
|
||||
if characterRegex.MatchString(message_string) {
|
||||
for _, character := range message_string {
|
||||
charString := string(character)
|
||||
if charString == "\n" {
|
||||
charString = "Enter"
|
||||
} else if charString == "\t" {
|
||||
charString = "Tab"
|
||||
} else if charString == "\b" {
|
||||
charString = "BackSpace"
|
||||
} else{
|
||||
doXDoTool("key", charString)
|
||||
continue
|
||||
}
|
||||
// key is required for special characters
|
||||
err = doXDoTool("key", charString)
|
||||
continue
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
doXDoTool("key", message_string)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if characterRegex.MatchString(message_string) {
|
||||
for _, character := range message_string {
|
||||
charString := string(character)
|
||||
if charString == "\n" {
|
||||
keyboard.Enter()
|
||||
continue
|
||||
}
|
||||
if charString == "\t" {
|
||||
keyboard.Tab()
|
||||
continue
|
||||
}
|
||||
if charString == "\b" {
|
||||
keyboard.BackSpace()
|
||||
continue
|
||||
}
|
||||
err = keyboard.Type(charString)
|
||||
if err != nil {
|
||||
log.Println("type:", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
err = keyboard.Type(message_string)
|
||||
if err != nil {
|
||||
log.Println("type:", err)
|
||||
}
|
||||
---
|
||||
```
|
39
server/Sendkeys.md
Normal file
39
server/Sendkeys.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Send keys streaming approach
|
||||
|
||||
When applicable to use send keys, we need to detect Enter, Tab, and BackSpace and use the applicatble functions.
|
||||
|
||||
We test if it meets the characterRegex, and if so we iterate for each character and send the corresponding key.
|
||||
Otherwise we send the entire string at once.
|
||||
|
||||
``` go
|
||||
--- do streaming sendkeys approach
|
||||
if characterRegex.MatchString(message_string) {
|
||||
for _, character := range message_string {
|
||||
charString := string(character)
|
||||
if charString == "\n" {
|
||||
keyboard.Enter()
|
||||
continue
|
||||
}
|
||||
if charString == "\t" {
|
||||
keyboard.Tab()
|
||||
continue
|
||||
}
|
||||
if charString == "\b" {
|
||||
keyboard.BackSpace()
|
||||
continue
|
||||
}
|
||||
err = keyboard.Type(charString)
|
||||
if err != nil {
|
||||
log.Println("type:", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
err = keyboard.Type(message_string)
|
||||
if err != nil {
|
||||
log.Println("type:", err)
|
||||
}
|
||||
---
|
||||
|
||||
```
|
90
server/Streaming.md
Normal file
90
server/Streaming.md
Normal file
@ -0,0 +1,90 @@
|
||||
# Streaming keyboard input
|
||||
|
||||
We use the Gorilla websocket library to handle the websocket connection.
|
||||
|
||||
Most of the time, we can use sendkeys (which uses libinput) to effeciently press keys. However, if we need to send a character that sendkeys doesn't know about, we can use the xdotool command. xdotool is also useful if one does not want to use root.
|
||||
|
||||
xdotool spawns a new process for each keypress, so it's not as effecient as sendkeys.
|
||||
|
||||
To specify xdotool usage, the client should send a message with the format `{kb_cmd:xdotool}:message` where message is a utf-8 string.
|
||||
|
||||
``` go
|
||||
--- streaming keyboard input
|
||||
|
||||
func clientConnected(w http.ResponseWriter, r *http.Request) {
|
||||
// regex if string has characters we need to convert to key presses
|
||||
characterRegex, _ := regexp.Compile(`[^\x08]\x08|\t|\n`)
|
||||
keyboard, err := sendkeys.NewKBWrapWithOptions(sendkeys.Noisy)
|
||||
|
||||
@{always use xdotool environment variable}
|
||||
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
log.Print("upgrade:", err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
// get auth token
|
||||
_, message, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
log.Println("read:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if auth.CheckAuthToken(string(message)) != nil {
|
||||
log.Println("invalid token")
|
||||
c.WriteMessage(websocket.TextMessage, []byte("invalid token"))
|
||||
return
|
||||
}
|
||||
c.WriteMessage(websocket.TextMessage, []byte("authenticated"))
|
||||
|
||||
for {
|
||||
time.Sleep(25 * time.Millisecond)
|
||||
_, message, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
log.Println("read:", err)
|
||||
break
|
||||
}
|
||||
log.Printf("recv: %s", message)
|
||||
message_string := string(message)
|
||||
if message_string == "" {
|
||||
message_string = "\n"
|
||||
}
|
||||
|
||||
@{send keys to system}
|
||||
}
|
||||
}
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
# Sending the keys
|
||||
|
||||
Sending the keys is a bit tricky as we need to manually convert backspace, tab, enter and modifier keys.
|
||||
|
||||
``` go
|
||||
|
||||
--- send keys to system
|
||||
|
||||
|
||||
doXDoTool := func(command string, keys string)(err error) {
|
||||
var cmd *exec.Cmd
|
||||
if command == "type" {
|
||||
cmd = exec.Command("xdotool", command, "--delay", "25", keys)
|
||||
} else {
|
||||
cmd = exec.Command("xdotool", command, keys)
|
||||
}
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
@{handle xdotoool commands}
|
||||
|
||||
@{do streaming sendkeys approach}
|
||||
|
||||
---
|
||||
```
|
54
server/XdotoolCommands.md
Normal file
54
server/XdotoolCommands.md
Normal file
@ -0,0 +1,54 @@
|
||||
# Handle xdotool commands
|
||||
|
||||
Currently the two commands are `type` and `key`. `type` is used to type a character and `key` is used to type a special character like `Enter` or `Backspace`.
|
||||
|
||||
`type` is specified by '{kb_cmd:xdotool}:', and `key` is specified by '{kb_cmd:kxdotool}:'. If the command is not specified and `alwaysUseXdotool` is set from the environment variable, it will default to `type`.
|
||||
|
||||
``` go
|
||||
|
||||
``` go
|
||||
|
||||
|
||||
``` go
|
||||
--- handle xdotoool commands
|
||||
|
||||
if alwaysUseXdotool || strings.HasPrefix(message_string, "{kb_cmd:xdotool}:") {
|
||||
message_string = strings.TrimPrefix(message_string, "{kb_cmd:xdotool}:")
|
||||
if message_string == "" {
|
||||
message_string = "\n"
|
||||
}
|
||||
|
||||
|
||||
if characterRegex.MatchString(message_string) {
|
||||
for _, character := range message_string {
|
||||
charString := string(character)
|
||||
if charString == "\n" {
|
||||
charString = "Enter"
|
||||
} else if charString == "\t" {
|
||||
charString = "Tab"
|
||||
} else if charString == "\b" {
|
||||
charString = "BackSpace"
|
||||
} else{
|
||||
doXDoTool("type", charString)
|
||||
continue
|
||||
}
|
||||
// key is required for special characters
|
||||
err = doXDoTool("key", charString)
|
||||
continue
|
||||
}
|
||||
continue
|
||||
} else {
|
||||
doXDoTool("type", message_string)
|
||||
}
|
||||
continue
|
||||
} else if strings.HasPrefix(message_string, "{kb_cmd:kxdotool}:") {
|
||||
message_string = strings.TrimPrefix(message_string, "{kb_cmd:kxdotool}:")
|
||||
if message_string == "" {
|
||||
message_string = "\n"
|
||||
}
|
||||
doXDoTool("key", message_string)
|
||||
continue
|
||||
}
|
||||
|
||||
---
|
||||
```
|
Loading…
Reference in New Issue
Block a user