422 lines
11 KiB
Go
422 lines
11 KiB
Go
package datacommands
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
)
|
|
|
|
// Unmarshal reads from the provided connection and returns
|
|
// the message type (as a string), the unmarshalled struct, or an error.
|
|
func Unmarshal(conn io.Reader) (interface{}, error) {
|
|
// Every command starts with a 1-byte command ID.
|
|
header := make([]byte, 1)
|
|
|
|
if _, err := io.ReadFull(conn, header); err != nil {
|
|
return nil, fmt.Errorf("couldn't read command ID: %w", err)
|
|
}
|
|
|
|
cmdID := header[0]
|
|
switch cmdID {
|
|
// ProxyStatusRequest: 1 byte ID + 2 bytes ProxyID.
|
|
case ProxyStatusRequestID:
|
|
buf := make([]byte, 2)
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyStatusRequest ProxyID: %w", err)
|
|
}
|
|
|
|
proxyID := binary.BigEndian.Uint16(buf)
|
|
|
|
return &ProxyStatusRequest{
|
|
ProxyID: proxyID,
|
|
}, nil
|
|
|
|
// ProxyStatusResponse: 1 byte ID + 2 bytes ProxyID + 1 byte IsActive.
|
|
case ProxyStatusResponseID:
|
|
buf := make([]byte, 2)
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyStatusResponse ProxyID: %w", err)
|
|
}
|
|
|
|
proxyID := binary.BigEndian.Uint16(buf)
|
|
boolBuf := make([]byte, 1)
|
|
|
|
if _, err := io.ReadFull(conn, boolBuf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyStatusResponse IsActive: %w", err)
|
|
}
|
|
|
|
isActive := boolBuf[0] != 0
|
|
|
|
return &ProxyStatusResponse{
|
|
ProxyID: proxyID,
|
|
IsActive: isActive,
|
|
}, nil
|
|
|
|
// RemoveProxy: 1 byte ID + 2 bytes ProxyID.
|
|
case RemoveProxyID:
|
|
buf := make([]byte, 2)
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read RemoveProxy ProxyID: %w", err)
|
|
}
|
|
|
|
proxyID := binary.BigEndian.Uint16(buf)
|
|
|
|
return &RemoveProxy{
|
|
ProxyID: proxyID,
|
|
}, nil
|
|
|
|
// ProxyConnectionsRequest: 1 byte ID + 2 bytes ProxyID.
|
|
case ProxyConnectionsRequestID:
|
|
buf := make([]byte, 2)
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyConnectionsRequest ProxyID: %w", err)
|
|
}
|
|
|
|
proxyID := binary.BigEndian.Uint16(buf)
|
|
|
|
return &ProxyConnectionsRequest{
|
|
ProxyID: proxyID,
|
|
}, nil
|
|
|
|
// ProxyConnectionsResponse: 1 byte ID + 2 bytes Connections length + 2 bytes for each Connection in Connections.
|
|
case ProxyConnectionsResponseID:
|
|
buf := make([]byte, 2)
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyConnectionsResponse length: %w", err)
|
|
}
|
|
|
|
length := binary.BigEndian.Uint16(buf)
|
|
connections := make([]uint16, length)
|
|
|
|
var failedDuringReading error
|
|
|
|
for connectionIndex := range connections {
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
failedDuringReading = fmt.Errorf("couldn't read ProxyConnectionsResponse with position of %d: %w", connectionIndex, err)
|
|
break
|
|
}
|
|
|
|
connections[connectionIndex] = binary.BigEndian.Uint16(buf)
|
|
}
|
|
|
|
return &ProxyConnectionsResponse{
|
|
Connections: connections,
|
|
}, failedDuringReading
|
|
|
|
// ProxyInstanceResponse: 1 byte ID + 2 bytes Proxies length + 2 bytes for each Proxy in Proxies.
|
|
case ProxyInstanceResponseID:
|
|
buf := make([]byte, 2)
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyConnectionsResponse length: %w", err)
|
|
}
|
|
|
|
length := binary.BigEndian.Uint16(buf)
|
|
proxies := make([]uint16, length)
|
|
|
|
var failedDuringReading error
|
|
|
|
for connectionIndex := range proxies {
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
failedDuringReading = fmt.Errorf("couldn't read ProxyConnectionsResponse with position of %d: %w", connectionIndex, err)
|
|
break
|
|
}
|
|
|
|
proxies[connectionIndex] = binary.BigEndian.Uint16(buf)
|
|
}
|
|
|
|
return &ProxyInstanceResponse{
|
|
Proxies: proxies,
|
|
}, failedDuringReading
|
|
|
|
// TCPConnectionOpened: 1 byte ID + 2 bytes ProxyID + 2 bytes ConnectionID.
|
|
case TCPConnectionOpenedID:
|
|
buf := make([]byte, 2+2)
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read TCPConnectionOpened fields: %w", err)
|
|
}
|
|
|
|
proxyID := binary.BigEndian.Uint16(buf[0:2])
|
|
connectionID := binary.BigEndian.Uint16(buf[2:4])
|
|
|
|
return &TCPConnectionOpened{
|
|
ProxyID: proxyID,
|
|
ConnectionID: connectionID,
|
|
}, nil
|
|
|
|
// TCPConnectionClosed: 1 byte ID + 2 bytes ProxyID + 2 bytes ConnectionID.
|
|
case TCPConnectionClosedID:
|
|
buf := make([]byte, 2+2)
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read TCPConnectionClosed fields: %w", err)
|
|
}
|
|
|
|
proxyID := binary.BigEndian.Uint16(buf[0:2])
|
|
connectionID := binary.BigEndian.Uint16(buf[2:4])
|
|
|
|
return &TCPConnectionClosed{
|
|
ProxyID: proxyID,
|
|
ConnectionID: connectionID,
|
|
}, nil
|
|
|
|
// TCPProxyData: 1 byte ID + 2 bytes ProxyID + 2 bytes ConnectionID + 2 bytes DataLength.
|
|
case TCPProxyDataID:
|
|
buf := make([]byte, 2+2+2)
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read TCPProxyData fields: %w", err)
|
|
}
|
|
|
|
proxyID := binary.BigEndian.Uint16(buf[0:2])
|
|
connectionID := binary.BigEndian.Uint16(buf[2:4])
|
|
dataLength := binary.BigEndian.Uint16(buf[4:6])
|
|
|
|
return &TCPProxyData{
|
|
ProxyID: proxyID,
|
|
ConnectionID: connectionID,
|
|
DataLength: dataLength,
|
|
}, nil
|
|
|
|
// UDPProxyData:
|
|
// Format: 1 byte ID + 2 bytes ProxyID + 2 bytes ConnectionID +
|
|
// 1 byte IP version + IP bytes + 2 bytes ClientPort + 2 bytes DataLength.
|
|
case UDPProxyDataID:
|
|
// Read 2 bytes ProxyID + 2 bytes ConnectionID.
|
|
buf := make([]byte, 2)
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read UDPProxyData ProxyID/ConnectionID: %w", err)
|
|
}
|
|
|
|
proxyID := binary.BigEndian.Uint16(buf)
|
|
|
|
// Read IP version.
|
|
ipVerBuf := make([]byte, 1)
|
|
|
|
if _, err := io.ReadFull(conn, ipVerBuf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read UDPProxyData IP version: %w", err)
|
|
}
|
|
|
|
var ipSize int
|
|
|
|
if ipVerBuf[0] == 4 {
|
|
ipSize = IPv4Size
|
|
} else if ipVerBuf[0] == 6 {
|
|
ipSize = IPv6Size
|
|
} else {
|
|
return nil, fmt.Errorf("invalid IP version received: %v", ipVerBuf[0])
|
|
}
|
|
|
|
// Read the IP bytes.
|
|
ipBytes := make([]byte, ipSize)
|
|
if _, err := io.ReadFull(conn, ipBytes); err != nil {
|
|
return nil, fmt.Errorf("couldn't read UDPProxyData IP bytes: %w", err)
|
|
}
|
|
clientIP := net.IP(ipBytes).String()
|
|
|
|
// Read ClientPort.
|
|
portBuf := make([]byte, 2)
|
|
|
|
if _, err := io.ReadFull(conn, portBuf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read UDPProxyData ClientPort: %w", err)
|
|
}
|
|
|
|
clientPort := binary.BigEndian.Uint16(portBuf)
|
|
|
|
// Read DataLength.
|
|
dataLengthBuf := make([]byte, 2)
|
|
|
|
if _, err := io.ReadFull(conn, dataLengthBuf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read UDPProxyData DataLength: %w", err)
|
|
}
|
|
|
|
dataLength := binary.BigEndian.Uint16(dataLengthBuf)
|
|
|
|
return &UDPProxyData{
|
|
ProxyID: proxyID,
|
|
ClientIP: clientIP,
|
|
ClientPort: clientPort,
|
|
DataLength: dataLength,
|
|
}, nil
|
|
|
|
// ProxyInformationRequest: 1 byte ID + 2 bytes ProxyID.
|
|
case ProxyInformationRequestID:
|
|
buf := make([]byte, 2)
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyInformationRequest ProxyID: %w", err)
|
|
}
|
|
|
|
proxyID := binary.BigEndian.Uint16(buf)
|
|
|
|
return &ProxyInformationRequest{
|
|
ProxyID: proxyID,
|
|
}, nil
|
|
|
|
// ProxyInformationResponse:
|
|
// Format: 1 byte ID + 1 byte Exists +
|
|
// 1 byte IP version + IP bytes + 2 bytes SourcePort + 2 bytes DestPort + 1 byte Protocol.
|
|
case ProxyInformationResponseID:
|
|
// Read Exists flag.
|
|
boolBuf := make([]byte, 1)
|
|
|
|
if _, err := io.ReadFull(conn, boolBuf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyInformationResponse Exists flag: %w", err)
|
|
}
|
|
|
|
exists := boolBuf[0] != 0
|
|
|
|
if !exists {
|
|
return &ProxyInformationResponse{
|
|
Exists: exists,
|
|
}, nil
|
|
}
|
|
|
|
// Read IP version.
|
|
ipVerBuf := make([]byte, 1)
|
|
|
|
if _, err := io.ReadFull(conn, ipVerBuf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyInformationResponse IP version: %w", err)
|
|
}
|
|
|
|
var ipSize int
|
|
|
|
if ipVerBuf[0] == 4 {
|
|
ipSize = IPv4Size
|
|
} else if ipVerBuf[0] == 6 {
|
|
ipSize = IPv6Size
|
|
} else {
|
|
return nil, fmt.Errorf("invalid IP version in ProxyInformationResponse: %v", ipVerBuf[0])
|
|
}
|
|
|
|
// Read the source IP bytes.
|
|
ipBytes := make([]byte, ipSize)
|
|
|
|
if _, err := io.ReadFull(conn, ipBytes); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyInformationResponse IP bytes: %w", err)
|
|
}
|
|
|
|
sourceIP := net.IP(ipBytes).String()
|
|
|
|
// Read SourcePort and DestPort.
|
|
portsBuf := make([]byte, 2+2)
|
|
|
|
if _, err := io.ReadFull(conn, portsBuf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyInformationResponse ports: %w", err)
|
|
}
|
|
|
|
sourcePort := binary.BigEndian.Uint16(portsBuf[0:2])
|
|
destPort := binary.BigEndian.Uint16(portsBuf[2:4])
|
|
|
|
// Read protocol.
|
|
protoBuf := make([]byte, 1)
|
|
|
|
if _, err := io.ReadFull(conn, protoBuf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyInformationResponse protocol: %w", err)
|
|
}
|
|
|
|
var protocol string
|
|
|
|
if protoBuf[0] == TCP {
|
|
protocol = "tcp"
|
|
} else if protoBuf[0] == UDP {
|
|
protocol = "udp"
|
|
} else {
|
|
return nil, fmt.Errorf("invalid protocol value in ProxyInformationResponse: %d", protoBuf[0])
|
|
}
|
|
|
|
return &ProxyInformationResponse{
|
|
Exists: exists,
|
|
SourceIP: sourceIP,
|
|
SourcePort: sourcePort,
|
|
DestPort: destPort,
|
|
Protocol: protocol,
|
|
}, nil
|
|
|
|
// ProxyConnectionInformationRequest: 1 byte ID + 2 bytes ProxyID + 2 bytes ConnectionID.
|
|
case ProxyConnectionInformationRequestID:
|
|
buf := make([]byte, 2+2)
|
|
|
|
if _, err := io.ReadFull(conn, buf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyConnectionInformationRequest fields: %w", err)
|
|
}
|
|
|
|
proxyID := binary.BigEndian.Uint16(buf[0:2])
|
|
connectionID := binary.BigEndian.Uint16(buf[2:4])
|
|
|
|
return &ProxyConnectionInformationRequest{
|
|
ProxyID: proxyID,
|
|
ConnectionID: connectionID,
|
|
}, nil
|
|
|
|
// ProxyConnectionInformationResponse:
|
|
// Format: 1 byte ID + 1 byte Exists + 1 byte IP version + IP bytes + 2 bytes ClientPort.
|
|
case ProxyConnectionInformationResponseID:
|
|
// Read Exists flag.
|
|
boolBuf := make([]byte, 1)
|
|
if _, err := io.ReadFull(conn, boolBuf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyConnectionInformationResponse Exists flag: %w", err)
|
|
}
|
|
|
|
exists := boolBuf[0] != 0
|
|
|
|
if !exists {
|
|
return &ProxyConnectionInformationResponse{
|
|
Exists: exists,
|
|
}, nil
|
|
}
|
|
|
|
// Read IP version.
|
|
ipVerBuf := make([]byte, 1)
|
|
|
|
if _, err := io.ReadFull(conn, ipVerBuf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyConnectionInformationResponse IP version: %w", err)
|
|
}
|
|
|
|
if ipVerBuf[0] != 4 && ipVerBuf[0] != 6 {
|
|
return nil, fmt.Errorf("invalid IP version in ProxyConnectionInformationResponse: %v", ipVerBuf[0])
|
|
}
|
|
|
|
var ipSize int
|
|
|
|
if ipVerBuf[0] == 4 {
|
|
ipSize = IPv4Size
|
|
} else {
|
|
ipSize = IPv6Size
|
|
}
|
|
|
|
// Read IP bytes.
|
|
ipBytes := make([]byte, ipSize)
|
|
|
|
if _, err := io.ReadFull(conn, ipBytes); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyConnectionInformationResponse IP bytes: %w", err)
|
|
}
|
|
|
|
clientIP := net.IP(ipBytes).String()
|
|
|
|
// Read ClientPort.
|
|
portBuf := make([]byte, 2)
|
|
|
|
if _, err := io.ReadFull(conn, portBuf); err != nil {
|
|
return nil, fmt.Errorf("couldn't read ProxyConnectionInformationResponse ClientPort: %w", err)
|
|
}
|
|
|
|
clientPort := binary.BigEndian.Uint16(portBuf)
|
|
|
|
return &ProxyConnectionInformationResponse{
|
|
Exists: exists,
|
|
ClientIP: clientIP,
|
|
ClientPort: clientPort,
|
|
}, nil
|
|
default:
|
|
return nil, fmt.Errorf("unknown command id: %v", cmdID)
|
|
}
|
|
}
|