youtubebeat/vendor/github.com/elastic/beats/filebeat/inputsource/tcp/client.go

133 lines
3.7 KiB
Go

// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package tcp
import (
"bufio"
"crypto/tls"
"crypto/x509"
"net"
"time"
"github.com/pkg/errors"
"github.com/elastic/beats/filebeat/inputsource"
"github.com/elastic/beats/libbeat/common/transport/tlscommon"
"github.com/elastic/beats/libbeat/logp"
)
// Client is a remote client.
type client struct {
conn net.Conn
log *logp.Logger
callback inputsource.NetworkFunc
done chan struct{}
metadata inputsource.NetworkMetadata
splitFunc bufio.SplitFunc
maxMessageSize uint64
timeout time.Duration
}
func newClient(
conn net.Conn,
log *logp.Logger,
callback inputsource.NetworkFunc,
splitFunc bufio.SplitFunc,
maxReadMessage uint64,
timeout time.Duration,
) *client {
client := &client{
conn: conn,
log: log.With("remote_address", conn.RemoteAddr()),
callback: callback,
done: make(chan struct{}),
splitFunc: splitFunc,
maxMessageSize: maxReadMessage,
timeout: timeout,
metadata: inputsource.NetworkMetadata{
RemoteAddr: conn.RemoteAddr(),
TLS: extractSSLInformation(conn),
},
}
extractSSLInformation(conn)
return client
}
func (c *client) handle() error {
r := NewResetableLimitedReader(NewDeadlineReader(c.conn, c.timeout), c.maxMessageSize)
buf := bufio.NewReader(r)
scanner := bufio.NewScanner(buf)
scanner.Split(c.splitFunc)
for scanner.Scan() {
err := scanner.Err()
if err != nil {
// we are forcing a close on the socket, lets ignore any error that could happen.
select {
case <-c.done:
break
default:
}
// This is a user defined limit and we should notify the user.
if IsMaxReadBufferErr(err) {
c.log.Errorw("client error", "error", err)
}
return errors.Wrap(err, "tcp client error")
}
r.Reset()
c.callback(scanner.Bytes(), c.metadata)
}
// We are out of the scanner, either we reached EOF or another fatal error occurred.
// like we failed to complete the TLS handshake or we are missing the client certificate when
// mutual auth is on, which is the default.
if err := scanner.Err(); err != nil {
return err
}
return nil
}
func (c *client) close() {
close(c.done)
c.conn.Close()
}
func extractSSLInformation(c net.Conn) *inputsource.TLSMetadata {
if tls, ok := c.(*tls.Conn); ok {
state := tls.ConnectionState()
return &inputsource.TLSMetadata{
TLSVersion: tlscommon.ResolveTLSVersion(state.Version),
CipherSuite: tlscommon.ResolveCipherSuite(state.CipherSuite),
ServerName: state.ServerName,
PeerCertificates: extractCertificate(state.PeerCertificates),
}
}
return nil
}
func extractCertificate(certificates []*x509.Certificate) []string {
strCertificate := make([]string, len(certificates))
for idx, c := range certificates {
// Ignore errors here, problematics cert have failed
//the handshake at this point.
b, _ := x509.MarshalPKIXPublicKey(c.PublicKey)
strCertificate[idx] = string(b)
}
return strCertificate
}