github.com/matrixorigin/matrixone@v1.2.0/pkg/proxy/handshake.go (about) 1 // Copyright 2021 - 2023 Matrix Origin 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 proxy 16 17 import ( 18 "bytes" 19 "context" 20 "crypto/tls" 21 "encoding/binary" 22 "github.com/fagongzi/goetty/v2" 23 "github.com/matrixorigin/matrixone/pkg/common/moerr" 24 "github.com/matrixorigin/matrixone/pkg/frontend" 25 ) 26 27 // writeInitialHandshake sends the initial handshake to client. 28 func (c *clientConn) writeInitialHandshake() error { 29 // TODO(volgariver6): serverVersion is not correct when the config of 30 // ParameterUnit.SV.MoVersion is not empty. 31 return c.mysqlProto.WritePacket(c.mysqlProto.MakeHandshakePayload()) 32 } 33 34 func (c *clientConn) handleHandshakeResp() error { 35 // The proxy reads login request from client. 36 pack, err := c.readPacket() 37 if err != nil { 38 return err 39 } 40 c.mysqlProto.AddSequenceId(1) 41 // Save the login packet in client connection, it will be used 42 // in the future. 43 c.handshakePack = pack 44 45 // Parse the login information and returns whether ssl is needed. 46 // Also, we can get connection attributes from client if it sets 47 // some. 48 ssl, err := c.mysqlProto.HandleHandshake(c.ctx, pack.Payload) 49 if err != nil { 50 return err 51 } 52 if ssl { 53 if err = c.upgradeToTLS(); err != nil { 54 return err 55 } 56 return c.handleHandshakeResp() 57 } 58 59 // parse tenant information from client login request. 60 if err := c.clientInfo.parse(c.mysqlProto.GetUserName()); err != nil { 61 return err 62 } 63 64 li := &c.clientInfo.labelInfo 65 c.clientInfo.labelInfo = newLabelInfo(c.clientInfo.Tenant, li.Labels) 66 return nil 67 } 68 69 // upgradeToTLS upgrades the connection to TLS connection. 70 func (c *clientConn) upgradeToTLS() error { 71 if c.tlsConfig == nil { 72 return moerr.NewInternalErrorNoCtx("TLS config is invalid") 73 } 74 // TLS handshake packet from client might have been read into the buffer, use a wrapped conn to 75 // avoid losing handshake packets. 76 tlsConn := tls.Server(c.conn.(goetty.BufferedIOSession).BufferedConn(), c.tlsConfig) 77 ctx, cancel := context.WithTimeout(context.Background(), c.tlsConnectTimeout) 78 defer cancel() 79 if err := tlsConn.HandshakeContext(ctx); err != nil { 80 return moerr.NewInternalError(ctx, "TSL handshake error: %v", err) 81 } 82 c.conn.UseConn(tlsConn) 83 return nil 84 } 85 86 func (s *serverConn) parseConnID(p *frontend.Packet) error { 87 if len(p.Payload) < 2 { 88 return moerr.NewInternalErrorNoCtx("protocol error: payload is too short") 89 } 90 // From the beginning of payload. 91 pos := 0 92 // Pass the protocol version. 93 pos += 1 94 zeroPos := bytes.IndexByte(p.Payload[pos:], 0) 95 if zeroPos == -1 { 96 return moerr.NewInternalErrorNoCtx("protocol error: cannot get null string") 97 } 98 // Pass the server version string. 99 pos += zeroPos + 1 100 if pos+3 >= int(p.Length) { 101 return moerr.NewInternalErrorNoCtx("protocol error: cannot parse connection ID") 102 } 103 s.connID = binary.LittleEndian.Uint32(p.Payload[pos : pos+4]) 104 return nil 105 } 106 107 // readInitialHandshake reads initial handshake from CN server. The result 108 // is useless. 109 func (s *serverConn) readInitialHandshake() error { 110 r, err := s.readPacket() 111 if err != nil { 112 return err 113 } 114 if err := s.parseConnID(r); err != nil { 115 return err 116 } 117 return nil 118 } 119 120 // writeHandshakeResp writes the auth packet to CN server. 121 func (s *serverConn) writeHandshakeResp(handshakeResp *frontend.Packet) (*frontend.Packet, error) { 122 if err := s.mysqlProto.WritePacket(handshakeResp.Payload); err != nil { 123 return nil, err 124 } 125 // The CN server send a response back to indicate if the auth packet 126 // is OK to login. 127 data, err := s.readPacket() 128 if err != nil { 129 return nil, err 130 } 131 return data, nil 132 }