telebit/v1.go

265 lines
6.5 KiB
Go

package telebit
import (
"errors"
"fmt"
"os"
"strconv"
"strings"
"git.rootprojects.org/root/telebit/dbg"
)
const (
// HeaderLengthState is the 2nd (1) state
HeaderLengthState State = 1 + iota
// HeaderState is the 3rd (2) state
HeaderState
// PayloadState is the 4th (3) state
PayloadState
)
const (
// FamilyIndex is the 1st (0) address element, either IPv4 or IPv6
FamilyIndex int = iota
// AddressIndex is the 2nd (1) address element, the IP or Hostname
AddressIndex
// PortIndex is the 3rd (2) address element, the Port
PortIndex
// LengthIndex is the 4th (3) address element, the Payload size
LengthIndex
// ServiceIndex is the 5th (4) address element, the Scheme or Control message type
ServiceIndex
// RelayPortIndex is the 6th (5) address element, the port on which the connection was established
RelayPortIndex
// ServernameIndex is the 7th (6) address element, the SNI Servername or Hostname
ServernameIndex
)
// Header is the MPLEXY address/control meta data that comes before a packet
type Header struct {
Family string
Address string
Port string
Service string
}
func (p *Parser) unpackV1(b []byte) (int, error) {
z := 0
for {
if z > 20 {
panic("stuck in an infinite loop?")
}
z++
n := len(b)
if n < 1 {
if dbg.Debug {
fmt.Fprintf(os.Stderr, "[debug] v1 message unpacked (%d loops) (%d bytes left)\n", z, n)
}
break
}
var err error
switch p.parseState {
case VersionState:
//fmt.Println("[debug] version state", b[0])
p.state.version = b[0]
b = b[1:]
p.consumed++
p.parseState++
case HeaderLengthState:
//fmt.Println("[debug] v1 h len")
b = p.unpackV1HeaderLength(b)
case HeaderState:
//fmt.Println("[debug] v1 header")
b, err = p.unpackV1Header(b, n)
if nil != err {
//fmt.Println("[debug] v1 header err", err)
consumed := p.consumed
p.consumed = 0
return consumed, err
}
case PayloadState:
//fmt.Println("[debug] v1 payload")
// if this payload is complete, reset all state
if p.state.payloadWritten == p.state.payloadLen {
p.state = ParserState{}
p.parseState = 0
}
b, err = p.unpackV1Payload(b, n)
if nil != err {
consumed := p.consumed
p.consumed = 0
return consumed, err
}
default:
fmt.Fprintf(os.Stderr, "[debug] v1 unknown state\n")
// do nothing
consumed := p.consumed
p.consumed = 0
return consumed, errors.New("error unpacking")
}
}
consumed := p.consumed
p.consumed = 0
return consumed, nil
}
func (p *Parser) unpackV1HeaderLength(b []byte) []byte {
p.state.headerLen = int(b[0])
//fmt.Println("[debug] unpacked header len", p.state.headerLen)
b = b[1:]
p.consumed++
p.parseState++
return b
}
func (p *Parser) unpackV1Header(b []byte, n int) ([]byte, error) {
//fmt.Println("[debug] got", len(b), "bytes", string(b))
m := len(p.state.header)
k := p.state.headerLen - m
if n < k {
k = n
}
p.consumed += k
c := b[0:k]
b = b[k:]
//fmt.Println("[debug] has", m, "want", k, "more and have", len(b), "more")
p.state.header = append(p.state.header, c...)
if p.state.headerLen != len(p.state.header) {
return b, nil
}
parts := strings.Split(strings.TrimSpace(string(p.state.header)), ",")
p.state.header = nil
if len(parts) < 5 {
return nil, errors.New("error unpacking header")
}
payloadLenStr := parts[LengthIndex]
payloadLen, err := strconv.Atoi(payloadLenStr)
if nil != err {
return nil, errors.New("error unpacking header payload length")
}
p.state.payloadLen = payloadLen
port, _ := strconv.Atoi(parts[PortIndex])
service := parts[ServiceIndex]
if dbg.Debug {
fmt.Fprintf(os.Stderr, "[debug] parts: %s\n", strings.Join(parts, " | "))
}
if "control" == service {
if dbg.Debug {
fmt.Fprintf(os.Stderr, "[debug] control: %s\n", service)
}
return nil, errors.New("'control' messages not implemented")
}
src := Addr{
family: parts[FamilyIndex],
addr: parts[AddressIndex],
port: port,
//scheme: Scheme(service),
}
dst := Addr{
scheme: Scheme(service),
}
if len(parts) > RelayPortIndex {
port, _ := strconv.Atoi(parts[RelayPortIndex])
dst.port = port
}
if len(parts) > ServernameIndex {
dst.addr = parts[ServernameIndex]
}
p.state.srcAddr = src
p.state.dstAddr = dst
/*
p.state.conn = p.conns[addr.Network()]
if nil == p.state.conn {
rconn, wconn := net.Pipe()
conn := Conn{
updated: time.Now(),
relayTargetAddr: addr,
relay: rconn,
local: wconn,
}
copied := conn
p.state.conn = &copied
p.conns[addr.Network()] = p.state.conn
p.newConns <- p.state.conn
}
*/
p.parseState++
if dbg.Debug {
fmt.Fprintf(os.Stderr, "[debug] unpackV1 parse state: %v\n", p.parseState)
}
if "end" == service {
fmt.Printf("[codec] [v1] unpack control message: 'end'\n")
p.handler.RouteBytes(p.state.srcAddr, p.state.dstAddr, []byte{})
}
return b, nil
}
func (p *Parser) unpackV1Payload(b []byte, n int) ([]byte, error) {
if dbg.Debug {
fmt.Fprintf(os.Stderr, "[debug] unpackV1 payload state: %+v\n", p.state)
}
// Handle "connect" and "end"
if 0 == p.state.payloadLen {
/*
p.newMsg <- msg{
addr: Addr,
bytes: []byte{},
}
addr := &p.state.conn.relayTargetAddr
if "end" == string(addr.scheme) {
if err := p.state.conn.Close(); nil != err {
// TODO log potential error?
}
}
return b, nil
*/
if dbg.Debug {
//fmt.Fprintf(os.Stderr, "[debug] [2] payload written: %d | payload length: %d\n", p.state.payloadWritten, p.state.payloadLen)
fmt.Fprintf(os.Stderr, "[debug] unpackV1 RouteBytes: %#v %#v %s\n", p.state.srcAddr, p.state.dstAddr, p.state.dstAddr.scheme)
}
p.handler.RouteBytes(p.state.srcAddr, p.state.dstAddr, []byte{})
return b, nil
}
k := p.state.payloadLen - p.state.payloadWritten
if n < k {
k = n
}
c := b[0:k]
b = b[k:]
// TODO don't let a write on one connection block others,
// and also put backpressure on just that connection
/*
m, err := p.state.conn.local.Write(c)
p.state.payloadWritten += m
if nil != err {
// TODO we want to surface this error somewhere, but not to the websocket
return b, nil
}
*/
p.handler.RouteBytes(p.state.srcAddr, p.state.dstAddr, c)
p.consumed += k
p.state.payloadWritten += k
//fmt.Fprintf(os.Stderr, "[debug] [1] payload written: %d | payload length: %d\n", p.state.payloadWritten, p.state.payloadLen)
// if this payload is complete, reset all state
if p.state.payloadWritten == p.state.payloadLen {
p.state = ParserState{}
p.parseState = 0
if dbg.Debug {
fmt.Println("[debug] MPLEXY completed packet and reset state")
}
}
return b, nil
}