New
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -72,12 +73,36 @@ func MaskInPlace(data []byte, sid SessionID, mode byte, seq uint64, response boo
|
||||
}
|
||||
|
||||
func WriteRequest(w io.Writer, mode byte, sid SessionID, seq uint64, plaintext []byte) error {
|
||||
return WriteRequestProfile(w, mode, sid, seq, plaintext, 0)
|
||||
}
|
||||
|
||||
// WriteRequestProfile writes a binary request whose first byte is XORed with
|
||||
// headerMask. The remaining framing and payload encoding stay unchanged.
|
||||
// Masks are selected once at client startup and then remain fixed.
|
||||
func WriteRequestProfile(w io.Writer, mode byte, sid SessionID, seq uint64, plaintext []byte, headerMask byte) error {
|
||||
return WriteRequestProfileEncoding(w, mode, sid, seq, plaintext, headerMask, false)
|
||||
}
|
||||
|
||||
// WriteRequestProfileEncoding optionally leaves the payload clear. Clear mode
|
||||
// is signalled by the connection cover preface, so legacy peers continue to use
|
||||
// the SHA-256 compatibility mask unchanged.
|
||||
func WriteRequestProfileEncoding(w io.Writer, mode byte, sid SessionID, seq uint64, plaintext []byte, headerMask byte, clear bool) error {
|
||||
if len(plaintext) > MaxPayload {
|
||||
return fmt.Errorf("request payload too large: %d", len(plaintext))
|
||||
}
|
||||
if clear {
|
||||
var header [RequestHeaderSize]byte
|
||||
header[0] = mode ^ headerMask
|
||||
copy(header[1:17], sid[:])
|
||||
binary.BigEndian.PutUint64(header[17:25], seq)
|
||||
binary.BigEndian.PutUint32(header[25:29], uint32(len(plaintext)))
|
||||
buffers := net.Buffers{header[:], plaintext}
|
||||
_, err := buffers.WriteTo(w)
|
||||
return err
|
||||
}
|
||||
|
||||
packet := make([]byte, RequestHeaderSize+len(plaintext))
|
||||
packet[0] = mode
|
||||
packet[0] = mode ^ headerMask
|
||||
copy(packet[1:17], sid[:])
|
||||
binary.BigEndian.PutUint64(packet[17:25], seq)
|
||||
binary.BigEndian.PutUint32(packet[25:29], uint32(len(plaintext)))
|
||||
@@ -87,13 +112,25 @@ func WriteRequest(w io.Writer, mode byte, sid SessionID, seq uint64, plaintext [
|
||||
}
|
||||
|
||||
func ReadRequest(r io.Reader) (Request, error) {
|
||||
return ReadRequestProfile(r, 0)
|
||||
}
|
||||
|
||||
// ReadRequestProfile decodes a request written with WriteRequestProfile.
|
||||
func ReadRequestProfile(r io.Reader, headerMask byte) (Request, error) {
|
||||
return ReadRequestProfileEncoding(r, headerMask, false)
|
||||
}
|
||||
|
||||
func ReadRequestProfileEncoding(r io.Reader, headerMask byte, clear bool) (Request, error) {
|
||||
var req Request
|
||||
var header [RequestHeaderSize]byte
|
||||
if _, err := io.ReadFull(r, header[:]); err != nil {
|
||||
return req, err
|
||||
}
|
||||
|
||||
req.Mode = header[0]
|
||||
req.Mode = header[0] ^ headerMask
|
||||
if req.Mode > ModeClose {
|
||||
return req, errors.New("unknown request mode")
|
||||
}
|
||||
copy(req.Session[:], header[1:17])
|
||||
req.Seq = binary.BigEndian.Uint64(header[17:25])
|
||||
n := binary.BigEndian.Uint32(header[25:29])
|
||||
@@ -106,28 +143,53 @@ func ReadRequest(r io.Reader) (Request, error) {
|
||||
if _, err := io.ReadFull(r, req.Payload); err != nil {
|
||||
return req, err
|
||||
}
|
||||
MaskInPlace(req.Payload, req.Session, req.Mode, req.Seq, false)
|
||||
if !clear {
|
||||
MaskInPlace(req.Payload, req.Session, req.Mode, req.Seq, false)
|
||||
}
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func WriteResponse(w io.Writer, status byte, body []byte) error {
|
||||
return WriteResponseProfile(w, status, body, writerHeaderMask(w))
|
||||
}
|
||||
|
||||
// WriteResponseProfile writes a response using the selected first-byte mask.
|
||||
func WriteResponseProfile(w io.Writer, status byte, body []byte, headerMask byte) error {
|
||||
if len(body) > MaxPayload {
|
||||
return fmt.Errorf("response body too large: %d", len(body))
|
||||
}
|
||||
packet := make([]byte, ResponseHeaderSize+len(body))
|
||||
packet[0] = status
|
||||
packet[0] = status ^ headerMask
|
||||
binary.BigEndian.PutUint32(packet[1:5], uint32(len(body)))
|
||||
copy(packet[5:], body)
|
||||
return writeAll(w, packet)
|
||||
}
|
||||
|
||||
func WriteMaskedResponse(w io.Writer, status byte, body []byte, sid SessionID, mode byte, seq uint64) error {
|
||||
return WriteMaskedResponseProfileEncoding(w, status, body, sid, mode, seq, writerHeaderMask(w), writerClearPayload(w))
|
||||
}
|
||||
|
||||
// WriteMaskedResponseProfile combines the normal payload mask with the
|
||||
// selected first-byte header mask.
|
||||
func WriteMaskedResponseProfile(w io.Writer, status byte, body []byte, sid SessionID, mode byte, seq uint64, headerMask byte) error {
|
||||
return WriteMaskedResponseProfileEncoding(w, status, body, sid, mode, seq, headerMask, false)
|
||||
}
|
||||
|
||||
func WriteMaskedResponseProfileEncoding(w io.Writer, status byte, body []byte, sid SessionID, mode byte, seq uint64, headerMask byte, clear bool) error {
|
||||
if len(body) > MaxPayload {
|
||||
return fmt.Errorf("response body too large: %d", len(body))
|
||||
}
|
||||
if clear {
|
||||
var header [ResponseHeaderSize]byte
|
||||
header[0] = status ^ headerMask
|
||||
binary.BigEndian.PutUint32(header[1:5], uint32(len(body)))
|
||||
buffers := net.Buffers{header[:], body}
|
||||
_, err := buffers.WriteTo(w)
|
||||
return err
|
||||
}
|
||||
packet := make([]byte, ResponseHeaderSize+len(body))
|
||||
packet[0] = status
|
||||
packet[0] = status ^ headerMask
|
||||
binary.BigEndian.PutUint32(packet[1:5], uint32(len(body)))
|
||||
copy(packet[5:], body)
|
||||
MaskInPlace(packet[5:], sid, mode, seq, true)
|
||||
@@ -135,6 +197,11 @@ func WriteMaskedResponse(w io.Writer, status byte, body []byte, sid SessionID, m
|
||||
}
|
||||
|
||||
func ReadResponse(r io.Reader) (byte, []byte, error) {
|
||||
return ReadResponseProfile(r, 0)
|
||||
}
|
||||
|
||||
// ReadResponseProfile decodes a response written with a header profile.
|
||||
func ReadResponseProfile(r io.Reader, headerMask byte) (byte, []byte, error) {
|
||||
var header [ResponseHeaderSize]byte
|
||||
if _, err := io.ReadFull(r, header[:]); err != nil {
|
||||
return 0, nil, err
|
||||
@@ -150,16 +217,19 @@ func ReadResponse(r io.Reader) (byte, []byte, error) {
|
||||
return 0, nil, err
|
||||
}
|
||||
}
|
||||
return header[0], body, nil
|
||||
status := header[0] ^ headerMask
|
||||
if status > StatusEOF {
|
||||
return 0, nil, errors.New("unknown response status")
|
||||
}
|
||||
return status, body, nil
|
||||
}
|
||||
|
||||
func DecodeMaskedResponse(status byte, body []byte, sid SessionID, mode byte, seq uint64) []byte {
|
||||
if len(body) == 0 || status == StatusError {
|
||||
return body
|
||||
}
|
||||
out := append([]byte(nil), body...)
|
||||
MaskInPlace(out, sid, mode, seq, true)
|
||||
return out
|
||||
MaskInPlace(body, sid, mode, seq, true)
|
||||
return body
|
||||
}
|
||||
|
||||
func writeAll(w io.Writer, b []byte) error {
|
||||
@@ -175,3 +245,17 @@ func writeAll(w io.Writer, b []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writerHeaderMask(w io.Writer) byte {
|
||||
if profiled, ok := w.(interface{ HeaderMask() byte }); ok {
|
||||
return profiled.HeaderMask()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func writerClearPayload(w io.Writer) bool {
|
||||
if profiled, ok := w.(interface{ ClearPayload() bool }); ok {
|
||||
return profiled.ClearPayload()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user