mirror of
https://github.com/gravitational/teleport
synced 2024-10-22 18:23:25 +00:00
125 lines
3.4 KiB
Go
125 lines
3.4 KiB
Go
|
/**
|
||
|
* Copyright 2013 Rackspace
|
||
|
*
|
||
|
* Licensed 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.
|
||
|
*
|
||
|
* Note: original copyright is preserved on purpose
|
||
|
*/
|
||
|
|
||
|
package multiplexer
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"fmt"
|
||
|
"net"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/gravitational/trace"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
// TCP4 is TCP over IPv4
|
||
|
TCP4 = "TCP4"
|
||
|
// TCP6 is tCP over IPv6
|
||
|
TCP6 = "TCP6"
|
||
|
// Unknown is unsupported or unknown protocol
|
||
|
UNKNOWN = "UNKNOWN"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
proxyCRLF = "\r\n"
|
||
|
proxySep = " "
|
||
|
)
|
||
|
|
||
|
// ProxyLine is HA Proxy protocol version 1
|
||
|
// https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
||
|
// Original implementation here: https://github.com/racker/go-proxy-protocol
|
||
|
type ProxyLine struct {
|
||
|
Protocol string
|
||
|
Source net.TCPAddr
|
||
|
Destination net.TCPAddr
|
||
|
}
|
||
|
|
||
|
// String returns on-the wire string representation of the proxy line
|
||
|
func (p *ProxyLine) String() string {
|
||
|
return fmt.Sprintf("PROXY %s %s %s %d %d\r\n", p.Protocol, p.Source.IP.String(), p.Destination.IP.String(), p.Source.Port, p.Destination.Port)
|
||
|
}
|
||
|
|
||
|
// ReadProxyLine reads proxy line protocol from the reader
|
||
|
func ReadProxyLine(reader *bufio.Reader) (*ProxyLine, error) {
|
||
|
line, err := reader.ReadString('\n')
|
||
|
if err != nil {
|
||
|
return nil, trace.Wrap(err)
|
||
|
}
|
||
|
if !strings.HasSuffix(line, proxyCRLF) {
|
||
|
return nil, trace.BadParameter("expected CRLF in proxy protocol, got something else")
|
||
|
}
|
||
|
tokens := strings.Split(line[:len(line)-2], proxySep)
|
||
|
ret := ProxyLine{}
|
||
|
if len(tokens) < 6 {
|
||
|
return nil, trace.BadParameter("malformed PROXY line protocol string")
|
||
|
}
|
||
|
switch tokens[1] {
|
||
|
case TCP4:
|
||
|
ret.Protocol = TCP4
|
||
|
case TCP6:
|
||
|
ret.Protocol = TCP6
|
||
|
default:
|
||
|
ret.Protocol = UNKNOWN
|
||
|
}
|
||
|
sourceIP, err := parseIP(ret.Protocol, tokens[2])
|
||
|
if err != nil {
|
||
|
return nil, trace.Wrap(err)
|
||
|
}
|
||
|
destIP, err := parseIP(ret.Protocol, tokens[3])
|
||
|
if err != nil {
|
||
|
return nil, trace.Wrap(err)
|
||
|
}
|
||
|
sourcePort, err := parsePortNumber(tokens[4])
|
||
|
if err != nil {
|
||
|
return nil, trace.Wrap(err)
|
||
|
}
|
||
|
destPort, err := parsePortNumber(tokens[5])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
ret.Source = net.TCPAddr{IP: sourceIP, Port: sourcePort}
|
||
|
ret.Destination = net.TCPAddr{IP: destIP, Port: destPort}
|
||
|
return &ret, nil
|
||
|
}
|
||
|
|
||
|
func parsePortNumber(portString string) (int, error) {
|
||
|
port, err := strconv.Atoi(portString)
|
||
|
if err != nil {
|
||
|
return -1, trace.BadParameter("bad port %q: %v", port, err)
|
||
|
}
|
||
|
if port < 0 || port > 65535 {
|
||
|
return -1, trace.BadParameter("port %q not in supported range [0...65535]", portString)
|
||
|
}
|
||
|
return port, nil
|
||
|
}
|
||
|
|
||
|
func parseIP(protocol string, addrString string) (net.IP, error) {
|
||
|
addr := net.ParseIP(addrString)
|
||
|
switch {
|
||
|
case len(addr) == 0:
|
||
|
return nil, trace.BadParameter("failed to parse address")
|
||
|
case addr.To4() != nil && protocol != TCP4:
|
||
|
return nil, trace.BadParameter("got IPV4 address %q for IPV6 proto %q", addr.String(), protocol)
|
||
|
case addr.To4() == nil && protocol == TCP6:
|
||
|
return nil, trace.BadParameter("got IPV6 address %v %q for IPV4 proto %q", len(addr), addr.String(), protocol)
|
||
|
}
|
||
|
return addr, nil
|
||
|
}
|