package flowclass import ( "bytes" "fmt" "strconv" "idio.link/go/parser" ) /*********************** IPPDelim *************************/ type fcIPPDelim struct { val *bytes.Buffer } func (t fcIPPDelim) Shift(sm *parser.SM, e rune) error { switch e { case ' ': case ':': t.val.WriteRune(e) default: if string(t.val.Bytes()) != ":" { return fmt.Errorf("ipp delimiter: unexpected input: '%s'", t.val.Bytes()) } return parser.NewUnrecognized("ipp delimiter", e) } return nil } func (_ fcIPPDelim) Coalesce(_ []*parser.CSTNode) any { return nil } func (t fcIPPDelim) Value() *bytes.Buffer { return t.val } /************************* IPPs ***************************/ type fcIPPs struct { } func (t fcIPPs) Shift(sm *parser.SM, e rune) error { switch { case e == ' ': case e == '{': sm.Push(fcIPPSet{}) case isDigit(e) || isAlpha(e): return sm.Push(fcIPPSet{}).Shift(sm, e) default: return parser.NewUnrecognized("ipps", e) } return nil } func (t fcIPPs) Coalesce(nodes []*parser.CSTNode) any { return nodes[0].Coalesce() } func (t fcIPPs) Value() *bytes.Buffer { return nil } /************************ IPPSet **************************/ type fcIPPSet struct { } func (t fcIPPSet) Shift(sm *parser.SM, e rune) error { next := fcSet[fcIPPInst, IPP]{ newToken: func() fcIPPInst { return fcIPPInst{} }, } if len(sm.Top().Nodes) == 0 { return sm.Push(next).Shift(sm, e) } return parser.NewUnrecognized("ipp set", e) } func (t fcIPPSet) Coalesce(nodes []*parser.CSTNode) any { return nodes[0].Coalesce() } func (t fcIPPSet) Value() *bytes.Buffer { return nil } /*********************** IPPInst **************************/ type fcIPPInst struct { } func (t fcIPPInst) Shift(sm *parser.SM, e rune) error { length := len(sm.Top().Nodes) if length == 0 { return sm.Push(fcIPP{}).Shift(sm, e) } switch { case e == '(': if length > 1 { return fmt.Errorf("ipp instance: unexpected state: %+v; event %v", sm, e) } // ippinst -> ipp -> octet | ippname ippVal := sm.Top().Nodes[0].Nodes[0].ASTNode.Value().Bytes() if bytes.Equal(ippVal, []byte("icmp")) || bytes.Equal(ippVal, []byte("1")) { sm.Push(fcICMPParms{}) } if bytes.Equal(ippVal, []byte("tcp")) || bytes.Equal(ippVal, []byte("6")) { sm.Push(fcTCPParms{}) } if bytes.Equal(ippVal, []byte("udp")) || bytes.Equal(ippVal, []byte("17")) { sm.Push(fcUDPParms{}) } default: return parser.NewUnrecognized("ipp instance", e) } return nil } func (t fcIPPInst) Coalesce(nodes []*parser.CSTNode) any { if len(nodes) == 2 { return nodes[1].Coalesce() } return nodes[0].Coalesce() } func (t fcIPPInst) Value() *bytes.Buffer { return nil } /************************* IPP ****************************/ type fcIPP struct { } func (t fcIPP) Shift(sm *parser.SM, e rune) error { switch { case isDigit(e): return sm.Push(fcIPPDecimal{}).Shift(sm, e) case isAlpha(e): buf := new(bytes.Buffer) buf.WriteRune(e) sm.Push(fcIPPName{val: buf}) default: return parser.NewUnrecognized("ipp", e) } return nil } func (t fcIPP) Coalesce(nodes []*parser.CSTNode) any { return nodes[0].Coalesce() } func (t fcIPP) Value() *bytes.Buffer { return nil } /*********************** IPPName **************************/ var ippNameToProto = map[string]byte{ "any": 0, "icmp": 1, "tcp": 6, "udp": 17, } type fcIPPName struct { val *bytes.Buffer } func (t fcIPPName) Shift(sm *parser.SM, e rune) error { switch { case isAlpha(e): t.val.WriteRune(e) default: name := string(t.val.Bytes()) if _, ok := ippNameToProto[name]; !ok { return fmt.Errorf("ipp name: unknown ipp name '%s'", name) } return parser.NewUnrecognized("ipp name", e) } return nil } func (t fcIPPName) Coalesce(_ []*parser.CSTNode) any { switch string(t.val.Bytes()) { case "any": return ipProtocol(0) case "icmp": return ippICMP{} case "tcp": return ippTCP{} case "udp": return ippUDP{} default: panic("BUG: unknown ipp name") } } func (t fcIPPName) Value() *bytes.Buffer { return t.val } /********************** IPPDecimal ************************/ type fcIPPDecimal struct { } func (t fcIPPDecimal) Shift(sm *parser.SM, e rune) error { if len(sm.Top().Nodes) == 0 { return sm.Push(fcOctet{}).Shift(sm, e) } return parser.NewUnrecognized("ipp decimal", e) } func (t fcIPPDecimal) Coalesce(nodes []*parser.CSTNode) any { oct := nodes[0].Coalesce().(byte) switch oct { case 1: return ippICMP{} case 6: return ippTCP{} case 17: return ippUDP{} default: return ipProtocol(oct) } } func (t fcIPPDecimal) Value() *bytes.Buffer { return nil } /********************** Parameters ************************/ type fcICMPParms struct { } func (t fcICMPParms) Value() *bytes.Buffer { return nil } func (t fcICMPParms) Shift(sm *parser.SM, e rune) error { next := fcVector[fcOctet, byte]{ dim: 2, newToken: func() fcOctet { return fcOctet{val: new(bytes.Buffer)} }, } if len(sm.Top().Nodes) == 0 { return sm.Push(next).Shift(sm, e) } return parser.NewUnrecognized("icmp params", e) } func (t fcICMPParms) Coalesce(nodes []*parser.CSTNode) any { return ippICMP{ icmpType: nodes[0].Nodes[0].Coalesce().(byte), code: nodes[0].Nodes[1].Coalesce().(byte), } } type fcTCPParms struct { } func (t fcTCPParms) Shift(sm *parser.SM, e rune) error { next := fcVector[fcPort, uint16]{ dim: 2, newToken: func() fcPort { return fcPort{val: new(bytes.Buffer)} }, } if len(sm.Top().Nodes) == 0 { return sm.Push(next).Shift(sm, e) } return parser.NewUnrecognized("tcp params", e) } func (t fcTCPParms) Coalesce(nodes []*parser.CSTNode) any { return ippTCP{ spt: nodes[0].Nodes[0].Coalesce().(uint16), dpt: nodes[0].Nodes[1].Coalesce().(uint16), } } func (t fcTCPParms) Value() *bytes.Buffer { return nil } type fcUDPParms struct { } func (t fcUDPParms) Shift(sm *parser.SM, e rune) error { next := fcVector[fcPort, uint16]{ dim: 2, newToken: func() fcPort { return fcPort{val: new(bytes.Buffer)} }, } if len(sm.Top().Nodes) == 0 { return sm.Push(next).Shift(sm, e) } return parser.NewUnrecognized("udp params", e) } func (t fcUDPParms) Coalesce(nodes []*parser.CSTNode) any { return ippUDP{ spt: nodes[0].Nodes[0].Coalesce().(uint16), dpt: nodes[0].Nodes[1].Coalesce().(uint16), } } func (t fcUDPParms) Value() *bytes.Buffer { return nil } type fcPort struct { val *bytes.Buffer } func (t fcPort) Shift(sm *parser.SM, e rune) error { return accNatural(e, "65535", t.val, "port") } func (t fcPort) Coalesce(_ []*parser.CSTNode) any { p, _ := strconv.Atoi(string(t.val.Bytes())) return uint16(p) } func (t fcPort) Value() *bytes.Buffer { return t.val }