package main

import (
	"flag"
	"log"
	"net"
	"strings"
)

// TS3 Proxy to capture clientinit parameters
func main() {
	var localAddr string
	var serverAddr string

	flag.StringVar(&localAddr, "listen", ":9988", "Local listen address")
	flag.StringVar(&serverAddr, "server", "localhost:9987", "Real TeamSpeak server address")
	flag.Parse()

	// Resolve addresses
	localUDP, err := net.ResolveUDPAddr("udp", localAddr)
	if err != nil {
		log.Fatalf("Invalid local address: %v", err)
	}
	serverUDP, err := net.ResolveUDPAddr("udp", serverAddr)
	if err != nil {
		log.Fatalf("Invalid server address: %v", err)
	}

	// Listen on local port
	conn, err := net.ListenUDP("udp", localUDP)
	if err != nil {
		log.Fatalf("Failed to listen: %v", err)
	}
	defer conn.Close()

	log.Printf("Proxy listening on %s, forwarding to %s", localAddr, serverAddr)
	log.Println("Connect your Real TeamSpeak Client to 'localhost:9988' to capture credentials.")

	// Map client addresses to server connections (for multiple clients support, simplifying for 1)
	// Simple forwarder: We only expect one client for this task.
	var clientAddr *net.UDPAddr

	buf := make([]byte, 2048)

	for {
		n, addr, err := conn.ReadFromUDP(buf)
		if err != nil {
			log.Printf("Read error: %v", err)
			continue
		}

		// Check if packet is from client or server
		if clientAddr == nil || addr.String() != clientAddr.String() {
			// New client or client packet
			// If it's from our known server (unlikely as we don't bind to serverAddr), it's a client.
			// But wait, we are listening on localUDP.
			// We need to dial the server to forward.

			// We'll use a separate socket to talk to the server to keep track of sessions?
			// Simplest UDP proxy:
			// 1. Receive from Client -> Send to Server (using a dialer)
			// 2. Receive from Server -> Send to Client

			// Let's reset for new clients to keep it simple
			clientAddr = addr
			handleClientPacket(conn, serverUDP, buf[:n], addr)
		} else {
			// Known client
			handleClientPacket(conn, serverUDP, buf[:n], addr)
		}
	}
}

// We need a persistent connection to the server to receive responses
// In a simple loop, we can't easily run two blocking reads (from client and from server) without goroutines.
// Let's structure:
// Main loop reads from Client (ListenUDP).
// On first packet, start a Goroutine that Dials the Server.
// That goroutine reads from Server and writes to Client.
// Main loop writes to Server using that connection.

var serverConn *net.UDPConn

func handleClientPacket(clientConn *net.UDPConn, serverAddr *net.UDPAddr, data []byte, clientAddr *net.UDPAddr) {
	// Inspect Packet for clientinit strings (Naive text search)
	// clientinit is encrypted in standard flow?
	// WAIT. If we proxy, the client and server negotiate encryption.
	// We CANNOT decrypt the traffic unless we perform a MitM attack on the Diffie-Hellman handshake.
	// TS3 uses ECDH. We cannot derive the shared secret just by passively observing.
	// We would need to:
	// 1. Intercept `initivexpand2` (Step 2/3/4?)
	// 2. Replace the Server's Public Key with OUR Public Key.
	// 3. Negotiate SharedSecret1 with Client.
	// 4. Negotiate SharedSecret2 with Server (using our Private Key).
	// 5. Decrypt Client packet -> Re-encrypt for Server.

	// This is significantly complex.
	// BUT, `clientinit` might be sent *plaintext* in some legacy modes?
	// No, the user's issue is specifically about the encrypted handshake.

	// However, `client_version` is sometimes sent in cleartext in the INIT1 packet?
	// No, INIT1 is just 4 timestamps + random bytes.

	// WAIT. The very first packet is valid?
	// `clientinit` is sent AFTER the handshake (Step 6). It is definitely encrypted.

	// If the user connects with a real client, the client calculates the SharedSecret using the REAL server's key.
	// We verify the packets pass through.
	// WE CANNOT READ THE PAYLOAD without a full MitM (replacing keys).

	// IS THERE ANOTHER WAY?
	// Does the client send version in clear text anywhere?
	// Maybe in the UserAgent? (UDP doesn't have headers like HTTP)

	// Proposal: Implement Full MitM.
	// We already have the ECDH logic implemented in Go!
	// We can act as the "Server" to the Client, and "Client" to the Server.
	// 1. Client connects to Proxy. Proxy acts as Server.
	// 2. Proxy sends its OWN Identity/Key to Client.
	// 3. Proxy connects to Real Server.
	// 4. Proxy completes handshake with Real Server.
	// 5. Client completes handshake with Proxy.
	// 6. Proxy decrypts Client's `clientinit`, LOGS IT, then re-encrypts for Server.

	// This is the chosen path. It reuses our `handshake.go` and `license.go` logic.

	inspectPacket(data)

	// Forwarding logic placeholder (Passive won't work for decryption)
}

func inspectPacket(data []byte) {
	// Check for "clientinit" ASCII pattern
	s := string(data)
	if strings.Contains(s, "clientinit") {
		log.Printf("[!] FOUND POSSIBLE CLIENTINIT (Plaintext?): %s", s)
	}
}
