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  }