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 }