Sergiusz Bazanski | 6eaaaf9 | 2019-08-02 01:25:31 +0200 | [diff] [blame] | 1 | package provider |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | |
| 6 | pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto" |
| 7 | ) |
| 8 | |
| 9 | // A raw, unparsed RPSL name/value pair. |
| 10 | type rpslRawAttribute struct { |
| 11 | name string |
| 12 | value string |
| 13 | } |
| 14 | |
| 15 | // parseAttributes converts raw RPSL attributes into parsed, structured protos. |
| 16 | func parseAttributes(attrs []rpslRawAttribute) []*pb.IRRAttribute { |
| 17 | res := []*pb.IRRAttribute{} |
| 18 | |
| 19 | for _, attr := range attrs { |
| 20 | switch attr.name { |
| 21 | case "remarks": |
| 22 | res = append(res, &pb.IRRAttribute{ |
| 23 | Value: &pb.IRRAttribute_Remarks{attr.value}, |
| 24 | }) |
| 25 | case "import": |
| 26 | ie := parseImportExport(attr.value) |
| 27 | if ie != nil { |
| 28 | res = append(res, &pb.IRRAttribute{ |
| 29 | Value: &pb.IRRAttribute_Import{ie}, |
| 30 | }) |
| 31 | } |
| 32 | case "export": |
| 33 | ie := parseImportExport(attr.value) |
| 34 | if ie != nil { |
| 35 | res = append(res, &pb.IRRAttribute{ |
| 36 | Value: &pb.IRRAttribute_Export{ie}, |
| 37 | }) |
| 38 | } |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | return res |
| 43 | } |
| 44 | |
| 45 | // parseImportExport tries to parse the RPSL subset for import/export |
| 46 | // attributes. |
| 47 | // It's a hand-written single-pass parser which makes it not very good. |
| 48 | // See rpsl_test.go for examples. |
| 49 | func parseImportExport(expression string) *pb.IRRAttribute_ImportExport { |
| 50 | popToken := func(s string) (string, string) { |
| 51 | s = strings.TrimSpace(s) |
| 52 | fields := strings.Fields(s) |
| 53 | if len(fields) < 2 { |
| 54 | return "", s |
| 55 | } |
| 56 | return fields[0], strings.TrimSpace(s[len(fields[0]):]) |
| 57 | } |
| 58 | |
| 59 | res := &pb.IRRAttribute_ImportExport{} |
| 60 | |
| 61 | expr := expression |
| 62 | var t string |
| 63 | t, expr = popToken(expr) |
| 64 | |
| 65 | for { |
| 66 | switch t { |
| 67 | case "": |
| 68 | break |
| 69 | case "protocol": |
| 70 | t, expr = popToken(expr) |
| 71 | res.ProtocolFrom = t |
| 72 | t, expr = popToken(expr) |
| 73 | continue |
| 74 | case "into": |
| 75 | t, expr = popToken(expr) |
| 76 | res.ProtocolInto = t |
| 77 | t, expr = popToken(expr) |
| 78 | continue |
| 79 | case "from": |
| 80 | fallthrough |
| 81 | case "to": |
| 82 | t, expr = popToken(expr) |
| 83 | peering := t |
| 84 | actions := []string{} |
| 85 | router_us := "" |
| 86 | router_them := "" |
| 87 | |
| 88 | t2, expr2 := popToken(expr) |
| 89 | for { |
| 90 | switch t2 { |
| 91 | case "at": |
| 92 | t2, expr2 = popToken(expr2) |
| 93 | router_us = t2 |
| 94 | t2, expr2 = popToken(expr2) |
| 95 | continue |
| 96 | case "action": |
| 97 | parts := strings.SplitN(expr2, ";", 2) |
| 98 | if len(parts) != 2 { |
| 99 | // malformed action, no ';' found - bail |
| 100 | return nil |
| 101 | } |
| 102 | actions = append(actions, parts[0]) |
| 103 | expr2 = strings.TrimSpace(parts[1]) |
| 104 | t2, expr2 = popToken(expr2) |
| 105 | continue |
| 106 | case "": |
| 107 | fallthrough |
| 108 | case "accept": |
| 109 | fallthrough |
| 110 | case "announce": |
| 111 | t = t2 |
| 112 | expr = expr2 |
| 113 | break |
| 114 | default: |
| 115 | if router_them == "" { |
| 116 | router_them = t2 |
| 117 | t2, expr2 = popToken(expr2) |
| 118 | continue |
| 119 | } |
| 120 | t = t2 |
| 121 | expr = expr2 |
| 122 | break |
| 123 | } |
| 124 | break |
| 125 | } |
| 126 | res.Expressions = append(res.Expressions, &pb.IRRAttribute_ImportExport_Expression{ |
| 127 | Peering: peering, |
| 128 | RouterUs: router_us, |
| 129 | RouterThem: router_them, |
| 130 | Actions: actions, |
| 131 | }) |
| 132 | continue |
| 133 | case "accept": |
| 134 | fallthrough |
| 135 | case "announce": |
| 136 | res.Filter = expr |
| 137 | t = "" |
| 138 | continue |
| 139 | default: |
| 140 | return nil |
| 141 | } |
| 142 | break |
| 143 | } |
| 144 | |
| 145 | return res |
| 146 | } |