Browse Source

http authenticator

Darien Raymond 9 years ago
parent
commit
ac3b91a877

+ 53 - 0
transport/internet/authenticators/http/config.go

@@ -0,0 +1,53 @@
+package http
+
+import (
+	"v2ray.com/core/common/dice"
+)
+
+func pickString(arr []string) string {
+	n := len(arr)
+	if n == 0 {
+		return ""
+	}
+	return arr[dice.Roll(n)]
+}
+
+func (this *RequestConfig) PickUri() string {
+	return pickString(this.Uri)
+}
+
+func (this *RequestConfig) PickHeaders() []string {
+	n := len(this.Header)
+	if n == 0 {
+		return nil
+	}
+	headers := make([]string, n)
+	for idx, headerConfig := range this.Header {
+		headerName := headerConfig.Name
+		headerValue := pickString(headerConfig.Value)
+		headers[idx] = headerName + ": " + headerValue
+	}
+	return headers
+}
+
+func (this *RequestConfig) GetVersion() string {
+	return "HTTP/" + this.Version
+}
+
+func (this *ResponseConfig) PickHeaders() []string {
+	n := len(this.Header)
+	if n == 0 {
+		return nil
+	}
+	headers := make([]string, n)
+	for idx, headerConfig := range this.Header {
+		headerName := headerConfig.Name
+		headerValue := pickString(headerConfig.Value)
+		headers[idx] = headerName + ": " + headerValue
+	}
+	return headers
+}
+
+func (this *ResponseConfig) GetVersion() string {
+	return "HTTP/" + this.Version
+}

+ 128 - 0
transport/internet/authenticators/http/config.pb.go

@@ -0,0 +1,128 @@
+// Code generated by protoc-gen-go.
+// source: v2ray.com/core/transport/internet/authenticators/http/config.proto
+// DO NOT EDIT!
+
+/*
+Package http is a generated protocol buffer package.
+
+It is generated from these files:
+	v2ray.com/core/transport/internet/authenticators/http/config.proto
+
+It has these top-level messages:
+	Header
+	HeaderEnding
+	RequestConfig
+	ResponseConfig
+*/
+package http
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+type Header struct {
+	// "Accept", "Cookie", etc
+	Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
+	// Each entry must be valid in one piece. Random entry will be chosen if multiple entries present.
+	Value []string `protobuf:"bytes,2,rep,name=value" json:"value,omitempty"`
+}
+
+func (m *Header) Reset()                    { *m = Header{} }
+func (m *Header) String() string            { return proto.CompactTextString(m) }
+func (*Header) ProtoMessage()               {}
+func (*Header) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
+
+type HeaderEnding struct {
+	Value string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"`
+}
+
+func (m *HeaderEnding) Reset()                    { *m = HeaderEnding{} }
+func (m *HeaderEnding) String() string            { return proto.CompactTextString(m) }
+func (*HeaderEnding) ProtoMessage()               {}
+func (*HeaderEnding) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
+
+type RequestConfig struct {
+	// Full HTTP version like "1.1".
+	Version string `protobuf:"bytes,1,opt,name=version" json:"version,omitempty"`
+	// GET, POST, CONNECT etc
+	Method string `protobuf:"bytes,2,opt,name=method" json:"method,omitempty"`
+	// URI like "/login.php"
+	Uri    []string  `protobuf:"bytes,3,rep,name=uri" json:"uri,omitempty"`
+	Header []*Header `protobuf:"bytes,4,rep,name=header" json:"header,omitempty"`
+}
+
+func (m *RequestConfig) Reset()                    { *m = RequestConfig{} }
+func (m *RequestConfig) String() string            { return proto.CompactTextString(m) }
+func (*RequestConfig) ProtoMessage()               {}
+func (*RequestConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
+
+func (m *RequestConfig) GetHeader() []*Header {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+type ResponseConfig struct {
+	Version string    `protobuf:"bytes,1,opt,name=version" json:"version,omitempty"`
+	Status  string    `protobuf:"bytes,2,opt,name=status" json:"status,omitempty"`
+	Reason  string    `protobuf:"bytes,3,opt,name=reason" json:"reason,omitempty"`
+	Header  []*Header `protobuf:"bytes,4,rep,name=header" json:"header,omitempty"`
+}
+
+func (m *ResponseConfig) Reset()                    { *m = ResponseConfig{} }
+func (m *ResponseConfig) String() string            { return proto.CompactTextString(m) }
+func (*ResponseConfig) ProtoMessage()               {}
+func (*ResponseConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
+
+func (m *ResponseConfig) GetHeader() []*Header {
+	if m != nil {
+		return m.Header
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*Header)(nil), "v2ray.core.transport.internet.authenticators.http.Header")
+	proto.RegisterType((*HeaderEnding)(nil), "v2ray.core.transport.internet.authenticators.http.HeaderEnding")
+	proto.RegisterType((*RequestConfig)(nil), "v2ray.core.transport.internet.authenticators.http.RequestConfig")
+	proto.RegisterType((*ResponseConfig)(nil), "v2ray.core.transport.internet.authenticators.http.ResponseConfig")
+}
+
+func init() {
+	proto.RegisterFile("v2ray.com/core/transport/internet/authenticators/http/config.proto", fileDescriptor0)
+}
+
+var fileDescriptor0 = []byte{
+	// 303 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xb4, 0x92, 0x31, 0x4b, 0xfc, 0x40,
+	0x10, 0xc5, 0xc9, 0xe5, 0xfe, 0xf9, 0x73, 0x73, 0x2a, 0xb2, 0x88, 0x6c, 0x79, 0x1c, 0x16, 0x57,
+	0x6d, 0xf0, 0xc4, 0xc2, 0xf6, 0x44, 0x10, 0x2b, 0x4d, 0x69, 0xb7, 0xe6, 0xc6, 0x4b, 0xc0, 0xec,
+	0xc4, 0xd9, 0x49, 0xc0, 0x2f, 0x64, 0xe7, 0x77, 0x94, 0x64, 0x13, 0xc5, 0xce, 0x2b, 0xec, 0xf6,
+	0xcd, 0xce, 0xef, 0xcd, 0x1b, 0x18, 0xd8, 0xb4, 0x6b, 0xb6, 0x6f, 0x26, 0xa7, 0x2a, 0xcd, 0x89,
+	0x31, 0x15, 0xb6, 0xce, 0xd7, 0xc4, 0x92, 0x96, 0x4e, 0x90, 0x1d, 0x4a, 0x6a, 0x1b, 0x29, 0xd0,
+	0x49, 0x99, 0x5b, 0x21, 0xf6, 0x69, 0x21, 0x52, 0xa7, 0x39, 0xb9, 0xe7, 0x72, 0x67, 0x6a, 0x26,
+	0x21, 0x75, 0x3e, 0x7a, 0x30, 0x9a, 0x2f, 0xde, 0x8c, 0xbc, 0xf9, 0xc9, 0x9b, 0x8e, 0x5f, 0xae,
+	0x21, 0xb9, 0x45, 0xbb, 0x45, 0x56, 0x0a, 0xa6, 0xce, 0x56, 0xa8, 0xa3, 0x45, 0xb4, 0x9a, 0x65,
+	0xfd, 0x5b, 0x9d, 0xc0, 0xbf, 0xd6, 0xbe, 0x34, 0xa8, 0x27, 0x8b, 0x78, 0x35, 0xcb, 0x82, 0x58,
+	0x9e, 0xc1, 0x41, 0x60, 0x6e, 0xdc, 0xb6, 0x74, 0xbb, 0xef, 0xae, 0x80, 0x0e, 0x5d, 0xef, 0x11,
+	0x1c, 0x66, 0xf8, 0xda, 0xa0, 0x97, 0xeb, 0x3e, 0xa4, 0xd2, 0xf0, 0xbf, 0x45, 0xf6, 0x25, 0xb9,
+	0xa1, 0x73, 0x94, 0xea, 0x14, 0x92, 0x0a, 0xa5, 0xa0, 0xad, 0x9e, 0xf4, 0x1f, 0x83, 0x52, 0xc7,
+	0x10, 0x37, 0x5c, 0xea, 0xb8, 0x9f, 0xde, 0x3d, 0xd5, 0x03, 0x24, 0x45, 0x3f, 0x5b, 0x4f, 0x17,
+	0xf1, 0x6a, 0xbe, 0xbe, 0x32, 0x7b, 0xef, 0x6c, 0x42, 0xf8, 0x6c, 0x30, 0x5a, 0x7e, 0x44, 0x70,
+	0x94, 0xa1, 0xaf, 0xc9, 0x79, 0xfc, 0x4d, 0x52, 0x2f, 0x56, 0x1a, 0x3f, 0x26, 0x0d, 0xaa, 0xab,
+	0x33, 0x5a, 0x4f, 0x4e, 0xc7, 0xa1, 0x1e, 0xd4, 0x1f, 0xe4, 0xdd, 0xdc, 0xc1, 0x65, 0x4e, 0xd5,
+	0xfe, 0x3e, 0x9b, 0x79, 0xd8, 0xee, 0xbe, 0xbb, 0x95, 0xc7, 0x69, 0x57, 0x7a, 0x4a, 0xfa, 0xc3,
+	0xb9, 0xf8, 0x0c, 0x00, 0x00, 0xff, 0xff, 0x78, 0x8c, 0xa7, 0x40, 0x7e, 0x02, 0x00, 0x00,
+}

+ 41 - 0
transport/internet/authenticators/http/config.proto

@@ -0,0 +1,41 @@
+syntax = "proto3";
+
+package v2ray.core.transport.internet.authenticators.http;
+option go_package = "http";
+option java_package = "com.v2ray.core.transport.internet.authenticators.http";
+option java_outer_classname = "ConfigProto";
+
+message Header {
+  // "Accept", "Cookie", etc
+  string name = 1;
+
+  // Each entry must be valid in one piece. Random entry will be chosen if multiple entries present.
+  repeated string value = 2;
+}
+
+message HeaderEnding {
+  string value = 1;
+}
+
+message RequestConfig {
+  // Full HTTP version like "1.1".
+  string version = 1;
+
+  // GET, POST, CONNECT etc
+  string method = 2;
+
+  // URI like "/login.php"
+  repeated string uri = 3;
+
+  repeated Header header = 4;
+}
+
+message ResponseConfig {
+  string version = 1;
+
+  string status = 2;
+
+  string reason = 3;
+
+  repeated Header header = 4;
+}

+ 120 - 0
transport/internet/authenticators/http/http.go

@@ -0,0 +1,120 @@
+package http
+
+import (
+	"bytes"
+	"io"
+	"v2ray.com/core/common/alloc"
+	"v2ray.com/core/transport/internet"
+)
+
+const (
+	CRLF   = "\r\n"
+	ENDING = CRLF + CRLF
+)
+
+type RequestAuthenticator struct {
+	config *RequestConfig
+}
+
+func (this *RequestAuthenticator) Seal(writer io.Writer) io.Writer {
+	header := alloc.NewLocalBuffer(2048)
+	header.AppendString(this.config.Method).AppendString(" ").AppendString(this.config.PickUri()).AppendString(" ").AppendString(this.config.GetVersion()).AppendString(CRLF)
+
+	headers := this.config.PickHeaders()
+	for _, h := range headers {
+		header.AppendString(h).AppendString(CRLF)
+	}
+	header.AppendString(CRLF)
+
+	writer.Write(header.Value)
+	header.Release()
+
+	return writer
+}
+
+func (this *RequestAuthenticator) Open(reader io.Reader) (io.Reader, error) {
+	buffer := alloc.NewLocalBuffer(2048)
+	for {
+		_, err := buffer.FillFrom(reader)
+		if err != nil {
+			return nil, err
+		}
+		if n := bytes.Index(buffer.Value, []byte(ENDING)); n != -1 {
+			buffer.SliceFrom(n + len(ENDING))
+			return &BufferAndReader{
+				buffer: buffer,
+				reader: reader,
+			}, nil
+		}
+		if buffer.Len() >= len(ENDING) {
+			copy(buffer.Value, buffer.Value[buffer.Len()-len(ENDING):])
+			buffer.Slice(0, len(ENDING))
+		}
+	}
+}
+
+type BufferAndReader struct {
+	buffer *alloc.Buffer
+	reader io.Reader
+}
+
+func (this *BufferAndReader) Read(b []byte) (int, error) {
+	if this.buffer.Len() == 0 {
+		return this.reader.Read(b)
+	}
+	n, err := this.buffer.Read(b)
+	if n == this.buffer.Len() {
+		this.buffer.Release()
+		this.buffer = nil
+	}
+	return n, err
+}
+
+type RequestAuthenticatorFactory struct{}
+
+func (RequestAuthenticatorFactory) Create(config interface{}) internet.ConnectionAuthenticator {
+	return &RequestAuthenticator{
+		config: config.(*RequestConfig),
+	}
+}
+
+type ResponseAuthenticator struct {
+	config *ResponseConfig
+}
+
+func (this *ResponseAuthenticator) Seal(writer io.Writer) io.Writer {
+	header := alloc.NewLocalBuffer(2048)
+	header.AppendString(this.config.GetVersion()).AppendString(" ").AppendString(this.config.Status).AppendString(" ").AppendString(this.config.Reason).AppendString(CRLF)
+
+	headers := this.config.PickHeaders()
+	for _, h := range headers {
+		header.AppendString(h).AppendString(CRLF)
+	}
+	header.AppendString(CRLF)
+
+	writer.Write(header.Value)
+	header.Release()
+
+	return writer
+}
+
+func (this *ResponseAuthenticator) Open(reader io.Reader) (io.Reader, error) {
+	buffer := alloc.NewLocalBuffer(2048)
+	for {
+		_, err := buffer.FillFrom(reader)
+		if err != nil {
+			return nil, err
+		}
+		if n := bytes.Index(buffer.Value, []byte(ENDING)); n != -1 {
+			buffer.SliceFrom(n + len(ENDING))
+			return &BufferAndReader{
+				buffer: buffer,
+				reader: reader,
+			}, nil
+		}
+		if buffer.Len() >= len(ENDING) {
+			copy(buffer.Value, buffer.Value[buffer.Len()-len(ENDING):])
+			buffer.Slice(0, len(ENDING))
+		}
+	}
+}

+ 38 - 0
transport/internet/authenticators/http/http_test.go

@@ -0,0 +1,38 @@
+package http_test
+
+import (
+	"testing"
+
+	"v2ray.com/core/common/alloc"
+	"v2ray.com/core/testing/assert"
+	. "v2ray.com/core/transport/internet/authenticators/http"
+)
+
+func TestRequestOpenSeal(t *testing.T) {
+	assert := assert.On(t)
+
+	content := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g'}
+
+	cache := alloc.NewLargeBuffer()
+	http := (RequestAuthenticatorFactory{}).Create(&RequestConfig{
+		Method:  "GET",
+		Uri:     []string{"/"},
+		Version: "1.1",
+		Header: []*Header{
+			{
+				Name:  "Content-Length",
+				Value: []string{"123"},
+			},
+		},
+	})
+
+	http.Seal(cache).Write(content)
+
+	actualContent := make([]byte, 256)
+	reader, err := http.Open(cache)
+	assert.Error(err).IsNil()
+
+	n, err := reader.Read(actualContent)
+	assert.Error(err).IsNil()
+	assert.Bytes(content).Equals(actualContent[:n])
+}

+ 18 - 0
transport/internet/authenticators/noop/noop.go

@@ -1,6 +1,7 @@
 package noop
 
 import (
+	"io"
 	"v2ray.com/core/common/alloc"
 	"v2ray.com/core/common/loader"
 	"v2ray.com/core/transport/internet"
@@ -22,6 +23,23 @@ func (this NoOpAuthenticatorFactory) Create(config interface{}) internet.Authent
 	return NoOpAuthenticator{}
 }
 
+type NoOpConnectionAuthenticator struct{}
+
+func (NoOpConnectionAuthenticator) Open(reader io.Reader) (bool, io.Reader) {
+	return true, reader
+}
+
+func (NoOpConnectionAuthenticator) Seal(writer io.Writer) io.Writer {
+	return writer
+}
+
+type NoOpConnectionAuthenticatorFactory struct{}
+
+func (NoOpConnectionAuthenticatorFactory) Create(config interface{}) internet.ConnectionAuthenticator {
+	return NoOpConnectionAuthenticator{}
+}
+
 func init() {
 	internet.RegisterAuthenticator(loader.GetType(new(Config)), NoOpAuthenticatorFactory{})
+	internet.RegisterConnectionAuthenticator(loader.GetType(new(Config)), NoOpConnectionAuthenticatorFactory{})
 }

+ 35 - 0
transport/internet/conn_authenticator.go

@@ -0,0 +1,35 @@
+package internet
+
+import (
+	"io"
+	"v2ray.com/core/common"
+)
+
+type ConnectionAuthenticator interface {
+	Seal(io.Writer) io.Writer
+	Open(io.Reader) (io.Reader, error)
+}
+
+type ConnectionAuthenticatorFactory interface {
+	Create(interface{}) ConnectionAuthenticator
+}
+
+var (
+	connectionAuthenticatorCache = make(map[string]ConnectionAuthenticatorFactory)
+)
+
+func RegisterConnectionAuthenticator(name string, factory ConnectionAuthenticatorFactory) error {
+	if _, found := connectionAuthenticatorCache[name]; found {
+		return common.ErrDuplicatedName
+	}
+	connectionAuthenticatorCache[name] = factory
+	return nil
+}
+
+func CreateConnectionAuthenticator(name string, config interface{}) (ConnectionAuthenticator, error) {
+	factory, found := connectionAuthenticatorCache[name]
+	if !found {
+		return nil, common.ErrObjectNotFound
+	}
+	return factory.Create(config), nil
+}