commit 866e8d58b32eb4472d05905db7a0d4ac0c193e80 Author: David Fifield david@bamsoftware.com Date: Wed Jan 30 13:46:41 2013 -0800
Do USERADDR and DONE commands. --- websocket-transport/pt.go | 103 +++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 103 insertions(+), 0 deletions(-)
diff --git a/websocket-transport/pt.go b/websocket-transport/pt.go index 2b4a759..f39e177 100644 --- a/websocket-transport/pt.go +++ b/websocket-transport/pt.go @@ -31,6 +31,7 @@ import ( "crypto/rand" "crypto/sha256" "crypto/subtle" + "encoding/binary" "errors" "fmt" "io" @@ -455,6 +456,103 @@ func extOrPortAuthenticate(s *net.TCPConn, info *PtServerInfo) error { return nil }
+// See section 3.1 of 196-transport-control-ports.txt. +const ( + extOrCmdDone = 0x0000 + extOrCmdUserAddr = 0x0001 + extOrCmdOkay = 0x1000 + extOrCmdDeny = 0x1001 +) + +func extOrPortWriteCommand(s *net.TCPConn, cmd uint16, body []byte) error { + var buf bytes.Buffer + if len(body) > 65535 { + return errors.New("command exceeds maximum length of 65535") + } + err := binary.Write(&buf, binary.BigEndian, cmd) + if err != nil { + return err + } + err = binary.Write(&buf, binary.BigEndian, uint16(len(body))) + if err != nil { + return err + } + err = binary.Write(&buf, binary.BigEndian, body) + if err != nil { + return err + } + _, err = s.Write(buf.Bytes()) + if err != nil { + return err + } + + return nil +} + +// Send a USERADDR command on s. See section 3.1 of +// 196-transport-control-ports.txt. +func extOrPortSendUserAddr(s *net.TCPConn, conn net.Conn) error { + return extOrPortWriteCommand(s, extOrCmdUserAddr, []byte(conn.RemoteAddr().String())) +} + +// Send a DONE command on s. See section 3.1 of 196-transport-control-ports.txt. +func extOrPortSendDone(s *net.TCPConn) error { + return extOrPortWriteCommand(s, extOrCmdDone, []byte{}) +} + +func extOrPortRecvCommand(s *net.TCPConn) (cmd uint16, body []byte, err error) { + var bodyLen uint16 + data := make([]byte, 4) + + _, err = io.ReadFull(s, data) + if err != nil { + return + } + buf := bytes.NewBuffer(data) + err = binary.Read(buf, binary.BigEndian, &cmd) + if err != nil { + return + } + err = binary.Read(buf, binary.BigEndian, &bodyLen) + if err != nil { + return + } + body = make([]byte, bodyLen) + _, err = io.ReadFull(s, body) + if err != nil { + return + } + + return cmd, body, err +} + +// Send a USERADDR command followed by a DONE command. Wait for an OKAY or DENY +// response command from the server. Returns nil if and only if OKAY is +// received. +func extOrPortDoUserAddr(s *net.TCPConn, conn net.Conn) error { + var err error + + err = extOrPortSendUserAddr(s, conn) + if err != nil { + return err + } + err = extOrPortSendDone(s) + if err != nil { + return err + } + cmd, _, err := extOrPortRecvCommand(s) + if err != nil { + return err + } + if cmd == extOrCmdDeny { + return errors.New("server returned DENY after our USERADDR and DONE") + } else if cmd != extOrCmdOkay { + return errors.New(fmt.Sprintf("server returned unknown command 0x%04x after our USERADDR and DONE", cmd)) + } + + return nil +} + // Connect to info.ExtendedOrAddr if defined, or else info.OrAddr, and return an // open *net.TCPConn. If connecting to the extended OR port, extended OR port // authentication à la 217-ext-orport-auth.txt is done before returning; an @@ -474,6 +572,11 @@ func PtConnectOr(info *PtServerInfo, conn net.Conn) (*net.TCPConn, error) { s.Close() return nil, err } + err = extOrPortDoUserAddr(s, conn) + if err != nil { + s.Close() + return nil, err + } s.SetDeadline(time.Time{})
return s, nil