summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Philipp15b/go-steam/connection.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/Philipp15b/go-steam/connection.go')
-rw-r--r--vendor/github.com/Philipp15b/go-steam/connection.go127
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
+}