serve2

package module
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 22, 2015 License: MIT Imports: 3 Imported by: 2

README

serve2 GoDoc Build Status

A protocol detecting server library. For examples. look at the docs for the github.com/joushou/serve2/proto package.

serve2 accepts a connection, and runs it through the active Protocols, reading the smallest amount of data necessary to identify the protocol. Protocols do not need to be certain about how much data they need up front - they can ask for more as needed. A default Protocol can be used when the server fails to identify the protocol, or one can rely on the default behaviour that closes the socket.

Protocols can implement transports themselves, by returning a new net.Conn that will be fed through the protocol detection mechanism again, allowing for transparent support of transports. HTTPS (for HTTP/1.1) is implemented in this manner simply by adding the TLS transport independent of HTTP, leaving HTTP completely unaware of TLS. This also allows for things like SSH over TLS, or any other protocol for that matter.

serve2 comes with a set of Protocols ready for consumption, in the form of TLS, HTTP (consuming a http.Handler), ECHO and DISCARD. There is also a very convenient Proxy Protocol, which instead of managing the protocol itself simply checks for a predefined set of bytes, and when matched, dials a configured service, leaving the actual protocol up to someone else. This is useful for things like SSH.

Ensuring that the read bytes are fed back in is done by ProxyConn, a net.Conn-implementing type with a buffered Read.

Installation and documentation

To get:

  go get github.com/joushou/serve2

More info and examples at:

Why?

Well, I always kind of wanted to make something that could understand everything. I get those kinds of ideas occasionally. At one point I remembered that idea, and hving gotten caught by the Go fever, I thought I'd try it out in Go, which proved to be very suitable for the idea.

Uses

Apart from how cool it is to be able to serve everything on any port, it also allows flexibility when firewall rules are present. Ever had to run SSH on port 80 to get through a firewall? You know, annoying corporate networks, or maybe difficulties with annoying ISP's and your home server. Well, now you can still have a nice web server on port 80 and still serve SSH. Or maybe you had a packet inspecting firewall that didn't think that was a good idea? Use openssl's s_client to open a TLS transport to port 443, and SSH to that then!

Limitations

serve2 cannot detect /all/ protocols. It's not really possible to detect DISCARD or ECHO, for example, as the client does not send any recognizable array of bytes before expecting the server to reply. Instead, they require that you send "ECHO" or "DISCARD" as the first message you want echoed or discarded.

In order to be able to detect a protocol, the client will have to send something either immediately on connect, or at the latest before expecting the server to reply/do anything. What it sends must furthermore either be a static magic, or a dynamic message within such boundaries that a pattern can be validated programmatically. Static "magics" can be seen in the form of SSH that starts out by sending "SSH..." (... being a longer version string), and more dynamic ones involve TLS that does not send a magic, but always starts by sending a ClientHello, of which the first byte is 0x16 (to inform that this is a handshake), followed by major/minor version numbers and the handshake type (which for the first message is always ClientHello, 0x01). With this information, one can verify the message/handshake type and major/minor version number ranges, and establish with a decent probability that this is indeed a TLS ClientHello handshake.

While Protocols can ask for as much data as they can dream of, and can incrementally increase how much data they need (in case dynamic patterns also have dynamic lengths, for example), but it is suggested that the detection amount is kept as small as possible while also maintaining good probability of the protocol. SSH can be detected with extremely high probability by reading 3 bytes, which is a nice and small amount, and HTTP can be detected by looking at the HTTP method (and increasing the read amount if longer method names must be tested).

Performance

This depends heavily on both the quantity of registered handlers, and the individual handlers themselves. Every time more data is has to be read, serve2 will read the smallest amount that was requested by the remaining handlers (handlers that need len(requested) >= len(read)), resulting in a few unnecessary calls to handlers. As long as the handlers are simple checks, and it doesn't end up running through this iteration several hundred times, then the overhead should not be too bad. A future optimization to ensure that handlers are only called when len(read) >= len(requested), in order to lower the overhead.

What does the name mean?

Nothing. I called the toy project "serve", and when making a new version I had to use a new folder name, and so "serve2" was born.

Documentation

Overview

Package serve2 provides a mechanism to detect and handle multiple protocols on a single net.Conn or net.Listener.

Protocols are defined as handlers that, given the amount of header bytes they require, can check if a header matches the protocol, as well as handle the connection itself afterwards.

The read bytes from the header are provided to the handler by ProxyConn, that simulates the first few reads until the header buffer is empty, at which point it resumes normal operation.

Any protocol where the client writes data that is unique to the protocol to the server immediately after opening the connection can be handled by serve2. Echo and discard do not really fit here, as they would normally put no constaints on the client - instead, serve2's test implementations require that you write ECHO or DISCARD to the connection to trigger those protocol handlers.

Index

Constants

View Source
const (
	// DefaultBytesToCheck default maximum amount of bytes to check
	DefaultBytesToCheck = 128
)

Variables

View Source
var (
	ErrGreedyHandler = errors.New("remaining handlers too greedy")
)

Errors

Functions

This section is empty.

Types

type Logger

type Logger func(format string, v ...interface{})

Logger is used to provide logging functionality for serve2

type Protocol

type Protocol interface {
	// Check informs if the bytes match the protocol. If there is not enough
	// data yet, it should return false and the wanted amount of bytes, allowing
	// future calls when more data is available. It does not need to return the
	// same every time, and incrementally checking more and more data is
	// allowed. Returning false and 0 bytes needed means that the protocol
	// handler is 100% sure that this is not the proper protocol, and will not
	// result in any further calls.
	// Check, when called with nil, nil, must return false, N, where N is the
	// smallest amount of bytes that makes sense to call Check with.
	Check(header []byte, hints []interface{}) (ok bool, needed int)

	// Handle manages the protocol. In case of an encapsulating protocol, Handle
	// can return a net.Conn which will be thrown through the entire protocol
	// management show again.
	Handle(c net.Conn) (net.Conn, error)
}

Protocol is the protocol detection and handling interface used by serve2.

type ProtocolHandler

type ProtocolHandler Protocol

ProtocolHandler is a legacy alias for Protocol

type Server

type Server struct {
	// DefaultProtocol is the protocol fallback if no match is made
	DefaultProtocol Protocol

	// Logger is used for logging if set
	Logger Logger

	// BytesToCheck is the max amount of bytes to check
	BytesToCheck int

	// Protocols is the list of protocols
	Protocols []Protocol
}

Server handles a set of Protocols.

func New

func New() *Server

New returns a new Server.

func (*Server) AddHandler

func (s *Server) AddHandler(p Protocol)

AddHandler registers a Protocol

func (*Server) AddHandlers

func (s *Server) AddHandlers(p ...Protocol)

AddHandlers registers a set of Protocols

func (*Server) HandleConn

func (s *Server) HandleConn(c net.Conn, hints []interface{}) error

HandleConn runs a connection through protocol detection and handling as needed.

func (*Server) Serve

func (s *Server) Serve(l net.Listener) error

Serve accepts connections on a listener, handling them as appropriate.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL