mirror of
https://github.com/mmatczuk/go-http-tunnel.git
synced 2026-05-15 06:06:03 -06:00
Revert "Update and lock latest go-http-tunnel dependency"
This reverts commit 3c8c8fed77.
This commit is contained in:
parent
3c8c8fed77
commit
cc79e7406f
26 changed files with 72 additions and 1694 deletions
16
Gopkg.lock
generated
16
Gopkg.lock
generated
|
|
@ -34,16 +34,7 @@
|
|||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:e2d808cdb7eda4933f1b22f6747bd0b8dbbf79744c272500ce0c49ef58d31c96"
|
||||
name = "github.com/inconshreveable/go-vhost"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "06d84117953b22058c096b49a429ebd4f3d3d97b"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a5b782c76280477562569308f47c8146626370d4a4657b78fc16b92c17afd8a6"
|
||||
digest = "1:3a86b4526cc15ca4caf041c7f6943b59b3eb3711a9bc832de27b0801b826e019"
|
||||
name = "github.com/mmatczuk/go-http-tunnel"
|
||||
packages = [
|
||||
".",
|
||||
|
|
@ -53,8 +44,8 @@
|
|||
"tunnelmock",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "f48a09d2ab15c6f4fcbeec67864d39a584f22b30"
|
||||
source = "github.com/mmatczuk/go-http-tunnel"
|
||||
revision = "75a30abccc8db9d1149845e9985e80cca57758a2"
|
||||
version = "2.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
|
@ -108,7 +99,6 @@
|
|||
"github.com/cenkalti/backoff",
|
||||
"github.com/felixge/tcpkeepalive",
|
||||
"github.com/golang/mock/gomock",
|
||||
"github.com/inconshreveable/go-vhost",
|
||||
"github.com/mmatczuk/go-http-tunnel",
|
||||
"github.com/mmatczuk/go-http-tunnel/id",
|
||||
"github.com/mmatczuk/go-http-tunnel/log",
|
||||
|
|
|
|||
|
|
@ -33,11 +33,6 @@
|
|||
name = "github.com/golang/mock"
|
||||
version = "1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mmatczuk/go-http-tunnel"
|
||||
branch = "master"
|
||||
source = "github.com/mmatczuk/go-http-tunnel"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
|
|
|||
13
vendor/github.com/inconshreveable/go-vhost/LICENSE
generated
vendored
13
vendor/github.com/inconshreveable/go-vhost/LICENSE
generated
vendored
|
|
@ -1,13 +0,0 @@
|
|||
Copyright 2014 Alan Shreve
|
||||
|
||||
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.
|
||||
110
vendor/github.com/inconshreveable/go-vhost/README.md
generated
vendored
110
vendor/github.com/inconshreveable/go-vhost/README.md
generated
vendored
|
|
@ -1,110 +0,0 @@
|
|||
# go-vhost
|
||||
go-vhost is a simple library that lets you implement virtual hosting functionality for different protocols (HTTP and TLS so far). go-vhost has a high-level and a low-level interface. The high-level interface lets you wrap existing net.Listeners with "muxer" objects. You can then Listen() on a muxer for a particular virtual host name of interest which will return to you a net.Listener for just connections with the virtual hostname of interest.
|
||||
|
||||
The lower-level go-vhost interface are just functions which extract the name/routing information for the given protocol and return an object implementing net.Conn which works as if no bytes had been consumed.
|
||||
|
||||
### [API Documentation](https://godoc.org/github.com/inconshreveable/go-vhost)
|
||||
|
||||
### Usage
|
||||
```go
|
||||
l, _ := net.Listen("tcp", *listen)
|
||||
|
||||
// start multiplexing on it
|
||||
mux, _ := vhost.NewHTTPMuxer(l, muxTimeout)
|
||||
|
||||
// listen for connections to different domains
|
||||
for _, v := range virtualHosts {
|
||||
vhost := v
|
||||
|
||||
// vhost.Name is a virtual hostname like "foo.example.com"
|
||||
muxListener, _ := mux.Listen(vhost.Name())
|
||||
|
||||
go func(vh virtualHost, ml net.Listener) {
|
||||
for {
|
||||
conn, _ := ml.Accept()
|
||||
go vh.Handle(conn)
|
||||
}
|
||||
}(vhost, muxListener)
|
||||
}
|
||||
|
||||
for {
|
||||
conn, err := mux.NextError()
|
||||
|
||||
switch err.(type) {
|
||||
case vhost.BadRequest:
|
||||
log.Printf("got a bad request!")
|
||||
conn.Write([]byte("bad request"))
|
||||
case vhost.NotFound:
|
||||
log.Printf("got a connection for an unknown vhost")
|
||||
conn.Write([]byte("vhost not found"))
|
||||
case vhost.Closed:
|
||||
log.Printf("closed conn: %s", err)
|
||||
default:
|
||||
if conn != nil {
|
||||
conn.Write([]byte("server error"))
|
||||
}
|
||||
}
|
||||
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
```
|
||||
### Low-level API usage
|
||||
```go
|
||||
// accept a new connection
|
||||
conn, _ := listener.Accept()
|
||||
|
||||
// parse out the HTTP request and the Host header
|
||||
if vhostConn, err = vhost.HTTP(conn); err != nil {
|
||||
panic("Not a valid http connection!")
|
||||
}
|
||||
|
||||
fmt.Printf("Target Host: ", vhostConn.Host())
|
||||
// Target Host: example.com
|
||||
|
||||
// vhostConn contains the entire request as if no bytes had been consumed
|
||||
bytes, _ := ioutil.ReadAll(vhostConn)
|
||||
fmt.Printf("%s", bytes)
|
||||
// GET / HTTP/1.1
|
||||
// Host: example.com
|
||||
// User-Agent: ...
|
||||
// ...
|
||||
```
|
||||
|
||||
### Advanced introspection
|
||||
The entire HTTP request headers are available for inspection in case you want to mux on something besides the Host header:
|
||||
```go
|
||||
// parse out the HTTP request and the Host header
|
||||
if vhostConn, err = vhost.HTTP(conn); err != nil {
|
||||
panic("Not a valid http connection!")
|
||||
}
|
||||
|
||||
httpVersion := vhost.Request.MinorVersion
|
||||
customRouting := vhost.Request.Header["X-Custom-Routing-Header"]
|
||||
```
|
||||
|
||||
Likewise for TLS, you can look at detailed information about the ClientHello message:
|
||||
```go
|
||||
if vhostConn, err = vhost.TLS(conn); err != nil {
|
||||
panic("Not a valid TLS connection!")
|
||||
}
|
||||
|
||||
cipherSuites := vhost.ClientHelloMsg.CipherSuites
|
||||
sessionId := vhost.ClientHelloMsg.SessionId
|
||||
```
|
||||
|
||||
##### Memory reduction with Free
|
||||
After you're done muxing, you probably don't need to inspect the header data anymore, so you can make it available for garbage collection:
|
||||
|
||||
```go
|
||||
// look up the upstream host
|
||||
upstreamHost := hostMapping[vhostConn.Host()]
|
||||
|
||||
// free up the muxing data
|
||||
vhostConn.Free()
|
||||
|
||||
// vhostConn.Host() == ""
|
||||
// vhostConn.Request == nil (HTTP)
|
||||
// vhostConn.ClientHelloMsg == nil (TLS)
|
||||
```
|
||||
42
vendor/github.com/inconshreveable/go-vhost/http.go
generated
vendored
42
vendor/github.com/inconshreveable/go-vhost/http.go
generated
vendored
|
|
@ -1,42 +0,0 @@
|
|||
package vhost
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type HTTPConn struct {
|
||||
*sharedConn
|
||||
Request *http.Request
|
||||
}
|
||||
|
||||
// HTTP parses the head of the first HTTP request on conn and returns
|
||||
// a new, unread connection with metadata for virtual host muxing
|
||||
func HTTP(conn net.Conn) (httpConn *HTTPConn, err error) {
|
||||
c, rd := newShared(conn)
|
||||
|
||||
httpConn = &HTTPConn{sharedConn: c}
|
||||
if httpConn.Request, err = http.ReadRequest(bufio.NewReader(rd)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// You probably don't need access to the request body and this makes the API
|
||||
// simpler by allowing you to call Free() optionally
|
||||
httpConn.Request.Body.Close()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Free sets Request to nil so that it can be garbage collected
|
||||
func (c *HTTPConn) Free() {
|
||||
c.Request = nil
|
||||
}
|
||||
|
||||
func (c *HTTPConn) Host() string {
|
||||
if c.Request == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return c.Request.Host
|
||||
}
|
||||
45
vendor/github.com/inconshreveable/go-vhost/http_test.go
generated
vendored
45
vendor/github.com/inconshreveable/go-vhost/http_test.go
generated
vendored
|
|
@ -1,45 +0,0 @@
|
|||
package vhost
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHTTPHost(t *testing.T) {
|
||||
var testHostname string = "foo.example.com"
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:12345")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
go func() {
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:12345")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
req, err := http.NewRequest("GET", "http://"+testHostname+"/bar", nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = req.Write(conn); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c, err := HTTP(conn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if c.Host() != testHostname {
|
||||
t.Errorf("Connection Host() is %s, expected %s", c.Host(), testHostname)
|
||||
}
|
||||
}
|
||||
11
vendor/github.com/inconshreveable/go-vhost/interface.go
generated
vendored
11
vendor/github.com/inconshreveable/go-vhost/interface.go
generated
vendored
|
|
@ -1,11 +0,0 @@
|
|||
package vhost
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
type Conn interface {
|
||||
net.Conn
|
||||
Host() string
|
||||
Free()
|
||||
}
|
||||
337
vendor/github.com/inconshreveable/go-vhost/mux.go
generated
vendored
337
vendor/github.com/inconshreveable/go-vhost/mux.go
generated
vendored
|
|
@ -1,337 +0,0 @@
|
|||
package vhost
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
normalize = strings.ToLower
|
||||
isClosed = func(err error) bool {
|
||||
netErr, ok := err.(net.Error)
|
||||
if ok {
|
||||
return netErr.Temporary()
|
||||
}
|
||||
return false
|
||||
}
|
||||
)
|
||||
|
||||
// NotFound is returned when a vhost is not found
|
||||
type NotFound struct {
|
||||
error
|
||||
}
|
||||
|
||||
// BadRequest is returned when extraction of the vhost name fails
|
||||
type BadRequest struct {
|
||||
error
|
||||
}
|
||||
|
||||
// Closed is returned when the underlying connection is closed
|
||||
type Closed struct {
|
||||
error
|
||||
}
|
||||
|
||||
type (
|
||||
// this is the function you apply to a net.Conn to get
|
||||
// a new virtual-host multiplexed connection
|
||||
muxFn func(net.Conn) (Conn, error)
|
||||
|
||||
// an error encountered when multiplexing a connection
|
||||
muxErr struct {
|
||||
err error
|
||||
conn net.Conn
|
||||
}
|
||||
)
|
||||
|
||||
type VhostMuxer struct {
|
||||
listener net.Listener // listener on which we mux connections
|
||||
muxTimeout time.Duration // a connection fails if it doesn't send enough data to mux after this timeout
|
||||
vhostFn muxFn // new connections are multiplexed by applying this function
|
||||
muxErrors chan muxErr // all muxing errors are sent over this channel
|
||||
registry map[string]*Listener // registry of name -> listener
|
||||
sync.RWMutex // protects the registry
|
||||
}
|
||||
|
||||
func NewVhostMuxer(listener net.Listener, vhostFn muxFn, muxTimeout time.Duration) (*VhostMuxer, error) {
|
||||
mux := &VhostMuxer{
|
||||
listener: listener,
|
||||
muxTimeout: muxTimeout,
|
||||
vhostFn: vhostFn,
|
||||
muxErrors: make(chan muxErr),
|
||||
registry: make(map[string]*Listener),
|
||||
}
|
||||
|
||||
go mux.run()
|
||||
return mux, nil
|
||||
}
|
||||
|
||||
// Listen begins multiplexing the underlying connection to send new
|
||||
// connections for the given name over the returned listener.
|
||||
func (m *VhostMuxer) Listen(name string) (net.Listener, error) {
|
||||
name = normalize(name)
|
||||
|
||||
vhost := &Listener{
|
||||
name: name,
|
||||
mux: m,
|
||||
accept: make(chan Conn),
|
||||
}
|
||||
|
||||
if err := m.set(name, vhost); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return vhost, nil
|
||||
}
|
||||
|
||||
// NextError returns the next error encountered while mux'ing a connection.
|
||||
// The net.Conn may be nil if the wrapped listener returned an error from Accept()
|
||||
func (m *VhostMuxer) NextError() (net.Conn, error) {
|
||||
muxErr := <-m.muxErrors
|
||||
return muxErr.conn, muxErr.err
|
||||
}
|
||||
|
||||
// Close closes the underlying listener
|
||||
func (m *VhostMuxer) Close() {
|
||||
m.listener.Close()
|
||||
}
|
||||
|
||||
// run is the VhostMuxer's main loop for accepting new connections from the wrapped listener
|
||||
func (m *VhostMuxer) run() {
|
||||
for {
|
||||
conn, err := m.listener.Accept()
|
||||
if err != nil {
|
||||
if isClosed(err) {
|
||||
m.sendError(nil, Closed{err})
|
||||
return
|
||||
} else {
|
||||
m.sendError(nil, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
go m.handle(conn)
|
||||
}
|
||||
}
|
||||
|
||||
// handle muxes a connection accepted from the listener
|
||||
func (m *VhostMuxer) handle(conn net.Conn) {
|
||||
defer func() {
|
||||
// recover from failures
|
||||
if r := recover(); r != nil {
|
||||
m.sendError(conn, fmt.Errorf("NameMux.handle failed with error %v", r))
|
||||
}
|
||||
}()
|
||||
|
||||
// Make sure we detect dead connections while we decide how to multiplex
|
||||
if err := conn.SetDeadline(time.Now().Add(m.muxTimeout)); err != nil {
|
||||
m.sendError(conn, fmt.Errorf("Failed to set deadline: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
// extract the name
|
||||
vconn, err := m.vhostFn(conn)
|
||||
if err != nil {
|
||||
m.sendError(conn, BadRequest{fmt.Errorf("Failed to extract vhost name: %v", err)})
|
||||
return
|
||||
}
|
||||
|
||||
// normalize the name
|
||||
host := normalize(vconn.Host())
|
||||
|
||||
// look up the correct listener
|
||||
l, ok := m.get(host)
|
||||
if !ok {
|
||||
m.sendError(vconn, NotFound{fmt.Errorf("Host not found: %v", host)})
|
||||
return
|
||||
}
|
||||
|
||||
if err = vconn.SetDeadline(time.Time{}); err != nil {
|
||||
m.sendError(vconn, fmt.Errorf("Failed unset connection deadline: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
l.accept <- vconn
|
||||
}
|
||||
|
||||
func (m *VhostMuxer) sendError(conn net.Conn, err error) {
|
||||
m.muxErrors <- muxErr{conn: conn, err: err}
|
||||
}
|
||||
|
||||
func (m *VhostMuxer) get(name string) (l *Listener, ok bool) {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
l, ok = m.registry[name]
|
||||
if !ok {
|
||||
// look for a matching wildcard
|
||||
parts := strings.Split(name, ".")
|
||||
for i := 0; i < len(parts)-1; i++ {
|
||||
parts[i] = "*"
|
||||
name = strings.Join(parts[i:], ".")
|
||||
l, ok = m.registry[name]
|
||||
if ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m *VhostMuxer) set(name string, l *Listener) error {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
if _, exists := m.registry[name]; exists {
|
||||
return fmt.Errorf("name %s is already bound", name)
|
||||
}
|
||||
m.registry[name] = l
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *VhostMuxer) del(name string) {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
delete(m.registry, name)
|
||||
}
|
||||
|
||||
const (
|
||||
serverError = `HTTP/1.0 500 Internal Server Error
|
||||
Content-Length: 22
|
||||
|
||||
Internal Server Error
|
||||
`
|
||||
|
||||
notFound = `HTTP/1.0 404 Not Found
|
||||
Content-Length: 14
|
||||
|
||||
404 not found
|
||||
`
|
||||
|
||||
badRequest = `HTTP/1.0 400 Bad Request
|
||||
Content-Length: 12
|
||||
|
||||
Bad Request
|
||||
`
|
||||
)
|
||||
|
||||
type HTTPMuxer struct {
|
||||
*VhostMuxer
|
||||
}
|
||||
|
||||
// HandleErrors handles muxing errors by calling .NextError(). You must
|
||||
// invoke this function if you do not want to handle the errors yourself.
|
||||
func (m *HTTPMuxer) HandleErrors() {
|
||||
for {
|
||||
m.HandleError(m.NextError())
|
||||
}
|
||||
}
|
||||
|
||||
func (m *HTTPMuxer) HandleError(conn net.Conn, err error) {
|
||||
switch err.(type) {
|
||||
case Closed:
|
||||
return
|
||||
case NotFound:
|
||||
conn.Write([]byte(notFound))
|
||||
case BadRequest:
|
||||
conn.Write([]byte(badRequest))
|
||||
default:
|
||||
if conn != nil {
|
||||
conn.Write([]byte(serverError))
|
||||
}
|
||||
}
|
||||
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// NewHTTPMuxer begins muxing HTTP connections on the given listener by inspecting
|
||||
// the HTTP Host header in new connections.
|
||||
func NewHTTPMuxer(listener net.Listener, muxTimeout time.Duration) (*HTTPMuxer, error) {
|
||||
fn := func(c net.Conn) (Conn, error) { return HTTP(c) }
|
||||
mux, err := NewVhostMuxer(listener, fn, muxTimeout)
|
||||
return &HTTPMuxer{mux}, err
|
||||
}
|
||||
|
||||
type TLSMuxer struct {
|
||||
*VhostMuxer
|
||||
}
|
||||
|
||||
// HandleErrors is the default error handler for TLS muxers. At the moment, it simply
|
||||
// closes connections which are invalid or destined for virtual host names that it is
|
||||
// not listening for.
|
||||
// You must invoke this function if you do not want to handle the errors yourself.
|
||||
func (m *TLSMuxer) HandleErrors() {
|
||||
for {
|
||||
conn, err := m.NextError()
|
||||
|
||||
if conn == nil {
|
||||
if _, ok := err.(Closed); ok {
|
||||
return
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// XXX: respond with valid TLS close messages
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *TLSMuxer) Listen(name string) (net.Listener, error) {
|
||||
// TLS SNI never includes the port
|
||||
host, _, err := net.SplitHostPort(name)
|
||||
if err != nil {
|
||||
host = name
|
||||
}
|
||||
return m.VhostMuxer.Listen(host)
|
||||
}
|
||||
|
||||
// NewTLSMuxer begins muxing TLS connections by inspecting the SNI extension.
|
||||
func NewTLSMuxer(listener net.Listener, muxTimeout time.Duration) (*TLSMuxer, error) {
|
||||
fn := func(c net.Conn) (Conn, error) { return TLS(c) }
|
||||
mux, err := NewVhostMuxer(listener, fn, muxTimeout)
|
||||
return &TLSMuxer{mux}, err
|
||||
}
|
||||
|
||||
// Listener is returned by a call to Listen() on a muxer. A Listener
|
||||
// only receives connections that were made to the name passed into the muxer's
|
||||
// Listen call.
|
||||
//
|
||||
// Listener implements the net.Listener interface, so you can Accept() new
|
||||
// connections and Close() it when finished. When you Close() a Listener,
|
||||
// the parent muxer will stop listening for connections to the Listener's name.
|
||||
type Listener struct {
|
||||
name string
|
||||
mux *VhostMuxer
|
||||
accept chan Conn
|
||||
}
|
||||
|
||||
// Accept returns the next mux'd connection for this listener and blocks
|
||||
// until one is available.
|
||||
func (l *Listener) Accept() (net.Conn, error) {
|
||||
conn, ok := <-l.accept
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Listener closed")
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Close stops the parent muxer from listening for connections to the mux'd
|
||||
// virtual host name.
|
||||
func (l *Listener) Close() error {
|
||||
l.mux.del(l.name)
|
||||
close(l.accept)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Addr returns the address of the bound listener used by the parent muxer.
|
||||
func (l *Listener) Addr() net.Addr {
|
||||
// XXX: include name in address?
|
||||
return l.mux.listener.Addr()
|
||||
}
|
||||
|
||||
// Name returns the name of the virtual host this listener receives connections on.
|
||||
func (l *Listener) Name() string {
|
||||
return l.name
|
||||
}
|
||||
195
vendor/github.com/inconshreveable/go-vhost/mux_test.go
generated
vendored
195
vendor/github.com/inconshreveable/go-vhost/mux_test.go
generated
vendored
|
|
@ -1,195 +0,0 @@
|
|||
package vhost
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestErrors ensures that error types for this package are implemented properly
|
||||
func TestErrors(t *testing.T) {
|
||||
// test case for https://github.com/inconshreveable/go-vhost/pull/2
|
||||
// create local err vars of error interface type
|
||||
var notFoundErr error
|
||||
var badRequestErr error
|
||||
var closedErr error
|
||||
|
||||
// stuff local error types in to interface values
|
||||
notFoundErr = NotFound{fmt.Errorf("test NotFound")}
|
||||
badRequestErr = BadRequest{fmt.Errorf("test BadRequest")}
|
||||
closedErr = Closed{fmt.Errorf("test Closed")}
|
||||
|
||||
// assert the types
|
||||
switch errType := notFoundErr.(type) {
|
||||
case NotFound:
|
||||
default:
|
||||
t.Fatalf("expected NotFound, got: %s", errType)
|
||||
}
|
||||
switch errType := badRequestErr.(type) {
|
||||
case BadRequest:
|
||||
default:
|
||||
t.Fatalf("expected BadRequest, got: %s", errType)
|
||||
}
|
||||
switch errType := closedErr.(type) {
|
||||
case Closed:
|
||||
default:
|
||||
t.Fatalf("expected Closed, got: %s", errType)
|
||||
}
|
||||
}
|
||||
|
||||
func localListener(t *testing.T) (net.Listener, string) {
|
||||
l, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
return l, strconv.Itoa(l.Addr().(*net.TCPAddr).Port)
|
||||
}
|
||||
|
||||
func TestHTTPMux(t *testing.T) {
|
||||
l, port := localListener(t)
|
||||
mux, err := NewHTTPMuxer(l, time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to start muxer: %v", err)
|
||||
}
|
||||
go mux.HandleErrors()
|
||||
|
||||
muxed, err := mux.Listen("example.com")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to listen on muxer: %v", muxed)
|
||||
}
|
||||
|
||||
go http.Serve(muxed, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
io.Copy(w, r.Body)
|
||||
}))
|
||||
|
||||
msg := "test"
|
||||
url := "http://localhost:" + port
|
||||
resp, err := http.Post(url, "text/plain", strings.NewReader(msg))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to post: %v", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != 404 {
|
||||
t.Fatalf("sent incorrect host header, expected 404 but got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("POST", url, strings.NewReader(msg))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to construct HTTP request: %v", err)
|
||||
}
|
||||
req.Host = "example.com"
|
||||
req.Header.Set("Content-Type", "text/plain")
|
||||
|
||||
resp, err = new(http.Client).Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to make HTTP request", err)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read: %v", err)
|
||||
}
|
||||
|
||||
got := string(body)
|
||||
if got != msg {
|
||||
t.Fatalf("unexpected resposne. got: %v, expected: %v", got, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func testMux(t *testing.T, listen, dial string) {
|
||||
muxFn := func(c net.Conn) (Conn, error) {
|
||||
return fakeConn{c, dial}, nil
|
||||
}
|
||||
|
||||
fakel := make(fakeListener, 1)
|
||||
mux, err := NewVhostMuxer(fakel, muxFn, time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to start vhost muxer: %v", err)
|
||||
}
|
||||
|
||||
l, err := mux.Listen(listen)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to listen for %s", err)
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to accept connection: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
got := conn.(Conn).Host()
|
||||
expected := dial
|
||||
if got != expected {
|
||||
t.Fatalf("got connection with unexpected host. got: %s, expected: %s", got, expected)
|
||||
return
|
||||
}
|
||||
|
||||
close(done)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
_, err := mux.NextError()
|
||||
if err != nil {
|
||||
t.Fatalf("muxing error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
fakel <- struct{}{}
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatalf("test timed out: dial: %s listen: %s", dial, listen)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMuxingPatterns(t *testing.T) {
|
||||
var tests = []struct {
|
||||
listen string
|
||||
dial string
|
||||
}{
|
||||
{"example.com", "example.com"},
|
||||
{"sub.example.com", "sub.example.com"},
|
||||
{"*.example.com", "sub.example.com"},
|
||||
{"*.example.com", "nested.sub.example.com"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
testMux(t, test.listen, test.dial)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeConn struct {
|
||||
net.Conn
|
||||
host string
|
||||
}
|
||||
|
||||
func (c fakeConn) SetDeadline(d time.Time) error { return nil }
|
||||
func (c fakeConn) Host() string { return c.host }
|
||||
func (c fakeConn) Free() {}
|
||||
|
||||
type fakeNetConn struct {
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (fakeNetConn) SetDeadline(time.Time) error { return nil }
|
||||
|
||||
type fakeListener chan struct{}
|
||||
|
||||
func (l fakeListener) Accept() (net.Conn, error) {
|
||||
for _ = range l {
|
||||
return fakeNetConn{nil}, nil
|
||||
}
|
||||
select {}
|
||||
}
|
||||
func (fakeListener) Addr() net.Addr { return nil }
|
||||
func (fakeListener) Close() error { return nil }
|
||||
52
vendor/github.com/inconshreveable/go-vhost/shared.go
generated
vendored
52
vendor/github.com/inconshreveable/go-vhost/shared.go
generated
vendored
|
|
@ -1,52 +0,0 @@
|
|||
package vhost
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
initVhostBufSize = 1024 // allocate 1 KB up front to try to avoid resizing
|
||||
)
|
||||
|
||||
type sharedConn struct {
|
||||
sync.Mutex
|
||||
net.Conn // the raw connection
|
||||
vhostBuf *bytes.Buffer // all of the initial data that has to be read in order to vhost a connection is saved here
|
||||
}
|
||||
|
||||
func newShared(conn net.Conn) (*sharedConn, io.Reader) {
|
||||
c := &sharedConn{
|
||||
Conn: conn,
|
||||
vhostBuf: bytes.NewBuffer(make([]byte, 0, initVhostBufSize)),
|
||||
}
|
||||
|
||||
return c, io.TeeReader(conn, c.vhostBuf)
|
||||
}
|
||||
|
||||
func (c *sharedConn) Read(p []byte) (n int, err error) {
|
||||
c.Lock()
|
||||
if c.vhostBuf == nil {
|
||||
c.Unlock()
|
||||
return c.Conn.Read(p)
|
||||
}
|
||||
n, err = c.vhostBuf.Read(p)
|
||||
|
||||
// end of the request buffer
|
||||
if err == io.EOF {
|
||||
// let the request buffer get garbage collected
|
||||
// and make sure we don't read from it again
|
||||
c.vhostBuf = nil
|
||||
|
||||
// continue reading from the connection
|
||||
var n2 int
|
||||
n2, err = c.Conn.Read(p[n:])
|
||||
|
||||
// update total read
|
||||
n += n2
|
||||
}
|
||||
c.Unlock()
|
||||
return
|
||||
}
|
||||
64
vendor/github.com/inconshreveable/go-vhost/shared_test.go
generated
vendored
64
vendor/github.com/inconshreveable/go-vhost/shared_test.go
generated
vendored
|
|
@ -1,64 +0,0 @@
|
|||
package vhost
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHeaderPreserved(t *testing.T) {
|
||||
var msg string = "TestHeaderPreserved message! Hello world!"
|
||||
var headerLen int = 15
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:12345")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
go func() {
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:12345")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err := conn.Write([]byte(msg)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = conn.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// create a shared connection object
|
||||
c, rd := newShared(conn)
|
||||
|
||||
// read out a "header"
|
||||
p := make([]byte, headerLen)
|
||||
_, err = io.ReadFull(rd, p)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// make sure we got the header
|
||||
expectedHeader := []byte(msg[:headerLen])
|
||||
if !reflect.DeepEqual(p, expectedHeader) {
|
||||
t.Errorf("Read header bytes %s, expected %s", p, expectedHeader)
|
||||
return
|
||||
}
|
||||
|
||||
// read out the entire connection. make sure it includes the header
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
io.Copy(buf, c)
|
||||
|
||||
expected := []byte(msg)
|
||||
if !reflect.DeepEqual(buf.Bytes(), expected) {
|
||||
t.Errorf("Read full connection bytes %s, expected %s", buf.Bytes(), expected)
|
||||
}
|
||||
}
|
||||
434
vendor/github.com/inconshreveable/go-vhost/tls.go
generated
vendored
434
vendor/github.com/inconshreveable/go-vhost/tls.go
generated
vendored
|
|
@ -1,434 +0,0 @@
|
|||
// Portions of the TLS code are:
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// TLS virtual hosting
|
||||
|
||||
package vhost
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
maxPlaintext = 16384 // maximum plaintext payload length
|
||||
maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
|
||||
recordHeaderLen = 5 // record header length
|
||||
maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
|
||||
)
|
||||
|
||||
type alert uint8
|
||||
|
||||
const (
|
||||
alertUnexpectedMessage alert = 10
|
||||
alertRecordOverflow alert = 22
|
||||
alertInternalError alert = 80
|
||||
)
|
||||
|
||||
var alertText = map[alert]string{
|
||||
alertUnexpectedMessage: "unexpected message",
|
||||
alertRecordOverflow: "record overflow",
|
||||
alertInternalError: "internal error",
|
||||
}
|
||||
|
||||
func (e alert) String() string {
|
||||
s, ok := alertText[e]
|
||||
if ok {
|
||||
return s
|
||||
}
|
||||
return "alert(" + strconv.Itoa(int(e)) + ")"
|
||||
}
|
||||
|
||||
func (e alert) Error() string {
|
||||
return e.String()
|
||||
}
|
||||
|
||||
// TLS record types.
|
||||
type recordType uint8
|
||||
|
||||
const (
|
||||
recordTypeHandshake recordType = 22
|
||||
)
|
||||
|
||||
// TLS handshake message types.
|
||||
const (
|
||||
typeClientHello uint8 = 1
|
||||
)
|
||||
|
||||
// TLS extension numbers
|
||||
var (
|
||||
extensionServerName uint16 = 0
|
||||
extensionStatusRequest uint16 = 5
|
||||
extensionSupportedCurves uint16 = 10
|
||||
extensionSupportedPoints uint16 = 11
|
||||
extensionSessionTicket uint16 = 35
|
||||
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
|
||||
)
|
||||
|
||||
// TLS CertificateStatusType (RFC 3546)
|
||||
const (
|
||||
statusTypeOCSP uint8 = 1
|
||||
)
|
||||
|
||||
// A Conn represents a secured connection.
|
||||
// It implements the net.Conn interface.
|
||||
type TLSConn struct {
|
||||
*sharedConn
|
||||
ClientHelloMsg *ClientHelloMsg
|
||||
}
|
||||
|
||||
// TLS parses the ClientHello message on conn and returns
|
||||
// a new, unread connection with metadata for virtual host muxing
|
||||
func TLS(conn net.Conn) (tlsConn *TLSConn, err error) {
|
||||
c, rd := newShared(conn)
|
||||
|
||||
tlsConn = &TLSConn{sharedConn: c}
|
||||
if tlsConn.ClientHelloMsg, err = readClientHello(rd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *TLSConn) Host() string {
|
||||
if c.ClientHelloMsg == nil {
|
||||
return ""
|
||||
}
|
||||
return c.ClientHelloMsg.ServerName
|
||||
}
|
||||
|
||||
func (c *TLSConn) Free() {
|
||||
c.ClientHelloMsg = nil
|
||||
}
|
||||
|
||||
// A block is a simple data buffer.
|
||||
type block struct {
|
||||
data []byte
|
||||
off int // index for Read
|
||||
}
|
||||
|
||||
// resize resizes block to be n bytes, growing if necessary.
|
||||
func (b *block) resize(n int) {
|
||||
if n > cap(b.data) {
|
||||
b.reserve(n)
|
||||
}
|
||||
b.data = b.data[0:n]
|
||||
}
|
||||
|
||||
// reserve makes sure that block contains a capacity of at least n bytes.
|
||||
func (b *block) reserve(n int) {
|
||||
if cap(b.data) >= n {
|
||||
return
|
||||
}
|
||||
m := cap(b.data)
|
||||
if m == 0 {
|
||||
m = 1024
|
||||
}
|
||||
for m < n {
|
||||
m *= 2
|
||||
}
|
||||
data := make([]byte, len(b.data), m)
|
||||
copy(data, b.data)
|
||||
b.data = data
|
||||
}
|
||||
|
||||
// readFromUntil reads from r into b until b contains at least n bytes
|
||||
// or else returns an error.
|
||||
func (b *block) readFromUntil(r io.Reader, n int) error {
|
||||
// quick case
|
||||
if len(b.data) >= n {
|
||||
return nil
|
||||
}
|
||||
|
||||
// read until have enough.
|
||||
b.reserve(n)
|
||||
for {
|
||||
m, err := r.Read(b.data[len(b.data):cap(b.data)])
|
||||
b.data = b.data[0 : len(b.data)+m]
|
||||
if len(b.data) >= n {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *block) Read(p []byte) (n int, err error) {
|
||||
n = copy(p, b.data[b.off:])
|
||||
b.off += n
|
||||
return
|
||||
}
|
||||
|
||||
// newBlock allocates a new block
|
||||
func newBlock() *block {
|
||||
return new(block)
|
||||
}
|
||||
|
||||
// splitBlock splits a block after the first n bytes,
|
||||
// returning a block with those n bytes and a
|
||||
// block with the remainder. the latter may be nil.
|
||||
func splitBlock(b *block, n int) (*block, *block) {
|
||||
if len(b.data) <= n {
|
||||
return b, nil
|
||||
}
|
||||
bb := newBlock()
|
||||
bb.resize(len(b.data) - n)
|
||||
copy(bb.data, b.data[n:])
|
||||
b.data = b.data[0:n]
|
||||
return b, bb
|
||||
}
|
||||
|
||||
// readHandshake reads the next handshake message from
|
||||
// the record layer.
|
||||
func readClientHello(rd io.Reader) (*ClientHelloMsg, error) {
|
||||
var nextBlock *block // raw input, right off the wire
|
||||
var hand bytes.Buffer // handshake data waiting to be read
|
||||
|
||||
// readRecord reads the next TLS record from the connection
|
||||
// and updates the record layer state.
|
||||
readRecord := func() error {
|
||||
// Caller must be in sync with connection:
|
||||
// handshake data if handshake not yet completed,
|
||||
// else application data. (We don't support renegotiation.)
|
||||
if nextBlock == nil {
|
||||
nextBlock = newBlock()
|
||||
}
|
||||
b := nextBlock
|
||||
|
||||
// Read header, payload.
|
||||
if err := b.readFromUntil(rd, recordHeaderLen); err != nil {
|
||||
return err
|
||||
}
|
||||
typ := recordType(b.data[0])
|
||||
|
||||
// No valid TLS record has a type of 0x80, however SSLv2 handshakes
|
||||
// start with a uint16 length where the MSB is set and the first record
|
||||
// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
|
||||
// an SSLv2 client.
|
||||
if typ == 0x80 {
|
||||
return errors.New("tls: unsupported SSLv2 handshake received")
|
||||
}
|
||||
|
||||
vers := uint16(b.data[1])<<8 | uint16(b.data[2])
|
||||
n := int(b.data[3])<<8 | int(b.data[4])
|
||||
if n > maxCiphertext {
|
||||
return alertRecordOverflow
|
||||
}
|
||||
|
||||
// First message, be extra suspicious:
|
||||
// this might not be a TLS client.
|
||||
// Bail out before reading a full 'body', if possible.
|
||||
// The current max version is 3.1.
|
||||
// If the version is >= 16.0, it's probably not real.
|
||||
// Similarly, a clientHello message encodes in
|
||||
// well under a kilobyte. If the length is >= 12 kB,
|
||||
// it's probably not real.
|
||||
if (typ != recordTypeHandshake) || vers >= 0x1000 || n >= 0x3000 {
|
||||
return alertUnexpectedMessage
|
||||
}
|
||||
|
||||
if err := b.readFromUntil(rd, recordHeaderLen+n); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Process message.
|
||||
b, nextBlock = splitBlock(b, recordHeaderLen+n)
|
||||
b.off = recordHeaderLen
|
||||
data := b.data[b.off:]
|
||||
if len(data) > maxPlaintext {
|
||||
return alertRecordOverflow
|
||||
}
|
||||
|
||||
hand.Write(data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := readRecord(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := hand.Bytes()
|
||||
n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
|
||||
if n > maxHandshake {
|
||||
return nil, alertInternalError
|
||||
}
|
||||
for hand.Len() < 4+n {
|
||||
if err := readRecord(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
data = hand.Next(4 + n)
|
||||
if data[0] != typeClientHello {
|
||||
return nil, alertUnexpectedMessage
|
||||
}
|
||||
|
||||
msg := new(ClientHelloMsg)
|
||||
if !msg.unmarshal(data) {
|
||||
return nil, alertUnexpectedMessage
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
type ClientHelloMsg struct {
|
||||
Raw []byte
|
||||
Vers uint16
|
||||
Random []byte
|
||||
SessionId []byte
|
||||
CipherSuites []uint16
|
||||
CompressionMethods []uint8
|
||||
NextProtoNeg bool
|
||||
ServerName string
|
||||
OcspStapling bool
|
||||
SupportedCurves []uint16
|
||||
SupportedPoints []uint8
|
||||
TicketSupported bool
|
||||
SessionTicket []uint8
|
||||
}
|
||||
|
||||
func (m *ClientHelloMsg) unmarshal(data []byte) bool {
|
||||
if len(data) < 42 {
|
||||
return false
|
||||
}
|
||||
m.Raw = data
|
||||
m.Vers = uint16(data[4])<<8 | uint16(data[5])
|
||||
m.Random = data[6:38]
|
||||
sessionIdLen := int(data[38])
|
||||
if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
|
||||
return false
|
||||
}
|
||||
m.SessionId = data[39 : 39+sessionIdLen]
|
||||
data = data[39+sessionIdLen:]
|
||||
if len(data) < 2 {
|
||||
return false
|
||||
}
|
||||
// cipherSuiteLen is the number of bytes of cipher suite numbers. Since
|
||||
// they are uint16s, the number must be even.
|
||||
cipherSuiteLen := int(data[0])<<8 | int(data[1])
|
||||
if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
|
||||
return false
|
||||
}
|
||||
numCipherSuites := cipherSuiteLen / 2
|
||||
m.CipherSuites = make([]uint16, numCipherSuites)
|
||||
for i := 0; i < numCipherSuites; i++ {
|
||||
m.CipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i])
|
||||
}
|
||||
data = data[2+cipherSuiteLen:]
|
||||
if len(data) < 1 {
|
||||
return false
|
||||
}
|
||||
compressionMethodsLen := int(data[0])
|
||||
if len(data) < 1+compressionMethodsLen {
|
||||
return false
|
||||
}
|
||||
m.CompressionMethods = data[1 : 1+compressionMethodsLen]
|
||||
|
||||
data = data[1+compressionMethodsLen:]
|
||||
|
||||
m.NextProtoNeg = false
|
||||
m.ServerName = ""
|
||||
m.OcspStapling = false
|
||||
m.TicketSupported = false
|
||||
m.SessionTicket = nil
|
||||
|
||||
if len(data) == 0 {
|
||||
// ClientHello is optionally followed by extension data
|
||||
return true
|
||||
}
|
||||
if len(data) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
extensionsLength := int(data[0])<<8 | int(data[1])
|
||||
data = data[2:]
|
||||
if extensionsLength != len(data) {
|
||||
return false
|
||||
}
|
||||
|
||||
for len(data) != 0 {
|
||||
if len(data) < 4 {
|
||||
return false
|
||||
}
|
||||
extension := uint16(data[0])<<8 | uint16(data[1])
|
||||
length := int(data[2])<<8 | int(data[3])
|
||||
data = data[4:]
|
||||
if len(data) < length {
|
||||
return false
|
||||
}
|
||||
|
||||
switch extension {
|
||||
case extensionServerName:
|
||||
if length < 2 {
|
||||
return false
|
||||
}
|
||||
numNames := int(data[0])<<8 | int(data[1])
|
||||
d := data[2:]
|
||||
for i := 0; i < numNames; i++ {
|
||||
if len(d) < 3 {
|
||||
return false
|
||||
}
|
||||
nameType := d[0]
|
||||
nameLen := int(d[1])<<8 | int(d[2])
|
||||
d = d[3:]
|
||||
if len(d) < nameLen {
|
||||
return false
|
||||
}
|
||||
if nameType == 0 {
|
||||
m.ServerName = string(d[0:nameLen])
|
||||
break
|
||||
}
|
||||
d = d[nameLen:]
|
||||
}
|
||||
case extensionNextProtoNeg:
|
||||
if length > 0 {
|
||||
return false
|
||||
}
|
||||
m.NextProtoNeg = true
|
||||
case extensionStatusRequest:
|
||||
m.OcspStapling = length > 0 && data[0] == statusTypeOCSP
|
||||
case extensionSupportedCurves:
|
||||
// http://tools.ietf.org/html/rfc4492#section-5.5.1
|
||||
if length < 2 {
|
||||
return false
|
||||
}
|
||||
l := int(data[0])<<8 | int(data[1])
|
||||
if l%2 == 1 || length != l+2 {
|
||||
return false
|
||||
}
|
||||
numCurves := l / 2
|
||||
m.SupportedCurves = make([]uint16, numCurves)
|
||||
d := data[2:]
|
||||
for i := 0; i < numCurves; i++ {
|
||||
m.SupportedCurves[i] = uint16(d[0])<<8 | uint16(d[1])
|
||||
d = d[2:]
|
||||
}
|
||||
case extensionSupportedPoints:
|
||||
// http://tools.ietf.org/html/rfc4492#section-5.5.2
|
||||
if length < 1 {
|
||||
return false
|
||||
}
|
||||
l := int(data[0])
|
||||
if length != l+1 {
|
||||
return false
|
||||
}
|
||||
m.SupportedPoints = make([]uint8, l)
|
||||
copy(m.SupportedPoints, data[1:])
|
||||
case extensionSessionTicket:
|
||||
// http://tools.ietf.org/html/rfc5077#section-3.2
|
||||
m.TicketSupported = true
|
||||
m.SessionTicket = data[:length]
|
||||
}
|
||||
data = data[length:]
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
39
vendor/github.com/inconshreveable/go-vhost/tls_test.go
generated
vendored
39
vendor/github.com/inconshreveable/go-vhost/tls_test.go
generated
vendored
|
|
@ -1,39 +0,0 @@
|
|||
package vhost
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSNI(t *testing.T) {
|
||||
var testHostname string = "foo.example.com"
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:12345")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
go func() {
|
||||
conf := &tls.Config{ServerName: testHostname}
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:12345", conf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
conn.Close()
|
||||
}()
|
||||
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c, err := TLS(conn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if c.Host() != testHostname {
|
||||
t.Errorf("Connection Host() is %s, expected %s", c.Host(), testHostname)
|
||||
}
|
||||
}
|
||||
3
vendor/github.com/mmatczuk/go-http-tunnel/.gitignore
generated
vendored
3
vendor/github.com/mmatczuk/go-http-tunnel/.gitignore
generated
vendored
|
|
@ -22,6 +22,3 @@ Session.vim
|
|||
*~
|
||||
# auto-generated tag files
|
||||
tags
|
||||
|
||||
### Mac
|
||||
.DS_Store
|
||||
|
|
|
|||
78
vendor/github.com/mmatczuk/go-http-tunnel/Gopkg.lock
generated
vendored
78
vendor/github.com/mmatczuk/go-http-tunnel/Gopkg.lock
generated
vendored
|
|
@ -2,110 +2,50 @@
|
|||
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d7c5f0bac5337c5098b18d000ee24a9a58d10a6b98824cd1821af74d7219ebea"
|
||||
name = "github.com/calmh/luhn"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "5b2abb343e70180dbf456397c5fd93f14471b08e"
|
||||
version = "v2.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:59df7ab87ddca233bb586ff32c6feba12bf787260f6694dc45ceecea9d3e1a7e"
|
||||
name = "github.com/cenkalti/backoff"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "1e4cf3da559842a91afcb6ea6141451e6c30c618"
|
||||
version = "v2.1.1"
|
||||
revision = "61153c768f31ee5f130071d08fc82b85208528de"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:50cf302cc52eb618cec25d4daf167ed5593145d43b447b906a9f0d24e271d48b"
|
||||
name = "github.com/felixge/tcpkeepalive"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "5bb0b2dea91e0de550022159b9571aafc72c08ba"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:530233672f656641b365f8efb38ed9fba80e420baff2ce87633813ab3755ed6d"
|
||||
name = "github.com/golang/mock"
|
||||
packages = ["gomock"]
|
||||
pruneopts = ""
|
||||
revision = "51421b967af1f557f93a59e0057aaf15ca02e29c"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3a86b4526cc15ca4caf041c7f6943b59b3eb3711a9bc832de27b0801b826e019"
|
||||
name = "github.com/mmatczuk/go-http-tunnel"
|
||||
packages = [
|
||||
".",
|
||||
"id",
|
||||
"log",
|
||||
"proto",
|
||||
"tunnelmock",
|
||||
]
|
||||
pruneopts = ""
|
||||
revision = "75a30abccc8db9d1149845e9985e80cca57758a2"
|
||||
version = "2.1"
|
||||
revision = "13f360950a79f5864a972c786a10a50e44b69541"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:1285570ced192ee4703ca44573a4eaf19b5a0895af56f9db2210b171eefd2a41"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"http2",
|
||||
"http2/hpack",
|
||||
"idna",
|
||||
"lex/httplex",
|
||||
]
|
||||
pruneopts = ""
|
||||
packages = ["context","http2","http2/hpack","idna","lex/httplex"]
|
||||
revision = "c7086645de248775cbf2373cf5ca4d2fa664b8c1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:39a1a71adca4b837a25dc6b5fcdd9d175e4597c6d3440f107dfeda31230218e4"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"collate",
|
||||
"collate/build",
|
||||
"internal/colltab",
|
||||
"internal/gen",
|
||||
"internal/tag",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"language",
|
||||
"secure/bidirule",
|
||||
"transform",
|
||||
"unicode/bidi",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
"unicode/rangetable",
|
||||
]
|
||||
pruneopts = ""
|
||||
packages = ["collate","collate/build","internal/colltab","internal/gen","internal/tag","internal/triegen","internal/ucd","language","secure/bidirule","transform","unicode/bidi","unicode/cldr","unicode/norm","unicode/rangetable"]
|
||||
revision = "88f656faf3f37f690df1a32515b479415e1a6769"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:cedccf16b71e86db87a24f8d4c70b0a855872eb967cb906a66b95de56aefbd0d"
|
||||
branch = "v2"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
pruneopts = ""
|
||||
revision = "51d6538a90f86fe93ac480b35f37b2be17fef232"
|
||||
version = "v2.2.2"
|
||||
revision = "287cf08546ab5e7e37d55a84f7ed3fd1db036de5"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"github.com/calmh/luhn",
|
||||
"github.com/cenkalti/backoff",
|
||||
"github.com/felixge/tcpkeepalive",
|
||||
"github.com/golang/mock/gomock",
|
||||
"github.com/mmatczuk/go-http-tunnel",
|
||||
"github.com/mmatczuk/go-http-tunnel/id",
|
||||
"github.com/mmatczuk/go-http-tunnel/log",
|
||||
"github.com/mmatczuk/go-http-tunnel/proto",
|
||||
"github.com/mmatczuk/go-http-tunnel/tunnelmock",
|
||||
"golang.org/x/net/http2",
|
||||
"gopkg.in/yaml.v2",
|
||||
]
|
||||
inputs-digest = "654fea302b8b5a71ce4f36f4097d0c0caa8150e46109cfbdd462adcd054a6768"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
|||
14
vendor/github.com/mmatczuk/go-http-tunnel/Gopkg.toml
generated
vendored
14
vendor/github.com/mmatczuk/go-http-tunnel/Gopkg.toml
generated
vendored
|
|
@ -27,24 +27,24 @@
|
|||
|
||||
[[constraint]]
|
||||
name = "github.com/cenkalti/backoff"
|
||||
version = "2.1.0"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/golang/mock"
|
||||
version = "1.2.0"
|
||||
version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/google/gops"
|
||||
version = "0.3.2"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[[constraint]]
|
||||
branch = "v2"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
version = "2.2.2"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/felixge/tcpkeepalive"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/inconshreveable/go-vhost"
|
||||
|
|
|
|||
81
vendor/github.com/mmatczuk/go-http-tunnel/Makefile
generated
vendored
81
vendor/github.com/mmatczuk/go-http-tunnel/Makefile
generated
vendored
|
|
@ -1,10 +1,3 @@
|
|||
|
||||
GO_FILES := $(shell \
|
||||
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
|
||||
-o -name '*.go' -print | cut -b3-)
|
||||
|
||||
LINT_IGNORE := "/id/\|/tunnelmock/\|/vendor/"
|
||||
|
||||
all: clean check test
|
||||
|
||||
.PHONY: clean
|
||||
|
|
@ -16,14 +9,11 @@ fmt:
|
|||
@go fmt ./...
|
||||
|
||||
.PHONY: check
|
||||
check: .check-fmt .check-vet .check-lint .check-ineffassign .check-static .check-misspell .check-vendor
|
||||
check: .check-fmt .check-vet .check-lint .check-ineffassign .check-mega .check-misspell .check-vendor
|
||||
|
||||
.PHONY: .check-fmt
|
||||
.check-fmt:
|
||||
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
|
||||
@cat /dev/null > $(FMT_LOG)
|
||||
@gofmt -e -s -l -d $(GO_FILES) > $(FMT_LOG) || true
|
||||
@[ ! -s "$(FMT_LOG)" ] || (echo "$@ failed:" | cat - $(FMT_LOG) && false)
|
||||
@go fmt ./... | tee /dev/stderr | ifne false
|
||||
|
||||
.PHONY: .check-vet
|
||||
.check-vet:
|
||||
|
|
@ -31,11 +21,10 @@ check: .check-fmt .check-vet .check-lint .check-ineffassign .check-static .check
|
|||
|
||||
.PHONY: .check-lint
|
||||
.check-lint:
|
||||
$(eval LINT_LOG := $(shell mktemp -t golint.XXXXX))
|
||||
@cat /dev/null > $(LINT_LOG)
|
||||
@$(foreach pkg, $(GO_FILES), golint $(pkg | grep -v $LINT_IGNORE) >> $(LINT_LOG) || true;)
|
||||
@[ ! -s "$(LINT_LOG)" ] || (echo "$@ failed:" | cat - $(LINT_LOG) && false)
|
||||
|
||||
@golint `go list ./...` \
|
||||
| grep -v /id/ \
|
||||
| grep -v /tunnelmock/ \
|
||||
| tee /dev/stderr | ifne false
|
||||
|
||||
.PHONY: .check-ineffassign
|
||||
.check-ineffassign:
|
||||
|
|
@ -46,8 +35,8 @@ check: .check-fmt .check-vet .check-lint .check-ineffassign .check-static .check
|
|||
@misspell ./...
|
||||
|
||||
.PHONY: .check-mega
|
||||
.check-static:
|
||||
@staticcheck -checks ['SA1006','ST1005'] ./...
|
||||
.check-mega:
|
||||
@megacheck ./...
|
||||
|
||||
.PHONY: .check-vendor
|
||||
.check-vendor:
|
||||
|
|
@ -67,37 +56,37 @@ get-deps:
|
|||
get-tools:
|
||||
@echo "==> Installing tools..."
|
||||
@go get -u github.com/golang/dep/cmd/dep
|
||||
@go get -u golang.org/x/lint/golint
|
||||
@go get -u github.com/golang/lint/golint
|
||||
@go get -u github.com/golang/mock/gomock
|
||||
|
||||
@go get -u github.com/client9/misspell/cmd/misspell
|
||||
@go get -u github.com/gordonklaus/ineffassign
|
||||
@go get -u github.com/mitchellh/gox
|
||||
@go get -u github.com/tcnksm/ghr
|
||||
@go get -u honnef.co/go/tools/cmd/staticcheck
|
||||
@go get -u honnef.co/go/tools/cmd/megacheck
|
||||
|
||||
OUTPUT_DIR = build
|
||||
OS = "darwin freebsd linux windows"
|
||||
ARCH = "386 amd64 arm"
|
||||
OSARCH = "!darwin/386 !darwin/arm !windows/arm"
|
||||
GIT_COMMIT = $(shell git describe --always)
|
||||
|
||||
.PHONY: release
|
||||
release: check test clean build package
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
mkdir ${OUTPUT_DIR}
|
||||
CGO_ENABLED=0 GOARM=5 gox -ldflags "-w -X main.version=$(GIT_COMMIT)" \
|
||||
-os=${OS} -arch=${ARCH} -osarch=${OSARCH} -output "${OUTPUT_DIR}/pkg/{{.OS}}_{{.Arch}}/{{.Dir}}" \
|
||||
./cmd/tunnel ./cmd/tunneld
|
||||
|
||||
.PHONY: package
|
||||
package:
|
||||
mkdir ${OUTPUT_DIR}/dist
|
||||
cd ${OUTPUT_DIR}/pkg/; for osarch in *; do (cd $$osarch; tar zcvf ../../dist/tunnel_$$osarch.tar.gz ./*); done;
|
||||
cd ${OUTPUT_DIR}/dist; sha256sum * > ./SHA256SUMS
|
||||
|
||||
.PHONY: publish
|
||||
publish:
|
||||
ghr -recreate -u mmatczuk -t ${GITHUB_TOKEN} -r go-http-tunnel pre-release ${OUTPUT_DIR}/dist
|
||||
#OUTPUT_DIR = build
|
||||
#OS = "darwin freebsd linux windows"
|
||||
#ARCH = "386 amd64 arm"
|
||||
#OSARCH = "!darwin/386 !darwin/arm !windows/arm"
|
||||
#GIT_COMMIT = $(shell git describe --always)
|
||||
#
|
||||
#.PHONY: release
|
||||
#release: check test clean build package
|
||||
#
|
||||
#.PHONY: build
|
||||
#build:
|
||||
# mkdir ${OUTPUT_DIR}
|
||||
# CGO_ENABLED=0 GOARM=5 gox -ldflags "-w -X main.version=$(GIT_COMMIT)" \
|
||||
# -os=${OS} -arch=${ARCH} -osarch=${OSARCH} -output "${OUTPUT_DIR}/pkg/{{.OS}}_{{.Arch}}/{{.Dir}}" \
|
||||
# ./cmd/tunnel ./cmd/tunneld
|
||||
#
|
||||
#.PHONY: package
|
||||
#package:
|
||||
# mkdir ${OUTPUT_DIR}/dist
|
||||
# cd ${OUTPUT_DIR}/pkg/; for osarch in *; do (cd $$osarch; tar zcvf ../../dist/tunnel_$$osarch.tar.gz ./*); done;
|
||||
# cd ${OUTPUT_DIR}/dist; sha256sum * > ./SHA256SUMS
|
||||
#
|
||||
#.PHONY: publish
|
||||
#publish:
|
||||
# ghr -recreate -u mmatczuk -t ${GITHUB_TOKEN} -r go-http-tunnel pre-release ${OUTPUT_DIR}/dist
|
||||
|
|
|
|||
73
vendor/github.com/mmatczuk/go-http-tunnel/README.md
generated
vendored
73
vendor/github.com/mmatczuk/go-http-tunnel/README.md
generated
vendored
|
|
@ -6,7 +6,6 @@ Features:
|
|||
|
||||
* HTTP proxy with [basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)
|
||||
* TCP proxy
|
||||
* [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication) vhost proxy
|
||||
* Client auto reconnect
|
||||
* Client management and eviction
|
||||
* Easy to use CLI
|
||||
|
|
@ -68,65 +67,6 @@ $ tunneld -tlsCrt .tunneld/server.crt -tlsKey .tunneld/server.key
|
|||
|
||||
This will run HTTP server on port `80` and HTTPS (HTTP/2) server on port `443`. If you want to use HTTPS it's recommended to get a properly signed certificate to avoid security warnings.
|
||||
|
||||
### Run Server as a Service on Ubuntu using Systemd:
|
||||
|
||||
* After completing the steps above successfully, create a new file for your service (you can name it whatever you want, just replace the name below with your chosen name).
|
||||
|
||||
``` bash
|
||||
$ vim tunneld.service
|
||||
```
|
||||
|
||||
* Add the following configuration to the file
|
||||
|
||||
```
|
||||
[Unit]
|
||||
Description=Go-Http-Tunnel Service
|
||||
After=network.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/path/to/your/tunneld -tlsCrt /path/to/your/folder/.tunneld/server.crt -tlsKey /path/to/your/folder/.tunneld/server.key
|
||||
TimeoutSec=30
|
||||
Restart=on-failure
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
* Save and exit this file.
|
||||
* Move this new file to /etc/systemd/system/
|
||||
|
||||
```bash
|
||||
$ sudo mv tunneld.service /etc/systemd/system/
|
||||
```
|
||||
|
||||
* Change the file permission to allow it to run.
|
||||
|
||||
```bash
|
||||
$ sudo chmod u+x /etc/systemd/system/tunneld.service
|
||||
```
|
||||
|
||||
* Start the new service and make sure you don't get any errors, and that your client is able to connect.
|
||||
|
||||
```bash
|
||||
$ sudo systemctl start tunneld.service
|
||||
```
|
||||
|
||||
* You can stop the service with:
|
||||
|
||||
```bash
|
||||
$ sudo systemctl stop tunneld.service
|
||||
```
|
||||
|
||||
* Finally, if you want the service to start automatically when the server is rebooted, you need to enable it.
|
||||
|
||||
```bash
|
||||
$ sudo systemctl enable tunneld.service
|
||||
```
|
||||
|
||||
There are many more options for systemd services, and this is by not means an exhaustive configuration file.
|
||||
|
||||
## Configuration
|
||||
|
||||
The tunnel client `tunnel` requires configuration file, by default it will try reading `tunnel.yml` in your current working directory. If you want to specify other file use `-config` flag.
|
||||
|
|
@ -150,10 +90,6 @@ looks like this
|
|||
proto: tcp
|
||||
addr: 192.168.0.5:22
|
||||
remote_addr: 0.0.0.0:22
|
||||
tls:
|
||||
proto: sni
|
||||
addr: localhost:443
|
||||
host: tls.my-tunnel-host.com
|
||||
```
|
||||
|
||||
Configuration options:
|
||||
|
|
@ -163,10 +99,10 @@ Configuration options:
|
|||
* `tls_key`: path to client TLS certificate key, *default:* `client.key` *in the config file directory*
|
||||
* `root_ca`: path to trusted root certificate authority pool file, if empty any server certificate is accepted
|
||||
* `tunnels / [name]`
|
||||
* `proto`: tunnel protocol, `http`, `tcp` or `sni`
|
||||
* `proto`: tunnel protocol, `http` or `tcp`
|
||||
* `addr`: forward traffic to this local port number or network address, for `proto=http` this can be full URL i.e. `https://machine/sub/path/?plus=params`, supports URL schemes `http` and `https`
|
||||
* `auth`: (`proto=http`) (optional) basic authentication credentials to enforce on tunneled requests, format `user:password`
|
||||
* `host`: (`proto=http`, `proto=sni`) hostname to request (requires reserved name and DNS CNAME)
|
||||
* `host`: (`proto=http`) hostname to request (requires reserved name and DNS CNAME)
|
||||
* `remote_addr`: (`proto=tcp`) bind the remote TCP address
|
||||
* `backoff`
|
||||
* `interval`: how long client would wait before redialing the server if connection was lost, exponential backoff initial interval, *default:* `500ms`
|
||||
|
|
@ -175,10 +111,9 @@ Configuration options:
|
|||
* `max_time`: maximal time client would try to reconnect to the server if connection was lost, set `0` to never stop trying, *default:* `15m`
|
||||
|
||||
## How it works
|
||||
Client opens a TLS connection to a server. Server accepts connections from known clients only, client is recognised by it's TLS certificate ID. The server is publicly available and proxies incoming connections to the client. Then the connection is further proxied in the client's network.
|
||||
|
||||
A client opens TLS connection to a server. The server accepts connections from known clients only. The client is recognized by its TLS certificate ID. The server is publicly available and proxies incoming connections to the client. Then the connection is further proxied in the client's network.
|
||||
|
||||
The tunnel is based HTTP/2 for speed and security. There is a single TCP connection between client and server and all the proxied connections are multiplexed using HTTP/2.
|
||||
Tunnel is based HTTP/2 for speed and security. There is a single TCP connection between client and server and all the proxied connections are multiplexed using HTTP/2.
|
||||
|
||||
## Donation
|
||||
|
||||
|
|
|
|||
28
vendor/github.com/mmatczuk/go-http-tunnel/cmd/tunnel/config.go
generated
vendored
28
vendor/github.com/mmatczuk/go-http-tunnel/cmd/tunnel/config.go
generated
vendored
|
|
@ -88,10 +88,6 @@ func loadClientConfigFromFile(file string) (*ClientConfig, error) {
|
|||
if err := validateTCP(t); err != nil {
|
||||
return nil, fmt.Errorf("%s %s", name, err)
|
||||
}
|
||||
case proto.SNI:
|
||||
if err := validateSNI(t); err != nil {
|
||||
return nil, fmt.Errorf("%s %s", name, err)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("%s invalid protocol %q", name, t.Protocol)
|
||||
}
|
||||
|
|
@ -144,27 +140,3 @@ func validateTCP(t *Tunnel) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateSNI(t *Tunnel) error {
|
||||
var err error
|
||||
if t.Host == "" {
|
||||
return fmt.Errorf("host: missing")
|
||||
}
|
||||
if t.Addr == "" {
|
||||
return fmt.Errorf("addr: missing")
|
||||
}
|
||||
if t.Addr, err = normalizeAddress(t.Addr); err != nil {
|
||||
return fmt.Errorf("addr: %s", err)
|
||||
}
|
||||
|
||||
// unexpected
|
||||
|
||||
if t.RemoteAddr != "" {
|
||||
return fmt.Errorf("remote_addr: unexpected")
|
||||
}
|
||||
if t.Auth != "" {
|
||||
return fmt.Errorf("auth: unexpected")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
4
vendor/github.com/mmatczuk/go-http-tunnel/cmd/tunnel/options.go
generated
vendored
4
vendor/github.com/mmatczuk/go-http-tunnel/cmd/tunnel/options.go
generated
vendored
|
|
@ -38,10 +38,6 @@ config.yaml:
|
|||
proto: tcp
|
||||
addr: 192.168.0.5:22
|
||||
remote_addr: 0.0.0.0:22
|
||||
tls:
|
||||
proto: sni
|
||||
addr: localhost:443
|
||||
host: tls.my-tunnel-host.com
|
||||
|
||||
Author:
|
||||
Written by M. Matczuk (mmatczuk@gmail.com)
|
||||
|
|
|
|||
2
vendor/github.com/mmatczuk/go-http-tunnel/cmd/tunnel/tunnel.go
generated
vendored
2
vendor/github.com/mmatczuk/go-http-tunnel/cmd/tunnel/tunnel.go
generated
vendored
|
|
@ -182,8 +182,6 @@ func proxy(m map[string]*Tunnel, logger log.Logger) tunnel.ProxyFunc {
|
|||
httpURL[t.Host] = u
|
||||
case proto.TCP, proto.TCP4, proto.TCP6:
|
||||
tcpAddr[t.RemoteAddr] = t.Addr
|
||||
case proto.SNI:
|
||||
tcpAddr[t.Host] = t.Addr
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
10
vendor/github.com/mmatczuk/go-http-tunnel/cmd/tunneld/options.go
generated
vendored
10
vendor/github.com/mmatczuk/go-http-tunnel/cmd/tunneld/options.go
generated
vendored
|
|
@ -16,10 +16,9 @@ options:
|
|||
|
||||
const usage2 string = `
|
||||
Example:
|
||||
tunneld
|
||||
tunneld -clients YMBKT3V-ESUTZ2Z-7MRILIJ-T35FHGO-D2DHO7D-FXMGSSR-V4LBSZX-BNDONQ4
|
||||
tunneld -httpAddr :8080 -httpsAddr ""
|
||||
tunneld -httpsAddr "" -sniAddr ":443" -rootCA client_root.crt -tlsCrt server.crt -tlsKey server.key
|
||||
tuneld
|
||||
tuneld -clients YMBKT3V-ESUTZ2Z-7MRILIJ-T35FHGO-D2DHO7D-FXMGSSR-V4LBSZX-BNDONQ4
|
||||
tuneld -httpAddr :8080 -httpsAddr ""
|
||||
|
||||
Author:
|
||||
Written by M. Matczuk (mmatczuk@gmail.com)
|
||||
|
|
@ -41,7 +40,6 @@ type options struct {
|
|||
httpAddr string
|
||||
httpsAddr string
|
||||
tunnelAddr string
|
||||
sniAddr string
|
||||
tlsCrt string
|
||||
tlsKey string
|
||||
rootCA string
|
||||
|
|
@ -54,7 +52,6 @@ func parseArgs() *options {
|
|||
httpAddr := flag.String("httpAddr", ":80", "Public address for HTTP connections, empty string to disable")
|
||||
httpsAddr := flag.String("httpsAddr", ":443", "Public address listening for HTTPS connections, emptry string to disable")
|
||||
tunnelAddr := flag.String("tunnelAddr", ":5223", "Public address listening for tunnel client")
|
||||
sniAddr := flag.String("sniAddr", "", "Public address listening for TLS SNI connections, empty string to disable")
|
||||
tlsCrt := flag.String("tlsCrt", "server.crt", "Path to a TLS certificate file")
|
||||
tlsKey := flag.String("tlsKey", "server.key", "Path to a TLS key file")
|
||||
rootCA := flag.String("rootCA", "", "Path to the trusted certificate chian used for client certificate authentication, if empty any client certificate is accepted")
|
||||
|
|
@ -67,7 +64,6 @@ func parseArgs() *options {
|
|||
httpAddr: *httpAddr,
|
||||
httpsAddr: *httpsAddr,
|
||||
tunnelAddr: *tunnelAddr,
|
||||
sniAddr: *sniAddr,
|
||||
tlsCrt: *tlsCrt,
|
||||
tlsKey: *tlsKey,
|
||||
rootCA: *rootCA,
|
||||
|
|
|
|||
15
vendor/github.com/mmatczuk/go-http-tunnel/cmd/tunneld/tunneld.go
generated
vendored
15
vendor/github.com/mmatczuk/go-http-tunnel/cmd/tunneld/tunneld.go
generated
vendored
|
|
@ -42,7 +42,6 @@ func main() {
|
|||
// setup server
|
||||
server, err := tunnel.NewServer(&tunnel.ServerConfig{
|
||||
Addr: opts.tunnelAddr,
|
||||
SNIAddr: opts.sniAddr,
|
||||
AutoSubscribe: autoSubscribe,
|
||||
TLSConfig: tlsconf,
|
||||
Logger: logger,
|
||||
|
|
@ -123,14 +122,12 @@ func tlsConfig(opts *options) (*tls.Config, error) {
|
|||
}
|
||||
|
||||
return &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
ClientAuth: clientAuth,
|
||||
ClientCAs: roots,
|
||||
SessionTicketsDisabled: true,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CipherSuites: []uint16{
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
Certificates: []tls.Certificate{cert},
|
||||
ClientAuth: clientAuth,
|
||||
ClientCAs: roots,
|
||||
SessionTicketsDisabled: true,
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
|
||||
PreferServerCipherSuites: true,
|
||||
NextProtos: []string{"h2"},
|
||||
}, nil
|
||||
|
|
|
|||
1
vendor/github.com/mmatczuk/go-http-tunnel/proto/controlmsg.go
generated
vendored
1
vendor/github.com/mmatczuk/go-http-tunnel/proto/controlmsg.go
generated
vendored
|
|
@ -32,7 +32,6 @@ const (
|
|||
TCP4 = "tcp4"
|
||||
TCP6 = "tcp6"
|
||||
UNIX = "unix"
|
||||
SNI = "sni"
|
||||
)
|
||||
|
||||
// ControlMessage is sent from server to client before streaming data. It's
|
||||
|
|
|
|||
92
vendor/github.com/mmatczuk/go-http-tunnel/server.go
generated
vendored
92
vendor/github.com/mmatczuk/go-http-tunnel/server.go
generated
vendored
|
|
@ -18,7 +18,6 @@ import (
|
|||
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"github.com/inconshreveable/go-vhost"
|
||||
"github.com/mmatczuk/go-http-tunnel/id"
|
||||
"github.com/mmatczuk/go-http-tunnel/log"
|
||||
"github.com/mmatczuk/go-http-tunnel/proto"
|
||||
|
|
@ -39,8 +38,6 @@ type ServerConfig struct {
|
|||
Listener net.Listener
|
||||
// Logger is optional logger. If nil logging is disabled.
|
||||
Logger log.Logger
|
||||
// Addr is TCP address to listen for TLS SNI connections
|
||||
SNIAddr string
|
||||
}
|
||||
|
||||
// Server is responsible for proxying public connections to the client over a
|
||||
|
|
@ -53,7 +50,6 @@ type Server struct {
|
|||
connPool *connPool
|
||||
httpClient *http.Client
|
||||
logger log.Logger
|
||||
vhostMuxer *vhost.TLSMuxer
|
||||
}
|
||||
|
||||
// NewServer creates a new Server.
|
||||
|
|
@ -86,54 +82,6 @@ func NewServer(config *ServerConfig) (*Server, error) {
|
|||
},
|
||||
}
|
||||
|
||||
if config.SNIAddr != "" {
|
||||
l, err := net.Listen("tcp", config.SNIAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mux, err := vhost.NewTLSMuxer(l, DefaultTimeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("SNI Muxer creation failed: %s", err)
|
||||
}
|
||||
s.vhostMuxer = mux
|
||||
go func() {
|
||||
for {
|
||||
conn, err := mux.NextError()
|
||||
vhostName := ""
|
||||
tlsConn, ok := conn.(*vhost.TLSConn)
|
||||
if ok {
|
||||
vhostName = tlsConn.Host()
|
||||
}
|
||||
|
||||
switch err.(type) {
|
||||
case vhost.BadRequest:
|
||||
logger.Log(
|
||||
"level", 0,
|
||||
"action", "got a bad request!",
|
||||
"addr", conn.RemoteAddr(),
|
||||
)
|
||||
case vhost.NotFound:
|
||||
|
||||
logger.Log(
|
||||
"level", 0,
|
||||
"action", "got a connection for an unknown vhost",
|
||||
"addr", vhostName,
|
||||
)
|
||||
case vhost.Closed:
|
||||
logger.Log(
|
||||
"level", 0,
|
||||
"action", "closed conn",
|
||||
"addr", vhostName,
|
||||
)
|
||||
}
|
||||
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
|
|
@ -438,25 +386,6 @@ func (s *Server) addTunnels(tunnels map[string]*proto.Tunnel, identifier id.ID)
|
|||
"addr", l.Addr(),
|
||||
)
|
||||
|
||||
i.Listeners = append(i.Listeners, l)
|
||||
case proto.SNI:
|
||||
if s.vhostMuxer == nil {
|
||||
err = fmt.Errorf("unable to configure SNI for tunnel %s: %s", name, t.Protocol)
|
||||
goto rollback
|
||||
}
|
||||
var l net.Listener
|
||||
l, err = s.vhostMuxer.Listen(t.Host)
|
||||
if err != nil {
|
||||
goto rollback
|
||||
}
|
||||
|
||||
s.logger.Log(
|
||||
"level", 2,
|
||||
"action", "add SNI vhost",
|
||||
"identifier", identifier,
|
||||
"host", t.Host,
|
||||
)
|
||||
|
||||
i.Listeners = append(i.Listeners, l)
|
||||
default:
|
||||
err = fmt.Errorf("unsupported protocol for tunnel %s: %s", name, t.Protocol)
|
||||
|
|
@ -501,8 +430,7 @@ func (s *Server) listen(l net.Listener, identifier id.ID) {
|
|||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "use of closed network connection") ||
|
||||
strings.Contains(err.Error(), "Listener closed") {
|
||||
if strings.Contains(err.Error(), "use of closed network connection") {
|
||||
s.logger.Log(
|
||||
"level", 2,
|
||||
"action", "listener closed",
|
||||
|
|
@ -524,20 +452,11 @@ func (s *Server) listen(l net.Listener, identifier id.ID) {
|
|||
|
||||
msg := &proto.ControlMessage{
|
||||
Action: proto.ActionProxy,
|
||||
ForwardedHost: l.Addr().String(),
|
||||
ForwardedProto: l.Addr().Network(),
|
||||
}
|
||||
|
||||
tlsConn, ok := conn.(*vhost.TLSConn)
|
||||
if ok {
|
||||
msg.ForwardedHost = tlsConn.Host()
|
||||
err = keepAlive(tlsConn.Conn)
|
||||
|
||||
} else {
|
||||
msg.ForwardedHost = l.Addr().String()
|
||||
err = keepAlive(conn)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err := keepAlive(conn); err != nil {
|
||||
s.logger.Log(
|
||||
"level", 1,
|
||||
"msg", "TCP keepalive for tunneled connection failed",
|
||||
|
|
@ -684,10 +603,7 @@ func (s *Server) proxyConn(identifier id.ID, conn net.Conn, msg *proto.ControlMe
|
|||
"src", identifier,
|
||||
))
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(DefaultTimeout):
|
||||
}
|
||||
<-done
|
||||
|
||||
s.logger.Log(
|
||||
"level", 2,
|
||||
|
|
|
|||
2
vendor/github.com/mmatczuk/go-http-tunnel/tcpproxy.go
generated
vendored
2
vendor/github.com/mmatczuk/go-http-tunnel/tcpproxy.go
generated
vendored
|
|
@ -57,7 +57,7 @@ func NewMultiTCPProxy(localAddrMap map[string]string, logger log.Logger) *TCPPro
|
|||
// Proxy is a ProxyFunc.
|
||||
func (p *TCPProxy) Proxy(w io.Writer, r io.ReadCloser, msg *proto.ControlMessage) {
|
||||
switch msg.ForwardedProto {
|
||||
case proto.TCP, proto.TCP4, proto.TCP6, proto.UNIX, proto.SNI:
|
||||
case proto.TCP, proto.TCP4, proto.TCP6, proto.UNIX:
|
||||
// ok
|
||||
default:
|
||||
p.logger.Log(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue