forked from lug/matterbridge
		
	
		
			
				
	
	
		
			128 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			128 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package steam
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto/aes"
 | 
						|
	"crypto/cipher"
 | 
						|
	"encoding/binary"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"net"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	"github.com/Philipp15b/go-steam/cryptoutil"
 | 
						|
	. "github.com/Philipp15b/go-steam/protocol"
 | 
						|
)
 | 
						|
 | 
						|
type connection interface {
 | 
						|
	Read() (*Packet, error)
 | 
						|
	Write([]byte) error
 | 
						|
	Close() error
 | 
						|
	SetEncryptionKey([]byte)
 | 
						|
	IsEncrypted() bool
 | 
						|
}
 | 
						|
 | 
						|
const tcpConnectionMagic uint32 = 0x31305456 // "VT01"
 | 
						|
 | 
						|
type tcpConnection struct {
 | 
						|
	conn        *net.TCPConn
 | 
						|
	ciph        cipher.Block
 | 
						|
	cipherMutex sync.RWMutex
 | 
						|
}
 | 
						|
 | 
						|
func dialTCP(laddr, raddr *net.TCPAddr) (*tcpConnection, error) {
 | 
						|
	conn, err := net.DialTCP("tcp", laddr, raddr)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return &tcpConnection{
 | 
						|
		conn: conn,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (c *tcpConnection) Read() (*Packet, error) {
 | 
						|
	// All packets begin with a packet length
 | 
						|
	var packetLen uint32
 | 
						|
	err := binary.Read(c.conn, binary.LittleEndian, &packetLen)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// A magic value follows for validation
 | 
						|
	var packetMagic uint32
 | 
						|
	err = binary.Read(c.conn, binary.LittleEndian, &packetMagic)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if packetMagic != tcpConnectionMagic {
 | 
						|
		return nil, fmt.Errorf("Invalid connection magic! Expected %d, got %d!", tcpConnectionMagic, packetMagic)
 | 
						|
	}
 | 
						|
 | 
						|
	buf := make([]byte, packetLen, packetLen)
 | 
						|
	_, err = io.ReadFull(c.conn, buf)
 | 
						|
	if err == io.ErrUnexpectedEOF {
 | 
						|
		return nil, io.EOF
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// Packets after ChannelEncryptResult are encrypted
 | 
						|
	c.cipherMutex.RLock()
 | 
						|
	if c.ciph != nil {
 | 
						|
		buf = cryptoutil.SymmetricDecrypt(c.ciph, buf)
 | 
						|
	}
 | 
						|
	c.cipherMutex.RUnlock()
 | 
						|
 | 
						|
	return NewPacket(buf)
 | 
						|
}
 | 
						|
 | 
						|
// Writes a message. This may only be used by one goroutine at a time.
 | 
						|
func (c *tcpConnection) Write(message []byte) error {
 | 
						|
	c.cipherMutex.RLock()
 | 
						|
	if c.ciph != nil {
 | 
						|
		message = cryptoutil.SymmetricEncrypt(c.ciph, message)
 | 
						|
	}
 | 
						|
	c.cipherMutex.RUnlock()
 | 
						|
 | 
						|
	err := binary.Write(c.conn, binary.LittleEndian, uint32(len(message)))
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	err = binary.Write(c.conn, binary.LittleEndian, tcpConnectionMagic)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	_, err = c.conn.Write(message)
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func (c *tcpConnection) Close() error {
 | 
						|
	return c.conn.Close()
 | 
						|
}
 | 
						|
 | 
						|
func (c *tcpConnection) SetEncryptionKey(key []byte) {
 | 
						|
	c.cipherMutex.Lock()
 | 
						|
	defer c.cipherMutex.Unlock()
 | 
						|
	if key == nil {
 | 
						|
		c.ciph = nil
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if len(key) != 32 {
 | 
						|
		panic("Connection AES key is not 32 bytes long!")
 | 
						|
	}
 | 
						|
 | 
						|
	var err error
 | 
						|
	c.ciph, err = aes.NewCipher(key)
 | 
						|
	if err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (c *tcpConnection) IsEncrypted() bool {
 | 
						|
	c.cipherMutex.RLock()
 | 
						|
	defer c.cipherMutex.RUnlock()
 | 
						|
	return c.ciph != nil
 | 
						|
}
 |