github.com/zhyoulun/cilium@v1.6.12/proxylib/testparsers/blockparser.go (about) 1 // Copyright 2018 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package testparsers 16 17 import ( 18 "bytes" 19 "fmt" 20 "strconv" 21 22 . "github.com/cilium/cilium/proxylib/proxylib" 23 24 "github.com/cilium/proxy/go/cilium/api" 25 log "github.com/sirupsen/logrus" 26 ) 27 28 // 29 // Block parser used for testing 30 // 31 32 type BlockParserFactory struct{} 33 34 var blockParserFactory *BlockParserFactory 35 36 const ( 37 blockParserName = "test.blockparser" 38 ) 39 40 func init() { 41 log.Debug("init(): Registering blockParserFactory") 42 RegisterParserFactory(blockParserName, blockParserFactory) 43 } 44 45 type BlockParser struct { 46 connection *Connection 47 inserted int 48 } 49 50 func (p *BlockParserFactory) Create(connection *Connection) Parser { 51 log.Debugf("BlockParserFactory: Create: %v", connection) 52 return &BlockParser{connection: connection} 53 } 54 55 func getBlock(data [][]byte) ([]byte, int, int, error) { 56 var block bytes.Buffer 57 58 offset := 0 59 block_len := 0 60 have_length := false 61 missing := 0 62 63 for _, s := range data { 64 if !have_length { 65 index := bytes.IndexByte(s[offset:], ':') 66 if index < 0 { 67 block.Write(s[offset:]) 68 if block.Len() > 0 { 69 missing = 1 // require at least one more if something was received 70 } 71 } else { 72 block.Write(s[offset : offset+index]) 73 offset += index 74 75 // Now 'block' contains everything before the ':', parse it as a decimal number 76 // indicating the length of the frame AFTER the ':' 77 len64, err := strconv.ParseUint(block.String(), 10, 64) 78 if err != nil { 79 return block.Bytes(), 0, 0, err 80 } 81 block_len = int(len64) 82 if block_len <= block.Len() { 83 return block.Bytes(), 0, 0, fmt.Errorf("Block length too short") 84 } 85 have_length = true 86 missing = block_len - block.Len() 87 } 88 } 89 if have_length { 90 s_len := len(s) - offset 91 92 if missing <= s_len { 93 block.Write(s[offset : offset+missing]) 94 return block.Bytes(), block_len, 0, nil 95 } else { 96 block.Write(s[offset:]) 97 missing -= s_len 98 } 99 } 100 offset = 0 101 } 102 103 return block.Bytes(), block_len, missing, nil 104 } 105 106 // 107 // Parses individual blocks that must start with one of: 108 // "PASS" the block is passed 109 // "DROP" the block is dropped 110 // "INJECT" the block is injected in reverse direction 111 // "INSERT" the block is injected in current direction 112 // 113 func (p *BlockParser) OnData(reply, endStream bool, data [][]byte) (OpType, int) { 114 block, block_len, missing, err := getBlock(data) 115 if err != nil { 116 log.WithError(err).Warnf("BlockParser: Invalid frame length") 117 return ERROR, int(ERROR_INVALID_FRAME_LENGTH) 118 } 119 120 if p.inserted > 0 { 121 if p.inserted == block_len { 122 p.inserted = 0 123 return DROP, block_len 124 } 125 // partial insert in progress 126 n := p.connection.Inject(reply, []byte(block)[p.inserted:]) 127 // Drop the INJECT block in the current direction 128 p.inserted += n 129 return INJECT, n 130 } 131 132 if !reply { 133 log.Debugf("BlockParser: Request: %s", block) 134 } else { 135 log.Debugf("BlockParser: Response: %s", block) 136 } 137 138 if missing == 0 && block_len == 0 { 139 // Nothing received, don't know if more will be coming; do nothing 140 return NOP, 0 141 } 142 143 log.Debugf("BlockParser: missing: %d", missing) 144 145 if bytes.Contains(block, []byte("PASS")) { 146 p.connection.Log(cilium.EntryType_Request, &cilium.LogEntry_GenericL7{ 147 GenericL7: &cilium.L7LogEntry{ 148 Proto: blockParserName, 149 Fields: map[string]string{ 150 "status": "200", 151 }, 152 }, 153 }) 154 return PASS, block_len 155 } 156 if bytes.Contains(block, []byte("DROP")) { 157 p.connection.Log(cilium.EntryType_Denied, &cilium.LogEntry_GenericL7{ 158 GenericL7: &cilium.L7LogEntry{ 159 Proto: blockParserName, 160 Fields: map[string]string{ 161 "status": "503", 162 }, 163 }, 164 }) 165 return DROP, block_len 166 } 167 168 if missing > 0 { 169 // Partial block received, ask for more 170 return MORE, missing 171 } 172 173 if bytes.Contains(block, []byte("INJECT")) { 174 // Inject block in the reverse direction 175 p.connection.Inject(!reply, []byte(block)) 176 // Drop the INJECT block in the current direction 177 return DROP, block_len 178 } 179 if bytes.Contains(block, []byte("INSERT")) { 180 // Inject the block in the current direction 181 n := p.connection.Inject(reply, []byte(block)) 182 // Drop the INJECT block in the current direction 183 p.inserted = n 184 return INJECT, n 185 } 186 187 return ERROR, int(ERROR_INVALID_FRAME_TYPE) 188 }