Refactor ssl-passthrough
This commit is contained in:
parent
0d9769c80d
commit
2db989caa6
8 changed files with 222 additions and 73 deletions
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
|
@ -164,6 +164,10 @@
|
||||||
"Comment": "v0.1.0",
|
"Comment": "v0.1.0",
|
||||||
"Rev": "2942f905437b665326fe044c49edb2094df13b37"
|
"Rev": "2942f905437b665326fe044c49edb2094df13b37"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ImportPath": "github.com/paultag/sniff/parser",
|
||||||
|
"Rev": "c36b8585a41425573d9e3e1890bf3b6ac89a3828"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/pborman/uuid",
|
"ImportPath": "github.com/pborman/uuid",
|
||||||
"Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4"
|
"Rev": "ca53cad383cad2479bbba7f7a1a05797ec1386e4"
|
||||||
|
|
|
@ -80,11 +80,8 @@ func newNGINXController() ingress.Controller {
|
||||||
binary: ngx,
|
binary: ngx,
|
||||||
configmap: &api_v1.ConfigMap{},
|
configmap: &api_v1.ConfigMap{},
|
||||||
isIPV6Enabled: isIPv6Enabled(),
|
isIPV6Enabled: isIPv6Enabled(),
|
||||||
<<<<<<< fc67b1d5e2a51cc0037a434583af6530efa1a59c
|
|
||||||
resolver: h,
|
resolver: h,
|
||||||
=======
|
|
||||||
proxy: &proxy{},
|
proxy: &proxy{},
|
||||||
>>>>>>>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", ":443")
|
listener, err := net.Listen("tcp", ":443")
|
||||||
|
@ -143,7 +140,8 @@ type NGINXController struct {
|
||||||
|
|
||||||
storeLister ingress.StoreLister
|
storeLister ingress.StoreLister
|
||||||
|
|
||||||
binary string
|
binary string
|
||||||
|
resolver []net.IP
|
||||||
|
|
||||||
cmdArgs []string
|
cmdArgs []string
|
||||||
|
|
||||||
|
@ -156,11 +154,7 @@ type NGINXController struct {
|
||||||
// returns true if IPV6 is enabled in the pod
|
// returns true if IPV6 is enabled in the pod
|
||||||
isIPV6Enabled bool
|
isIPV6Enabled bool
|
||||||
|
|
||||||
<<<<<<< fc67b1d5e2a51cc0037a434583af6530efa1a59c
|
|
||||||
resolver []net.IP
|
|
||||||
=======
|
|
||||||
proxy *proxy
|
proxy *proxy
|
||||||
>>>>>>>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start start a new NGINX master process running in foreground.
|
// Start start a new NGINX master process running in foreground.
|
||||||
|
@ -476,6 +470,7 @@ func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) ([]byte, er
|
||||||
for _, pb := range ingressCfg.PassthroughBackends {
|
for _, pb := range ingressCfg.PassthroughBackends {
|
||||||
svc := pb.Service
|
svc := pb.Service
|
||||||
if svc == nil {
|
if svc == nil {
|
||||||
|
glog.Warningf("missing service for PassthroughBackends %v", pb.Backend)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
port, err := strconv.Atoi(pb.Port.String())
|
port, err := strconv.Atoi(pb.Port.String())
|
||||||
|
|
|
@ -40,14 +40,14 @@ func (p *proxy) Handle(conn net.Conn) {
|
||||||
|
|
||||||
length, err := conn.Read(data)
|
length, err := conn.Read(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(4).Infof("Error reading the first 4k of the connection: %s", err)
|
glog.V(4).Infof("error reading the first 4k of the connection: %s", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var proxy *server
|
var proxy *server
|
||||||
hostname, err := parser.GetHostname(data[:])
|
hostname, err := parser.GetHostname(data[:])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
glog.V(2).Infof("Parsed hostname: %s", hostname)
|
glog.V(2).Infof("parsed hostname: %s", hostname)
|
||||||
proxy = p.Get(hostname)
|
proxy = p.Get(hostname)
|
||||||
if proxy == nil {
|
if proxy == nil {
|
||||||
return
|
return
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
{{ $healthzURI := .HealthzURI }}
|
{{ $healthzURI := .HealthzURI }}
|
||||||
{{ $backends := .Backends }}
|
{{ $backends := .Backends }}
|
||||||
{{ $proxyHeaders := .ProxySetHeaders }}
|
{{ $proxyHeaders := .ProxySetHeaders }}
|
||||||
{{ $passthroughBackends := .PassthroughBackends }}
|
|
||||||
daemon off;
|
daemon off;
|
||||||
|
|
||||||
worker_processes {{ $cfg.WorkerProcesses }};
|
worker_processes {{ $cfg.WorkerProcesses }};
|
||||||
|
@ -476,20 +475,6 @@ http {
|
||||||
}
|
}
|
||||||
|
|
||||||
stream {
|
stream {
|
||||||
<<<<<<< fc67b1d5e2a51cc0037a434583af6530efa1a59c
|
|
||||||
{{ if gt (len $passthroughBackends) 0 }}
|
|
||||||
# map FQDN that requires SSL passthrough
|
|
||||||
map $ssl_preread_server_name $stream_upstream {
|
|
||||||
hostnames;
|
|
||||||
{{ range $i, $passthrough := .PassthroughBackends }}
|
|
||||||
{{ $passthrough.Hostname }} {{ $passthrough.Backend }};
|
|
||||||
{{ end }}
|
|
||||||
# send SSL traffic to this nginx in a different port
|
|
||||||
default nginx-ssl-backend;
|
|
||||||
}
|
|
||||||
=======
|
|
||||||
>>>>>>>
|
|
||||||
|
|
||||||
log_format log_stream {{ $cfg.LogFormatStream }};
|
log_format log_stream {{ $cfg.LogFormatStream }};
|
||||||
|
|
||||||
{{ if $cfg.DisableAccessLog }}
|
{{ if $cfg.DisableAccessLog }}
|
||||||
|
|
|
@ -381,6 +381,8 @@ func (ic *GenericController) syncIngress(key interface{}) error {
|
||||||
passUpstreams = append(passUpstreams, &ingress.SSLPassthroughBackend{
|
passUpstreams = append(passUpstreams, &ingress.SSLPassthroughBackend{
|
||||||
Backend: loc.Backend,
|
Backend: loc.Backend,
|
||||||
Hostname: server.Hostname,
|
Hostname: server.Hostname,
|
||||||
|
Service: loc.Service,
|
||||||
|
Port: loc.Port,
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -620,6 +622,8 @@ func (ic *GenericController) getBackendServers() ([]*ingress.Backend, []*ingress
|
||||||
loc.Backend = ups.Name
|
loc.Backend = ups.Name
|
||||||
loc.IsDefBackend = false
|
loc.IsDefBackend = false
|
||||||
loc.Backend = ups.Name
|
loc.Backend = ups.Name
|
||||||
|
loc.Port = ups.Port
|
||||||
|
loc.Service = ups.Service
|
||||||
mergeLocationAnnotations(loc, anns)
|
mergeLocationAnnotations(loc, anns)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -631,6 +635,8 @@ func (ic *GenericController) getBackendServers() ([]*ingress.Backend, []*ingress
|
||||||
Path: nginxPath,
|
Path: nginxPath,
|
||||||
Backend: ups.Name,
|
Backend: ups.Name,
|
||||||
IsDefBackend: false,
|
IsDefBackend: false,
|
||||||
|
Service: ups.Service,
|
||||||
|
Port: ups.Port,
|
||||||
}
|
}
|
||||||
mergeLocationAnnotations(loc, anns)
|
mergeLocationAnnotations(loc, anns)
|
||||||
server.Locations = append(server.Locations, loc)
|
server.Locations = append(server.Locations, loc)
|
||||||
|
@ -641,7 +647,6 @@ func (ic *GenericController) getBackendServers() ([]*ingress.Backend, []*ingress
|
||||||
|
|
||||||
// Configure Backends[].SSLPassthrough
|
// Configure Backends[].SSLPassthrough
|
||||||
for _, upstream := range upstreams {
|
for _, upstream := range upstreams {
|
||||||
isHTTP := false
|
|
||||||
isHTTPSfrom := []*ingress.Server{}
|
isHTTPSfrom := []*ingress.Server{}
|
||||||
for _, server := range servers {
|
for _, server := range servers {
|
||||||
for _, location := range server.Locations {
|
for _, location := range server.Locations {
|
||||||
|
@ -650,26 +655,18 @@ func (ic *GenericController) getBackendServers() ([]*ingress.Backend, []*ingress
|
||||||
if location.Path == rootLocation {
|
if location.Path == rootLocation {
|
||||||
if location.Backend == defUpstreamName {
|
if location.Backend == defUpstreamName {
|
||||||
glog.Warningf("ignoring ssl passthrough of %v as it doesn't have a default backend (root context)", server.Hostname)
|
glog.Warningf("ignoring ssl passthrough of %v as it doesn't have a default backend (root context)", server.Hostname)
|
||||||
} else {
|
continue
|
||||||
isHTTPSfrom = append(isHTTPSfrom, server)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isHTTPSfrom = append(isHTTPSfrom, server)
|
||||||
}
|
}
|
||||||
} else {
|
continue
|
||||||
isHTTP = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(isHTTPSfrom) > 0 {
|
if len(isHTTPSfrom) > 0 {
|
||||||
if isHTTP {
|
upstream.SSLPassthrough = true
|
||||||
for _, server := range isHTTPSfrom {
|
|
||||||
glog.Warningf("backend type mismatch on %v, assuming HTTP on ssl passthrough host %v", upstream.Name, server.Hostname)
|
|
||||||
// removing this server from the PassthroughBackends slice
|
|
||||||
server.SSLPassthrough = false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
upstream.SSLPassthrough = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -761,42 +758,43 @@ func (ic *GenericController) createUpstreams(data []interface{}) map[string]*ing
|
||||||
path.Backend.ServiceName,
|
path.Backend.ServiceName,
|
||||||
path.Backend.ServicePort.String())
|
path.Backend.ServicePort.String())
|
||||||
|
|
||||||
upstream, ok := upstreams[name]
|
if _, ok := upstreams[name]; ok {
|
||||||
isNewUpstream := !ok
|
continue
|
||||||
|
|
||||||
if isNewUpstream {
|
|
||||||
glog.V(3).Infof("creating upstream %v", name)
|
|
||||||
upstream = newUpstream(name)
|
|
||||||
upstreams[name] = upstream
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !upstream.Secure {
|
glog.V(3).Infof("creating upstream %v", name)
|
||||||
upstream.Secure = secUpstream
|
upstreams[name] = newUpstream(name)
|
||||||
|
if !upstreams[name].Secure {
|
||||||
|
upstreams[name].Secure = secUpstream
|
||||||
}
|
}
|
||||||
|
if upstreams[name].SessionAffinity.AffinityType == "" {
|
||||||
if upstream.SessionAffinity.AffinityType == "" {
|
upstreams[name].SessionAffinity.AffinityType = affinity.AffinityType
|
||||||
upstream.SessionAffinity.AffinityType = affinity.AffinityType
|
|
||||||
if affinity.AffinityType == "cookie" {
|
if affinity.AffinityType == "cookie" {
|
||||||
upstream.SessionAffinity.CookieSessionAffinity.Name = affinity.CookieConfig.Name
|
upstreams[name].SessionAffinity.CookieSessionAffinity.Name = affinity.CookieConfig.Name
|
||||||
upstream.SessionAffinity.CookieSessionAffinity.Hash = affinity.CookieConfig.Hash
|
upstreams[name].SessionAffinity.CookieSessionAffinity.Hash = affinity.CookieConfig.Hash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if isNewUpstream {
|
svcKey := fmt.Sprintf("%v/%v", ing.GetNamespace(), path.Backend.ServiceName)
|
||||||
svcKey := fmt.Sprintf("%v/%v", ing.GetNamespace(), path.Backend.ServiceName)
|
endp, err := ic.serviceEndpoints(svcKey, path.Backend.ServicePort.String(), hz)
|
||||||
endp, err := ic.serviceEndpoints(svcKey, path.Backend.ServicePort.String(), hz)
|
if err != nil {
|
||||||
if err != nil {
|
glog.Warningf("error obtaining service endpoints: %v", err)
|
||||||
glog.Warningf("error obtaining service endpoints: %v", err)
|
continue
|
||||||
continue
|
|
||||||
}
|
|
||||||
upstream.Endpoints = endp
|
|
||||||
|
|
||||||
s, e, _ := ic.svcLister.Store.GetByKey(svcKey)
|
|
||||||
if e {
|
|
||||||
upstream.Service = s.(*api.Service)
|
|
||||||
}
|
|
||||||
upstream.Port = path.Backend.ServicePort
|
|
||||||
}
|
}
|
||||||
|
upstreams[name].Endpoints = endp
|
||||||
|
|
||||||
|
s, exists, err := ic.svcLister.Store.GetByKey(svcKey)
|
||||||
|
if err != nil {
|
||||||
|
glog.Warningf("error obtaining service: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
upstreams[name].Service = s.(*api.Service)
|
||||||
|
} else {
|
||||||
|
glog.Warningf("service %v does not exists", svcKey)
|
||||||
|
}
|
||||||
|
upstreams[name].Port = path.Backend.ServicePort
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -932,6 +930,7 @@ func (ic *GenericController) createServers(data []interface{},
|
||||||
// server already configured
|
// server already configured
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
servers[host] = &ingress.Server{
|
servers[host] = &ingress.Server{
|
||||||
Hostname: host,
|
Hostname: host,
|
||||||
Locations: []*ingress.Location{
|
Locations: []*ingress.Location{
|
||||||
|
|
|
@ -147,9 +147,9 @@ type Configuration struct {
|
||||||
// Backend describes one or more remote server/s (endpoints) associated with a service
|
// Backend describes one or more remote server/s (endpoints) associated with a service
|
||||||
type Backend struct {
|
type Backend struct {
|
||||||
// Name represents an unique api.Service name formatted as <namespace>-<name>-<port>
|
// Name represents an unique api.Service name formatted as <namespace>-<name>-<port>
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Service *api.Service
|
Service *api.Service `json:"service"`
|
||||||
Port intstr.IntOrString
|
Port intstr.IntOrString `json:"port"`
|
||||||
// This indicates if the communication protocol between the backend and the endpoint is HTTP or HTTPS
|
// This indicates if the communication protocol between the backend and the endpoint is HTTP or HTTPS
|
||||||
// Allowing the use of HTTPS
|
// Allowing the use of HTTPS
|
||||||
// The endpoint/s must provide a TLS connection.
|
// The endpoint/s must provide a TLS connection.
|
||||||
|
@ -245,6 +245,9 @@ type Location struct {
|
||||||
IsDefBackend bool `json:"isDefBackend"`
|
IsDefBackend bool `json:"isDefBackend"`
|
||||||
// Backend describes the name of the backend to use.
|
// Backend describes the name of the backend to use.
|
||||||
Backend string `json:"backend"`
|
Backend string `json:"backend"`
|
||||||
|
|
||||||
|
Service *api.Service `json:"service"`
|
||||||
|
Port intstr.IntOrString `json:"port"`
|
||||||
// BasicDigestAuth returns authentication configuration for
|
// BasicDigestAuth returns authentication configuration for
|
||||||
// an Ingress rule.
|
// an Ingress rule.
|
||||||
// +optional
|
// +optional
|
||||||
|
@ -292,9 +295,8 @@ type Location struct {
|
||||||
// The endpoints must provide the TLS termination exposing the required SSL certificate.
|
// The endpoints must provide the TLS termination exposing the required SSL certificate.
|
||||||
// The ingress controller only pipes the underlying TCP connection
|
// The ingress controller only pipes the underlying TCP connection
|
||||||
type SSLPassthroughBackend struct {
|
type SSLPassthroughBackend struct {
|
||||||
Namespace string
|
Service *api.Service `json:"service"`
|
||||||
Service *api.Service
|
Port intstr.IntOrString `json:"port"`
|
||||||
Port intstr.IntOrString
|
|
||||||
// Backend describes the endpoints to use.
|
// Backend describes the endpoints to use.
|
||||||
Backend string `json:"namespace,omitempty"`
|
Backend string `json:"namespace,omitempty"`
|
||||||
// Hostname returns the FQDN of the server
|
// Hostname returns the FQDN of the server
|
||||||
|
|
17
vendor/github.com/paultag/sniff/LICENSE
generated
vendored
Normal file
17
vendor/github.com/paultag/sniff/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
147
vendor/github.com/paultag/sniff/parser/parser.go
generated
vendored
Normal file
147
vendor/github.com/paultag/sniff/parser/parser.go
generated
vendored
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE. }}} */
|
||||||
|
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
var TLSHeaderLength = 5
|
||||||
|
|
||||||
|
/* This function is basically all most folks want to invoke out of this
|
||||||
|
* jumble of bits. This will take an incoming TLS Client Hello (including
|
||||||
|
* all the fuzzy bits at the beginning of it - fresh out of the socket) and
|
||||||
|
* go ahead and give us the SNI Name they want. */
|
||||||
|
func GetHostname(data []byte) (string, error) {
|
||||||
|
if len(data) == 0 || data[0] != 0x16 {
|
||||||
|
return "", fmt.Errorf("Doesn't look like a TLS Client Hello")
|
||||||
|
}
|
||||||
|
|
||||||
|
extensions, err := GetExtensionBlock(data)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sn, err := GetSNBlock(extensions)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sni, err := GetSNIBlock(sn)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(sni), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given a Server Name TLS Extension block, parse out and return the SNI
|
||||||
|
* (Server Name Indication) payload */
|
||||||
|
func GetSNIBlock(data []byte) ([]byte, error) {
|
||||||
|
index := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
if index >= len(data) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
length := int((data[index] << 8) + data[index+1])
|
||||||
|
endIndex := index + 2 + length
|
||||||
|
if data[index+2] == 0x00 { /* SNI */
|
||||||
|
sni := data[index+3:]
|
||||||
|
sniLength := int((sni[0] << 8) + sni[1])
|
||||||
|
return sni[2 : sniLength+2], nil
|
||||||
|
}
|
||||||
|
index = endIndex
|
||||||
|
}
|
||||||
|
return []byte{}, fmt.Errorf(
|
||||||
|
"Finished parsing the SN block without finding an SNI",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given a TLS Extensions data block, go ahead and find the SN block */
|
||||||
|
func GetSNBlock(data []byte) ([]byte, error) {
|
||||||
|
index := 0
|
||||||
|
|
||||||
|
if len(data) < 2 {
|
||||||
|
return []byte{}, fmt.Errorf("Not enough bytes to be an SN block")
|
||||||
|
}
|
||||||
|
|
||||||
|
extensionLength := int((data[index] << 8) + data[index+1])
|
||||||
|
data = data[2 : extensionLength+2]
|
||||||
|
|
||||||
|
for {
|
||||||
|
if index >= len(data) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
length := int((data[index+2] << 8) + data[index+3])
|
||||||
|
endIndex := index + 4 + length
|
||||||
|
if data[index] == 0x00 && data[index+1] == 0x00 {
|
||||||
|
return data[index+4 : endIndex], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
index = endIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
return []byte{}, fmt.Errorf(
|
||||||
|
"Finished parsing the Extension block without finding an SN block",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given a raw TLS Client Hello, go ahead and find all the Extensions */
|
||||||
|
func GetExtensionBlock(data []byte) ([]byte, error) {
|
||||||
|
/* data[0] - content type
|
||||||
|
* data[1], data[2] - major/minor version
|
||||||
|
* data[3], data[4] - total length
|
||||||
|
* data[...38+5] - start of SessionID (length bit)
|
||||||
|
* data[38+5] - length of SessionID
|
||||||
|
*/
|
||||||
|
var index = TLSHeaderLength + 38
|
||||||
|
|
||||||
|
if len(data) <= index+1 {
|
||||||
|
return []byte{}, fmt.Errorf("Not enough bits to be a Client Hello")
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Index is at SessionID Length bit */
|
||||||
|
if newIndex := index + 1 + int(data[index]); (newIndex + 2) < len(data) {
|
||||||
|
index = newIndex
|
||||||
|
} else {
|
||||||
|
return []byte{}, fmt.Errorf("Not enough bytes for the SessionID")
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Index is at Cipher List Length bits */
|
||||||
|
if newIndex := (index + 2 + int((data[index]<<8)+data[index+1])); (newIndex + 1) < len(data) {
|
||||||
|
index = newIndex
|
||||||
|
} else {
|
||||||
|
return []byte{}, fmt.Errorf("Not enough bytes for the Cipher List")
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Index is now at the compression length bit */
|
||||||
|
if newIndex := index + 1 + int(data[index]); newIndex < len(data) {
|
||||||
|
index = newIndex
|
||||||
|
} else {
|
||||||
|
return []byte{}, fmt.Errorf("Not enough bytes for the compression length")
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now we're at the Extension start */
|
||||||
|
if len(data[index:]) == 0 {
|
||||||
|
return nil, fmt.Errorf("No extensions")
|
||||||
|
}
|
||||||
|
return data[index:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: foldmethod=marker
|
Loading…
Reference in a new issue