diff options
Diffstat (limited to 'vendor/github.com/Philipp15b/go-steam/connection.go')
-rw-r--r-- | vendor/github.com/Philipp15b/go-steam/connection.go | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/vendor/github.com/Philipp15b/go-steam/connection.go b/vendor/github.com/Philipp15b/go-steam/connection.go new file mode 100644 index 00000000..0036e40f --- /dev/null +++ b/vendor/github.com/Philipp15b/go-steam/connection.go @@ -0,0 +1,127 @@ +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 +} |