mirror of
https://github.com/ehang-io/nps.git
synced 2025-09-08 00:26:52 +00:00
add new file
This commit is contained in:
34
lib/cert/cert.go
Normal file
34
lib/cert/cert.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// GetCertSnFromConfig return SerialNumber by tls.Config
|
||||
func GetCertSnFromConfig(config *tls.Config) (string, error) {
|
||||
if len(config.Certificates) == 0 || len(config.Certificates[0].Certificate) == 0 {
|
||||
return "", errors.New("certificates is empty")
|
||||
}
|
||||
return GetCertSnFromBlock(config.Certificates[0].Certificate[0])
|
||||
}
|
||||
|
||||
// GetCertSnFromEncode return SerialNumber by encoded cert
|
||||
func GetCertSnFromEncode(b []byte) (string, error) {
|
||||
block, _ := pem.Decode(b)
|
||||
if block == nil {
|
||||
return "", errors.New("block is not a cert encoded")
|
||||
}
|
||||
return GetCertSnFromBlock(block.Bytes)
|
||||
}
|
||||
|
||||
// GetCertSnFromBlock return SerialNumber by decode block
|
||||
func GetCertSnFromBlock(block []byte) (string, error) {
|
||||
cert, err := x509.ParseCertificate(block)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "ParseCertificate")
|
||||
}
|
||||
return cert.SerialNumber.String(), nil
|
||||
}
|
42
lib/cert/cert_test.go
Normal file
42
lib/cert/cert_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509/pkix"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetCertSerialNumber(t *testing.T) {
|
||||
g := NewX509Generator(pkix.Name{
|
||||
Country: []string{"CN"},
|
||||
Organization: []string{"Ehang.io"},
|
||||
OrganizationalUnit: []string{"nps"},
|
||||
Province: []string{"Beijing"},
|
||||
CommonName: "nps",
|
||||
Locality: []string{"Beijing"},
|
||||
})
|
||||
cert, key, err := g.CreateRootCa()
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, os.WriteFile(filepath.Join(os.TempDir(), "cert.pem"), cert, 0600))
|
||||
assert.NoError(t, os.WriteFile(filepath.Join(os.TempDir(), "key.pem"), key, 0600))
|
||||
assert.NoError(t, err)
|
||||
|
||||
cliCrt, err := tls.LoadX509KeyPair(filepath.Join(os.TempDir(), "cert.pem"), filepath.Join(os.TempDir(), "key.pem"))
|
||||
assert.NoError(t, err)
|
||||
|
||||
config := &tls.Config{
|
||||
Certificates: []tls.Certificate{cliCrt},
|
||||
}
|
||||
sn1, err := GetCertSnFromConfig(config)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, sn1)
|
||||
|
||||
sn2, err := GetCertSnFromEncode(cert)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, sn2)
|
||||
|
||||
assert.Equal(t, sn1, sn2)
|
||||
}
|
253
lib/cert/client_hello.go
Normal file
253
lib/cert/client_hello.go
Normal file
@@ -0,0 +1,253 @@
|
||||
package cert
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CurveID uint16
|
||||
type SignatureScheme uint16
|
||||
|
||||
const (
|
||||
statusTypeOCSP uint8 = 1
|
||||
extensionServerName uint16 = 0
|
||||
extensionStatusRequest uint16 = 5
|
||||
extensionSupportedCurves uint16 = 10
|
||||
extensionSupportedPoints uint16 = 11
|
||||
extensionSignatureAlgorithms uint16 = 13
|
||||
extensionALPN uint16 = 16
|
||||
extensionSCT uint16 = 18 // https://tools.ietf.org/html/rfc6962#section-6
|
||||
extensionSessionTicket uint16 = 35
|
||||
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
|
||||
extensionRenegotiationInfo uint16 = 0xff01
|
||||
scsvRenegotiation uint16 = 0x00ff
|
||||
)
|
||||
|
||||
type ClientHelloMsg struct {
|
||||
raw []byte
|
||||
vers uint16
|
||||
random []byte
|
||||
sessionId []byte
|
||||
cipherSuites []uint16
|
||||
compressionMethods []uint8
|
||||
nextProtoNeg bool
|
||||
serverName string
|
||||
ocspStapling bool
|
||||
scts bool
|
||||
supportedCurves []CurveID
|
||||
supportedPoints []uint8
|
||||
ticketSupported bool
|
||||
sessionTicket []uint8
|
||||
supportedSignatureAlgorithms []SignatureScheme
|
||||
secureRenegotiation []byte
|
||||
secureRenegotiationSupported bool
|
||||
alpnProtocols []string
|
||||
}
|
||||
|
||||
func (m *ClientHelloMsg) GetServerName() string {
|
||||
return m.serverName
|
||||
}
|
||||
|
||||
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])
|
||||
if m.cipherSuites[i] == scsvRenegotiation {
|
||||
m.secureRenegotiationSupported = true
|
||||
}
|
||||
}
|
||||
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
|
||||
m.supportedSignatureAlgorithms = nil
|
||||
m.alpnProtocols = nil
|
||||
m.scts = false
|
||||
|
||||
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:
|
||||
d := data[:length]
|
||||
if len(d) < 2 {
|
||||
return false
|
||||
}
|
||||
namesLen := int(d[0])<<8 | int(d[1])
|
||||
d = d[2:]
|
||||
if len(d) != namesLen {
|
||||
return false
|
||||
}
|
||||
for len(d) > 0 {
|
||||
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[:nameLen])
|
||||
// An SNI value may not include a
|
||||
// trailing dot. See
|
||||
// https://tools.ietf.org/html/rfc6066#section-3.
|
||||
if strings.HasSuffix(m.serverName, ".") {
|
||||
return false
|
||||
}
|
||||
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:
|
||||
// https://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([]CurveID, numCurves)
|
||||
d := data[2:]
|
||||
for i := 0; i < numCurves; i++ {
|
||||
m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1])
|
||||
d = d[2:]
|
||||
}
|
||||
case extensionSupportedPoints:
|
||||
// https://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:
|
||||
// https://tools.ietf.org/html/rfc5077#section-3.2
|
||||
m.ticketSupported = true
|
||||
m.sessionTicket = data[:length]
|
||||
case extensionSignatureAlgorithms:
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
|
||||
if length < 2 || length&1 != 0 {
|
||||
return false
|
||||
}
|
||||
l := int(data[0])<<8 | int(data[1])
|
||||
if l != length-2 {
|
||||
return false
|
||||
}
|
||||
n := l / 2
|
||||
d := data[2:]
|
||||
m.supportedSignatureAlgorithms = make([]SignatureScheme, n)
|
||||
for i := range m.supportedSignatureAlgorithms {
|
||||
m.supportedSignatureAlgorithms[i] = SignatureScheme(d[0])<<8 | SignatureScheme(d[1])
|
||||
d = d[2:]
|
||||
}
|
||||
case extensionRenegotiationInfo:
|
||||
if length == 0 {
|
||||
return false
|
||||
}
|
||||
d := data[:length]
|
||||
l := int(d[0])
|
||||
d = d[1:]
|
||||
if l != len(d) {
|
||||
return false
|
||||
}
|
||||
|
||||
m.secureRenegotiation = d
|
||||
m.secureRenegotiationSupported = true
|
||||
case extensionALPN:
|
||||
if length < 2 {
|
||||
return false
|
||||
}
|
||||
l := int(data[0])<<8 | int(data[1])
|
||||
if l != length-2 {
|
||||
return false
|
||||
}
|
||||
d := data[2:length]
|
||||
for len(d) != 0 {
|
||||
stringLen := int(d[0])
|
||||
d = d[1:]
|
||||
if stringLen == 0 || stringLen > len(d) {
|
||||
return false
|
||||
}
|
||||
m.alpnProtocols = append(m.alpnProtocols, string(d[:stringLen]))
|
||||
d = d[stringLen:]
|
||||
}
|
||||
case extensionSCT:
|
||||
m.scts = true
|
||||
if length != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
data = data[length:]
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
108
lib/cert/generate.go
Normal file
108
lib/cert/generate.go
Normal file
@@ -0,0 +1,108 @@
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ Generator = (*X509Generator)(nil)
|
||||
|
||||
type Generator interface {
|
||||
CreateRootCa() ([]byte, []byte, error)
|
||||
CreateCert(dnsName string) ([]byte, []byte, error)
|
||||
InitRootCa(rootCa []byte, rootKey []byte) error
|
||||
}
|
||||
|
||||
type X509Generator struct {
|
||||
rootCert *x509.Certificate
|
||||
rootRsaPrivate *rsa.PrivateKey
|
||||
subject pkix.Name
|
||||
}
|
||||
|
||||
func NewX509Generator(subject pkix.Name) *X509Generator {
|
||||
return &X509Generator{
|
||||
subject: subject,
|
||||
}
|
||||
}
|
||||
|
||||
func (cg *X509Generator) InitRootCa(rootCa []byte, rootKey []byte) error {
|
||||
var err error
|
||||
caBlock, _ := pem.Decode(rootCa)
|
||||
cg.rootCert, err = x509.ParseCertificate(caBlock.Bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyBlock, _ := pem.Decode(rootKey)
|
||||
cg.rootRsaPrivate, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cg *X509Generator) CreateCert(dnsName string) ([]byte, []byte, error) {
|
||||
return cg.create(false, dnsName)
|
||||
}
|
||||
|
||||
func (cg *X509Generator) CreateRootCa() ([]byte, []byte, error) {
|
||||
return cg.create(true, "")
|
||||
}
|
||||
|
||||
func (cg *X509Generator) create(isRootCa bool, dnsName string) ([]byte, []byte, error) {
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: cg.subject,
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(3, 0, 0),
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: false,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageDataEncipherment,
|
||||
DNSNames: []string{dnsName},
|
||||
}
|
||||
|
||||
if isRootCa {
|
||||
template.IsCA = true
|
||||
template.KeyUsage |= x509.KeyUsageCertSign
|
||||
}
|
||||
|
||||
priKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var ca []byte
|
||||
if !isRootCa {
|
||||
if cg.rootCert == nil || cg.rootRsaPrivate == nil {
|
||||
return nil, nil, errors.New("root ca is not exist")
|
||||
}
|
||||
ca, err = x509.CreateCertificate(rand.Reader, template, cg.rootCert, &priKey.PublicKey, cg.rootRsaPrivate)
|
||||
} else {
|
||||
ca, err = x509.CreateCertificate(rand.Reader, template, template, &priKey.PublicKey, priKey)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
caPem := &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: ca,
|
||||
}
|
||||
ca = pem.EncodeToMemory(caPem)
|
||||
|
||||
buf := x509.MarshalPKCS1PrivateKey(priKey)
|
||||
keyPem := &pem.Block{
|
||||
Type: "PRIVATE KEY",
|
||||
Bytes: buf,
|
||||
}
|
||||
key := pem.EncodeToMemory(keyPem)
|
||||
return ca, key, nil
|
||||
}
|
62
lib/cert/generate_test.go
Normal file
62
lib/cert/generate_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package cert
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreateCert(t *testing.T) {
|
||||
dnsName := "ehang.io"
|
||||
g := NewX509Generator(pkix.Name{
|
||||
Country: []string{"CN"},
|
||||
Organization: []string{"ehang.io"},
|
||||
OrganizationalUnit: []string{"nps"},
|
||||
Province: []string{"Beijing"},
|
||||
CommonName: "nps",
|
||||
Locality: []string{"Beijing"},
|
||||
})
|
||||
// generate root ca
|
||||
rootCa, rootKey, err := g.CreateRootCa()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = g.InitRootCa(rootCa, rootKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// generate npc cert
|
||||
clientCa, _, err := g.CreateCert(dnsName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// verify npc cert by root cert
|
||||
roots := x509.NewCertPool()
|
||||
ok := roots.AppendCertsFromPEM(rootCa)
|
||||
if !ok {
|
||||
panic("failed to parse root certificate")
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(clientCa)
|
||||
if block == nil {
|
||||
t.Fatal("failed to parse certificate PEM")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
t.Fatal("failed to parse certificate: " + err.Error())
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: roots,
|
||||
DNSName: dnsName,
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
|
||||
if _, err := cert.Verify(opts); err != nil {
|
||||
t.Fatal("failed to verify certificate: " + err.Error())
|
||||
}
|
||||
|
||||
}
|
141
lib/common/addr.go
Normal file
141
lib/common/addr.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var AddrError = errors.New("addr error")
|
||||
|
||||
// SOCKS address types as defined in RFC 1928 section 5.
|
||||
const (
|
||||
AtypIPv4 = 1
|
||||
AtypDomainName = 3
|
||||
AtypIPv6 = 4
|
||||
)
|
||||
|
||||
// MaxAddrLen is the maximum size of SOCKS address in bytes.
|
||||
const MaxAddrLen = 1 + 1 + 255 + 2
|
||||
|
||||
// Addr represents a SOCKS address as defined in RFC 1928 section 5.
|
||||
type Addr []byte
|
||||
|
||||
// String serializes SOCKS address a to string form.
|
||||
func (a Addr) String() string {
|
||||
var host, port string
|
||||
|
||||
switch a[0] { // address type
|
||||
case AtypDomainName:
|
||||
host = string(a[2 : 2+int(a[1])])
|
||||
port = strconv.Itoa((int(a[2+int(a[1])]) << 8) | int(a[2+int(a[1])+1]))
|
||||
case AtypIPv4:
|
||||
host = net.IP(a[1 : 1+net.IPv4len]).String()
|
||||
port = strconv.Itoa((int(a[1+net.IPv4len]) << 8) | int(a[1+net.IPv4len+1]))
|
||||
case AtypIPv6:
|
||||
host = net.IP(a[1 : 1+net.IPv6len]).String()
|
||||
port = strconv.Itoa((int(a[1+net.IPv6len]) << 8) | int(a[1+net.IPv6len+1]))
|
||||
}
|
||||
|
||||
return net.JoinHostPort(host, port)
|
||||
}
|
||||
|
||||
func readAddr(r io.Reader, b []byte) (Addr, error) {
|
||||
if len(b) < MaxAddrLen {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
_, err := io.ReadFull(r, b[:1]) // read 1st byte for address type
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch b[0] {
|
||||
case AtypDomainName:
|
||||
_, err = io.ReadFull(r, b[1:2]) // read 2nd byte for domain length
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = io.ReadFull(r, b[2:2+int(b[1])+2])
|
||||
return b[:1+1+int(b[1])+2], err
|
||||
case AtypIPv4:
|
||||
_, err = io.ReadFull(r, b[1:1+net.IPv4len+2])
|
||||
return b[:1+net.IPv4len+2], err
|
||||
case AtypIPv6:
|
||||
_, err = io.ReadFull(r, b[1:1+net.IPv6len+2])
|
||||
return b[:1+net.IPv6len+2], err
|
||||
}
|
||||
|
||||
return nil, errors.New("addr type not supported")
|
||||
}
|
||||
|
||||
// ReadAddr reads just enough bytes from r to get a valid Addr.
|
||||
func ReadAddr(r io.Reader) (Addr, error) {
|
||||
return readAddr(r, make([]byte, MaxAddrLen))
|
||||
}
|
||||
|
||||
// SplitAddr slices a SOCKS address from beginning of b. Returns nil if failed.
|
||||
func SplitAddr(b []byte) (Addr, error) {
|
||||
addrLen := 1
|
||||
if len(b) < addrLen {
|
||||
return nil, AddrError
|
||||
}
|
||||
|
||||
switch b[0] {
|
||||
case AtypDomainName:
|
||||
if len(b) < 2 {
|
||||
return nil, AddrError
|
||||
}
|
||||
addrLen = 1 + 1 + int(b[1]) + 2
|
||||
case AtypIPv4:
|
||||
addrLen = 1 + net.IPv4len + 2
|
||||
case AtypIPv6:
|
||||
addrLen = 1 + net.IPv6len + 2
|
||||
default:
|
||||
return nil, AddrError
|
||||
|
||||
}
|
||||
|
||||
if len(b) < addrLen {
|
||||
return nil, AddrError
|
||||
}
|
||||
|
||||
return b[:addrLen], nil
|
||||
}
|
||||
|
||||
// ParseAddr parses the address in string s. Returns nil if failed.
|
||||
func ParseAddr(s string) (Addr, error) {
|
||||
var addr Addr
|
||||
host, port, err := net.SplitHostPort(s)
|
||||
if err != nil {
|
||||
return nil, AddrError
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
addr = make([]byte, 1+net.IPv4len+2)
|
||||
addr[0] = AtypIPv4
|
||||
copy(addr[1:], ip4)
|
||||
} else {
|
||||
addr = make([]byte, 1+net.IPv6len+2)
|
||||
addr[0] = AtypIPv6
|
||||
copy(addr[1:], ip)
|
||||
}
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return nil, AddrError
|
||||
}
|
||||
addr = make([]byte, 1+1+len(host)+2)
|
||||
addr[0] = AtypDomainName
|
||||
addr[1] = byte(len(host))
|
||||
copy(addr[2:], host)
|
||||
}
|
||||
|
||||
portnum, err := strconv.ParseUint(port, 10, 16)
|
||||
if err != nil {
|
||||
return nil, AddrError
|
||||
}
|
||||
|
||||
addr[len(addr)-2], addr[len(addr)-1] = byte(portnum>>8), byte(portnum)
|
||||
|
||||
return addr, nil
|
||||
}
|
75
lib/common/utils.go
Normal file
75
lib/common/utils.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CopyBuffer is an implement of io.Copy with buffer pool
|
||||
func CopyBuffer(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) {
|
||||
for {
|
||||
nr, er := src.Read(buf)
|
||||
if nr > 0 {
|
||||
nw, ew := dst.Write(buf[0:nr])
|
||||
if nw < 0 || nr < nw {
|
||||
nw = 0
|
||||
if ew == nil {
|
||||
ew = errors.New("invalid write result")
|
||||
}
|
||||
}
|
||||
written += int64(nw)
|
||||
if ew != nil {
|
||||
err = ew
|
||||
break
|
||||
}
|
||||
if nr != nw {
|
||||
err = errors.New("short write")
|
||||
break
|
||||
}
|
||||
}
|
||||
if er != nil {
|
||||
if er != io.EOF {
|
||||
err = er
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return written, err
|
||||
}
|
||||
|
||||
// HostContains tests whether the string host contained ruleHost
|
||||
func HostContains(ruleHost string, host string) bool {
|
||||
return strings.HasSuffix(host, strings.Replace(ruleHost, "*", "", -1))
|
||||
}
|
||||
|
||||
// WriteLenBytes is used to write length and bytes to writer
|
||||
func WriteLenBytes(w io.Writer, b []byte) (int, error) {
|
||||
err := binary.Write(w, binary.LittleEndian, uint32(len(b)))
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "write len")
|
||||
}
|
||||
n, err := w.Write(b)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "write bytes")
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// ReadLenBytes is used to read bytes from reader
|
||||
func ReadLenBytes(r io.Reader, b []byte) (int, error) {
|
||||
var l int32
|
||||
err := binary.Read(r, binary.LittleEndian, &l)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "read len")
|
||||
}
|
||||
if int(l) > len(b) {
|
||||
return 0, errors.Errorf("data is too long(%d)", l)
|
||||
}
|
||||
n, err := io.ReadAtLeast(r, b, int(l))
|
||||
if err != nil {
|
||||
return n, errors.Wrap(err, "read data error")
|
||||
}
|
||||
return n, nil
|
||||
}
|
119
lib/enet/conn.go
Normal file
119
lib/enet/conn.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package enet
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/pool"
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type Conn interface {
|
||||
net.Conn
|
||||
Reset(int) error
|
||||
Clear()
|
||||
Readable() bool
|
||||
AllBytes() ([]byte, error)
|
||||
SyscallConn() (syscall.RawConn, error)
|
||||
}
|
||||
|
||||
var _ Conn = (*ReaderConn)(nil)
|
||||
|
||||
var bp = pool.NewBufferPool(MaxReadSize)
|
||||
|
||||
const MaxReadSize = 32 * 1024
|
||||
|
||||
// ReaderConn is an implement of reusable data connection
|
||||
type ReaderConn struct {
|
||||
buf []byte
|
||||
nowIndex int
|
||||
hasRead int
|
||||
hasClear bool
|
||||
net.Conn
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// NewReaderConn returns a new ReaderConn
|
||||
func NewReaderConn(conn net.Conn) *ReaderConn {
|
||||
return &ReaderConn{Conn: conn, buf: bp.Get()}
|
||||
}
|
||||
|
||||
// SyscallConn returns a raw network connection
|
||||
func (rc *ReaderConn) SyscallConn() (syscall.RawConn, error) {
|
||||
return rc.Conn.(syscall.Conn).SyscallConn()
|
||||
}
|
||||
|
||||
// Read is an implement of Net.Conn Read function
|
||||
func (rc *ReaderConn) Read(b []byte) (n int, err error) {
|
||||
rc.Lock()
|
||||
defer rc.Unlock()
|
||||
if rc.hasClear || (rc.nowIndex == rc.hasRead && rc.hasRead == MaxReadSize) {
|
||||
if !rc.hasClear {
|
||||
rc.Clear()
|
||||
}
|
||||
return rc.Conn.Read(b)
|
||||
}
|
||||
if rc.hasRead > rc.nowIndex {
|
||||
n = copy(b, rc.buf[rc.nowIndex:rc.hasRead])
|
||||
rc.nowIndex += n
|
||||
return
|
||||
}
|
||||
if rc.hasRead == MaxReadSize {
|
||||
n = copy(b, rc.buf[rc.nowIndex:rc.hasRead])
|
||||
rc.nowIndex += n
|
||||
return
|
||||
}
|
||||
err = rc.readOnce()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n = copy(b, rc.buf[rc.nowIndex:rc.hasRead])
|
||||
rc.nowIndex += n
|
||||
return
|
||||
}
|
||||
|
||||
// readOnce
|
||||
func (rc *ReaderConn) readOnce() error {
|
||||
// int(math.Min(float64(MaxReadSize-rc.hasRead), float64(len(b)-(rc.hasRead-rc.nowIndex))))
|
||||
// read as much as possible to judge whether there is still readable
|
||||
n, err := rc.Conn.Read(rc.buf[rc.nowIndex : rc.hasRead+MaxReadSize-rc.hasRead])
|
||||
rc.hasRead += n
|
||||
return err
|
||||
}
|
||||
|
||||
// Readable return whether there is data in the buffer
|
||||
func (rc *ReaderConn) Readable() bool {
|
||||
return (rc.hasRead - rc.nowIndex) > 0
|
||||
}
|
||||
|
||||
// AllBytes return all data in the buffer
|
||||
func (rc *ReaderConn) AllBytes() ([]byte, error) {
|
||||
rc.Lock()
|
||||
defer rc.Unlock()
|
||||
if rc.hasRead == 0 {
|
||||
if err := rc.readOnce(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if !rc.Readable() {
|
||||
return nil, errors.New("can not read '")
|
||||
}
|
||||
b := rc.buf[rc.nowIndex:rc.hasRead]
|
||||
rc.nowIndex = rc.hasRead
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Reset will reset data index
|
||||
func (rc *ReaderConn) Reset(n int) error {
|
||||
if !rc.hasClear {
|
||||
rc.nowIndex = n
|
||||
return nil
|
||||
}
|
||||
return errors.New("the enet can not reset anymore")
|
||||
}
|
||||
|
||||
// Clear will put buf to pool and can not reuse anymore
|
||||
func (rc *ReaderConn) Clear() {
|
||||
rc.hasClear = true
|
||||
bp.Put(rc.buf)
|
||||
}
|
81
lib/enet/conn_test.go
Normal file
81
lib/enet/conn_test.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package enet
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestReaderConn_Read(t *testing.T) {
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:61254")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b := make([]byte, 33*1024)
|
||||
go func() {
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:61254")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
for i := 0; i < 33*1024; i++ {
|
||||
b[i] = byte(rand.Intn(128))
|
||||
}
|
||||
conn.Write(b)
|
||||
}()
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rConn := NewReaderConn(conn)
|
||||
buf := make([]byte, 1024)
|
||||
nn := 0
|
||||
times := 0
|
||||
for {
|
||||
n, err := rConn.Read(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < 1024; i++ {
|
||||
if b[times*1024+i] != buf[i] {
|
||||
t.Fatal("data error")
|
||||
}
|
||||
}
|
||||
times++
|
||||
nn += n
|
||||
if nn > 30*1024 {
|
||||
break
|
||||
}
|
||||
if times > 100 {
|
||||
t.Fatal("read error")
|
||||
}
|
||||
}
|
||||
|
||||
rConn.Reset(0)
|
||||
nn = 0
|
||||
times = 0
|
||||
for {
|
||||
n, err := rConn.Read(buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := 0; i < 1024; i++ {
|
||||
if b[times*1024+i] != buf[i] {
|
||||
t.Fatal("data error")
|
||||
}
|
||||
}
|
||||
nn += n
|
||||
times++
|
||||
if nn > 32*1024 {
|
||||
break
|
||||
}
|
||||
if times > 100 {
|
||||
t.Fatal("read error")
|
||||
}
|
||||
}
|
||||
if !rConn.hasClear || rConn.hasRead != rConn.nowIndex || rConn.nowIndex != MaxReadSize {
|
||||
t.Fatal("read error")
|
||||
}
|
||||
|
||||
}
|
62
lib/enet/listener.go
Normal file
62
lib/enet/listener.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package enet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var _ net.Listener = (*Listener)(nil)
|
||||
|
||||
// Listener is an implementation of net.Listener
|
||||
type Listener struct {
|
||||
ch chan net.Conn
|
||||
closeCh chan struct{}
|
||||
closed int32
|
||||
nowNum int32
|
||||
addr net.Addr
|
||||
}
|
||||
|
||||
// NewListener returns an initialized Listener
|
||||
func NewListener() *Listener {
|
||||
return &Listener{ch: make(chan net.Conn, 10), closeCh: make(chan struct{})}
|
||||
}
|
||||
|
||||
// SendConn is used to add connection to the listener
|
||||
func (bl *Listener) SendConn(c net.Conn) error {
|
||||
if atomic.LoadInt32(&bl.closed) == 1 {
|
||||
return errors.New("the listener is already closed")
|
||||
}
|
||||
atomic.AddInt32(&bl.nowNum, 1)
|
||||
select {
|
||||
case bl.ch <- c:
|
||||
return nil
|
||||
case <-bl.closeCh:
|
||||
}
|
||||
if atomic.AddInt32(&bl.nowNum, -1) == 0 && atomic.LoadInt32(&bl.closed) == 1 {
|
||||
close(bl.ch)
|
||||
}
|
||||
return errors.New("the listener is already closed")
|
||||
}
|
||||
|
||||
// Accept is used to get connection from the listener
|
||||
func (bl *Listener) Accept() (net.Conn, error) {
|
||||
c := <-bl.ch
|
||||
if c == nil {
|
||||
return nil, errors.New("the listener is already closed")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Close is used to close the listener, it will discard all existing connections
|
||||
func (bl *Listener) Close() error {
|
||||
if atomic.CompareAndSwapInt32(&bl.closed, 0, 1) {
|
||||
close(bl.closeCh)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Addr returns the listener's address'
|
||||
func (bl *Listener) Addr() net.Addr {
|
||||
return bl.addr
|
||||
}
|
161
lib/enet/paket.go
Normal file
161
lib/enet/paket.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package enet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/pool"
|
||||
"github.com/pkg/errors"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
_ net.PacketConn = (*TcpPacketConn)(nil)
|
||||
_ PacketConn = (*ReaderPacketConn)(nil)
|
||||
)
|
||||
|
||||
type PacketConn interface {
|
||||
net.PacketConn
|
||||
SendPacket([]byte, net.Addr) error
|
||||
FirstPacket() ([]byte, net.Addr, error)
|
||||
}
|
||||
|
||||
var udpBp = pool.NewBufferPool(1500)
|
||||
|
||||
// TcpPacketConn is an implement of net.PacketConn by net.Conn
|
||||
type TcpPacketConn struct {
|
||||
udpBp []byte
|
||||
net.Conn
|
||||
}
|
||||
|
||||
// NewTcpPacketConn return a *TcpPacketConn
|
||||
func NewTcpPacketConn(conn net.Conn) *TcpPacketConn {
|
||||
return &TcpPacketConn{Conn: conn}
|
||||
}
|
||||
|
||||
// ReadFrom is a implement of net.PacketConn ReadFrom
|
||||
func (tp *TcpPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
b := udpBp.Get()
|
||||
defer udpBp.Put(b)
|
||||
n, err = common.ReadLenBytes(tp.Conn, b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rAddr, err := common.ReadAddr(bytes.NewReader(b[:n]))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n = copy(p, b[len(rAddr):n])
|
||||
addr, err = net.ResolveUDPAddr("udp", rAddr.String())
|
||||
return
|
||||
}
|
||||
|
||||
// WriteTo is a implement of net.PacketConn WriteTo
|
||||
func (tp *TcpPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
var pAddr common.Addr
|
||||
pAddr, err = common.ParseAddr(addr.String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return common.WriteLenBytes(tp.Conn, append(pAddr, p...))
|
||||
}
|
||||
|
||||
// ReaderPacketConn is an implementation of net.PacketConn
|
||||
type ReaderPacketConn struct {
|
||||
ch chan *packet
|
||||
closeCh chan struct{}
|
||||
closed int32
|
||||
nowNum int32
|
||||
addr net.Addr
|
||||
writePacketConn net.PacketConn
|
||||
readTimer *time.Timer
|
||||
firstPacket []byte
|
||||
}
|
||||
|
||||
type packet struct {
|
||||
b []byte
|
||||
addr net.Addr
|
||||
}
|
||||
|
||||
// NewReaderPacketConn returns an initialized PacketConn
|
||||
func NewReaderPacketConn(writePacketConn net.PacketConn, firstPacket []byte, addr net.Addr) *ReaderPacketConn {
|
||||
return &ReaderPacketConn{
|
||||
ch: make(chan *packet, 10),
|
||||
closeCh: make(chan struct{}),
|
||||
addr: addr,
|
||||
writePacketConn: writePacketConn,
|
||||
readTimer: time.NewTimer(time.Hour * 24 * 3650),
|
||||
firstPacket: firstPacket,
|
||||
}
|
||||
}
|
||||
|
||||
func (pc *ReaderPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
var pt *packet
|
||||
select {
|
||||
case pt = <-pc.ch:
|
||||
case <-pc.readTimer.C:
|
||||
}
|
||||
if pt == nil {
|
||||
return 0, nil, errors.New("the PacketConn is already closed")
|
||||
}
|
||||
copy(p, pt.b)
|
||||
return len(pt.b), pt.addr, nil
|
||||
}
|
||||
|
||||
func (pc *ReaderPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
return pc.writePacketConn.WriteTo(p, addr)
|
||||
}
|
||||
|
||||
// LocalAddr returns the listener's address
|
||||
func (pc *ReaderPacketConn) LocalAddr() net.Addr {
|
||||
return pc.addr
|
||||
}
|
||||
|
||||
func (pc *ReaderPacketConn) SetDeadline(t time.Time) error {
|
||||
pc.readTimer.Reset(t.Sub(time.Now()))
|
||||
return pc.writePacketConn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func (pc *ReaderPacketConn) SetReadDeadline(t time.Time) error {
|
||||
pc.readTimer.Reset(t.Sub(time.Now()))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pc *ReaderPacketConn) SetWriteDeadline(t time.Time) error {
|
||||
return pc.writePacketConn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func (pc *ReaderPacketConn) FirstPacket() ([]byte, net.Addr, error) {
|
||||
if pc.firstPacket == nil || pc.addr == nil {
|
||||
return nil, nil, errors.New("not found first packet")
|
||||
}
|
||||
return pc.firstPacket, pc.addr, nil
|
||||
}
|
||||
|
||||
// SendPacket is used to add connection to the listener
|
||||
func (pc *ReaderPacketConn) SendPacket(b []byte, addr net.Addr) error {
|
||||
if atomic.LoadInt32(&pc.closed) == 1 {
|
||||
return errors.New("the listener is already closed")
|
||||
}
|
||||
atomic.AddInt32(&pc.nowNum, 1)
|
||||
select {
|
||||
case pc.ch <- &packet{b: b, addr: addr}:
|
||||
return nil
|
||||
case <-pc.closeCh:
|
||||
case <-pc.readTimer.C:
|
||||
_ = pc.Close()
|
||||
}
|
||||
if atomic.AddInt32(&pc.nowNum, -1) == 0 && atomic.LoadInt32(&pc.closed) == 1 {
|
||||
close(pc.ch)
|
||||
}
|
||||
return errors.New("the packetConn is already closed")
|
||||
}
|
||||
|
||||
// Close is used to close the listener, it will discard all existing connections
|
||||
func (pc *ReaderPacketConn) Close() error {
|
||||
if atomic.CompareAndSwapInt32(&pc.closed, 0, 1) {
|
||||
close(pc.closeCh)
|
||||
}
|
||||
return nil
|
||||
}
|
78
lib/enet/paket_test.go
Normal file
78
lib/enet/paket_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package enet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTcpPacketConn(t *testing.T) {
|
||||
bs := bytes.Repeat([]byte{1}, 100)
|
||||
targetAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:53")
|
||||
assert.NoError(t, err)
|
||||
|
||||
finish := make(chan struct{}, 0)
|
||||
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
assert.NoError(t, err)
|
||||
go func() {
|
||||
conn, err := ln.Accept()
|
||||
assert.NoError(t, err)
|
||||
b := make([]byte, 1024)
|
||||
n, addr, err := NewTcpPacketConn(conn).ReadFrom(b)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, targetAddr, addr)
|
||||
assert.Equal(t, n, 100)
|
||||
finish <- struct{}{}
|
||||
}()
|
||||
|
||||
conn, err := net.Dial("tcp", ln.Addr().String())
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, err = NewTcpPacketConn(conn).WriteTo(bs, targetAddr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
<-finish
|
||||
}
|
||||
|
||||
func TestPacketConn(t *testing.T) {
|
||||
finish := make(chan struct{}, 0)
|
||||
|
||||
sPacketConn, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
assert.NoError(t, err)
|
||||
|
||||
cPacketConn, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
assert.NoError(t, err)
|
||||
|
||||
bPacketConn := NewReaderPacketConn(sPacketConn, nil, sPacketConn.LocalAddr())
|
||||
|
||||
sendAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:53")
|
||||
assert.NoError(t, err)
|
||||
|
||||
go func() {
|
||||
b := make([]byte, 1024)
|
||||
n, addr, err := bPacketConn.ReadFrom(b)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sendAddr, addr)
|
||||
assert.Equal(t, n, 4)
|
||||
|
||||
_, err = bPacketConn.WriteTo(bytes.Repeat(b[:n], 10), cPacketConn.LocalAddr())
|
||||
assert.NoError(t, err)
|
||||
|
||||
finish <- struct{}{}
|
||||
}()
|
||||
|
||||
err = bPacketConn.SendPacket([]byte{0, 0, 0, 0}, sendAddr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
b := make([]byte, 1024)
|
||||
n, addr, err := cPacketConn.ReadFrom(b)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, n, 40)
|
||||
assert.Equal(t, addr, sPacketConn.LocalAddr())
|
||||
|
||||
<-finish
|
||||
|
||||
}
|
55
lib/enet/s5_packet.go
Normal file
55
lib/enet/s5_packet.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package enet
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/pool"
|
||||
"github.com/pkg/errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
var packetBp = pool.NewBufferPool(1500)
|
||||
|
||||
type S5PacketConn struct {
|
||||
net.PacketConn
|
||||
remoteAddr net.Addr
|
||||
}
|
||||
|
||||
func NewS5PacketConn(pc net.PacketConn, remoteAddr net.Addr) *S5PacketConn {
|
||||
return &S5PacketConn{PacketConn: pc, remoteAddr: remoteAddr}
|
||||
}
|
||||
|
||||
func (s *S5PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
b := packetBp.Get()
|
||||
defer packetBp.Put(b)
|
||||
n, addr, err = s.PacketConn.ReadFrom(b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var targetAddr common.Addr
|
||||
targetAddr, err = common.SplitAddr(b[3:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n = copy(p, b[3+len(targetAddr):n])
|
||||
addr, err = net.ResolveUDPAddr("udp", targetAddr.String())
|
||||
return
|
||||
}
|
||||
|
||||
func (s *S5PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
n = len(p)
|
||||
b := packetBp.Get()
|
||||
defer packetBp.Put(b)
|
||||
var sAddr common.Addr
|
||||
sAddr, err = common.ParseAddr(addr.String())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
copy(b[3:], sAddr)
|
||||
if (3 + len(sAddr) + len(p)) > len(b) {
|
||||
err = errors.Errorf("data too long(%d)", len(p))
|
||||
return
|
||||
}
|
||||
copy(b[3+len(sAddr):], p)
|
||||
_, err = s.PacketConn.WriteTo(b[:3+len(sAddr)+len(p)], s.remoteAddr)
|
||||
return
|
||||
}
|
50
lib/enet/s5_packet_test.go
Normal file
50
lib/enet/s5_packet_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package enet
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/common"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewS5PacketConn(t *testing.T) {
|
||||
serverPc, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
assert.NoError(t, err)
|
||||
localPc, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
assert.NoError(t, err)
|
||||
appAddr, err := net.ResolveUDPAddr("udp", "8.8.8.8:53")
|
||||
assert.NoError(t, err)
|
||||
data := []byte("test")
|
||||
go func() {
|
||||
p := make([]byte, 1500)
|
||||
n, addr, err := serverPc.ReadFrom(p)
|
||||
assert.NoError(t, err)
|
||||
pc := NewReaderPacketConn(serverPc, p[:n], addr)
|
||||
err = pc.SendPacket(p[:n], addr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, addr, err = pc.FirstPacket()
|
||||
assert.NoError(t, err)
|
||||
s5Pc := NewS5PacketConn(pc, addr)
|
||||
n, addr, err = s5Pc.ReadFrom(p)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, data, p[:n])
|
||||
assert.Equal(t, addr.String(), "8.8.8.8:53")
|
||||
_, err = s5Pc.WriteTo(data, appAddr)
|
||||
assert.NoError(t, err)
|
||||
}()
|
||||
b := []byte{0, 0, 0}
|
||||
pAddr, err := common.ParseAddr(appAddr.String())
|
||||
assert.NoError(t, err)
|
||||
b = append(b, pAddr...)
|
||||
b = append(b, data...)
|
||||
_, err = localPc.WriteTo(b, serverPc.LocalAddr())
|
||||
assert.NoError(t, err)
|
||||
p := make([]byte, 1500)
|
||||
n, _, err := localPc.ReadFrom(p)
|
||||
assert.NoError(t, err)
|
||||
respAddr, err := common.SplitAddr(p[3:])
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, respAddr.String(), appAddr.String())
|
||||
assert.Equal(t, p[3+len(respAddr):n], data)
|
||||
}
|
96
lib/lb/algo.go
Normal file
96
lib/lb/algo.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package lb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func GetLbAlgo(algo string) Algo {
|
||||
// switch
|
||||
return NewRoundRobin()
|
||||
}
|
||||
|
||||
type Algo interface {
|
||||
Next() (interface{}, error)
|
||||
Append(i interface{}) error
|
||||
Remove(i interface{}) error
|
||||
Empty() bool
|
||||
}
|
||||
|
||||
// rotation
|
||||
type roundRobin struct {
|
||||
head *server
|
||||
now *server
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type server struct {
|
||||
self interface{}
|
||||
next *server
|
||||
}
|
||||
|
||||
func NewRoundRobin() *roundRobin {
|
||||
return &roundRobin{}
|
||||
}
|
||||
|
||||
func (r *roundRobin) Append(i interface{}) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
if r.head == nil {
|
||||
r.head = &server{self: i}
|
||||
return nil
|
||||
}
|
||||
r.now = r.head
|
||||
for {
|
||||
if r.now.next == nil {
|
||||
r.now.next = &server{self: i}
|
||||
break
|
||||
}
|
||||
r.now = r.now.next
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *roundRobin) Remove(i interface{}) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
o := r.head
|
||||
var last *server
|
||||
for {
|
||||
if o == nil {
|
||||
return errors.New("not round")
|
||||
}
|
||||
if o.self == i {
|
||||
if last == nil {
|
||||
r.head = o.next
|
||||
} else {
|
||||
last.next = o.next
|
||||
}
|
||||
r.now = r.head
|
||||
return nil
|
||||
}
|
||||
last = o
|
||||
o = o.next
|
||||
}
|
||||
}
|
||||
|
||||
func (r *roundRobin) Next() (interface{}, error) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
if r.head == nil {
|
||||
return nil, errors.New("not found component")
|
||||
}
|
||||
if r.now == nil {
|
||||
r.now = r.head
|
||||
}
|
||||
i := r.now
|
||||
r.now = r.now.next
|
||||
return i.self, nil
|
||||
}
|
||||
|
||||
func (r *roundRobin) Empty() bool {
|
||||
if r.head != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
59
lib/lb/lb.go
Normal file
59
lib/lb/lb.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package lb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func NewLoadBalancer() *LoadBalancer {
|
||||
return &LoadBalancer{
|
||||
instances: make(map[string]Algo, 0),
|
||||
}
|
||||
}
|
||||
|
||||
type LoadBalancer struct {
|
||||
instances map[string]Algo
|
||||
Algo string
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (lb *LoadBalancer) SetClient(id string, instance interface{}) error {
|
||||
lb.Lock()
|
||||
defer lb.Unlock()
|
||||
var l Algo
|
||||
var ok bool
|
||||
if l, ok = lb.instances[id]; !ok {
|
||||
l = GetLbAlgo(lb.Algo)
|
||||
lb.instances[id] = l
|
||||
}
|
||||
return l.Append(instance)
|
||||
}
|
||||
|
||||
func (lb *LoadBalancer) RemoveClient(id string, instance interface{}) error {
|
||||
lb.Lock()
|
||||
defer lb.Unlock()
|
||||
var l Algo
|
||||
var ok bool
|
||||
if l, ok = lb.instances[id]; !ok {
|
||||
return errors.New("not found Client")
|
||||
}
|
||||
err := l.Remove(instance)
|
||||
if l.Empty() {
|
||||
delete(lb.instances, id)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (lb *LoadBalancer) GetClient(id string) (interface{}, error) {
|
||||
lb.Lock()
|
||||
l, ok := lb.instances[id]
|
||||
lb.Unlock()
|
||||
if !ok {
|
||||
return nil, errors.New("client can not found")
|
||||
}
|
||||
i, err := l.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return i, nil
|
||||
}
|
49
lib/lb/lb_test.go
Normal file
49
lib/lb/lb_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package lb
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type test struct {
|
||||
id int
|
||||
}
|
||||
|
||||
func TestLb(t *testing.T) {
|
||||
lb := NewLoadBalancer()
|
||||
|
||||
for i := 1; i <= 100; i++ {
|
||||
err := lb.SetClient("test", &test{id: i})
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
m := make(map[int]bool)
|
||||
|
||||
for i := 1; i <= 100; i++ {
|
||||
tt, err := lb.GetClient("test")
|
||||
assert.NoError(t, err)
|
||||
if _, ok := m[tt.(*test).id]; ok {
|
||||
t.Fail()
|
||||
}
|
||||
m[tt.(*test).id] = true
|
||||
}
|
||||
|
||||
for i := 1; i <= 50; i++ {
|
||||
tt, err := lb.GetClient("test")
|
||||
assert.NoError(t, err)
|
||||
err = lb.RemoveClient("test", tt)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
m = make(map[int]bool)
|
||||
for i := 1; i <= 50; i++ {
|
||||
tt, err := lb.GetClient("test")
|
||||
assert.NoError(t, err)
|
||||
if _, ok := m[tt.(*test).id]; ok {
|
||||
t.Fail()
|
||||
}
|
||||
m[tt.(*test).id] = true
|
||||
}
|
||||
|
||||
|
||||
}
|
93
lib/logger/logger.go
Normal file
93
lib/logger/logger.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
var log *zap.Logger
|
||||
var atomicLevel zap.AtomicLevel
|
||||
|
||||
func Debug(msg string, fields ...zap.Field) {
|
||||
log.Debug(msg, fields...)
|
||||
}
|
||||
|
||||
func Info(msg string, fields ...zap.Field) {
|
||||
log.Info(msg, fields...)
|
||||
}
|
||||
|
||||
func Warn(msg string, fields ...zap.Field) {
|
||||
log.Warn(msg, fields...)
|
||||
}
|
||||
|
||||
func Error(msg string, fields ...zap.Field) {
|
||||
log.Error(msg, fields...)
|
||||
}
|
||||
|
||||
func DPanic(msg string, fields ...zap.Field) {
|
||||
log.DPanic(msg, fields...)
|
||||
}
|
||||
|
||||
func Panic(msg string, fields ...zap.Field) {
|
||||
log.Panic(msg, fields...)
|
||||
}
|
||||
|
||||
func Fatal(msg string, fields ...zap.Field) {
|
||||
log.Fatal(msg, fields...)
|
||||
}
|
||||
|
||||
func Sync() {
|
||||
log.Sync()
|
||||
}
|
||||
|
||||
func SetLogLevel(level int) {
|
||||
atomicLevel.SetLevel(zapcore.Level(level))
|
||||
}
|
||||
|
||||
func init() {
|
||||
hook := lumberjack.Logger{
|
||||
Filename: filepath.Join(os.TempDir(), "nps.log"),
|
||||
MaxSize: 128,
|
||||
MaxBackups: 30,
|
||||
MaxAge: 7,
|
||||
Compress: true,
|
||||
}
|
||||
|
||||
encoderConfig := zap.NewDevelopmentEncoderConfig()
|
||||
encoderConfig.TimeKey = "log_time"
|
||||
encoderConfig.LevelKey = "level"
|
||||
encoderConfig.NameKey = "logger"
|
||||
encoderConfig.CallerKey = "caller"
|
||||
encoderConfig.MessageKey = "msg"
|
||||
encoderConfig.StacktraceKey = "StacktraceKey"
|
||||
encoderConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
|
||||
enc.AppendString(t.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
consoleConfig := encoderConfig
|
||||
consoleConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||
|
||||
atomicLevel = zap.NewAtomicLevel()
|
||||
// init info level
|
||||
atomicLevel.SetLevel(zapcore.Level(-1))
|
||||
|
||||
core := zapcore.NewTee(
|
||||
zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.NewMultiWriteSyncer(zapcore.AddSync(&hook)), atomicLevel),
|
||||
zapcore.NewCore(zapcore.NewConsoleEncoder(consoleConfig), zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout)), atomicLevel),
|
||||
)
|
||||
|
||||
caller := zap.AddCaller()
|
||||
development := zap.Development()
|
||||
|
||||
log = zap.New(core, caller, development, zap.AddCallerSkip(1), zap.AddStacktrace(zap.ErrorLevel))
|
||||
defer log.Sync()
|
||||
undo := zap.ReplaceGlobals(log)
|
||||
defer undo()
|
||||
logLevelSignal()
|
||||
|
||||
}
|
38
lib/logger/logger_others.go
Normal file
38
lib/logger/logger_others.go
Normal file
@@ -0,0 +1,38 @@
|
||||
// +build !windows
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go.uber.org/zap/zapcore"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func logLevelSignal() {
|
||||
c := make(chan os.Signal)
|
||||
signal.Notify(c, syscall.SIGUSR1, syscall.SIGUSR2)
|
||||
fmt.Println("notify receive signal")
|
||||
|
||||
go func() {
|
||||
for s := range c {
|
||||
fmt.Println("receive signal ", s.String())
|
||||
switch s {
|
||||
case syscall.SIGUSR1:
|
||||
cur := atomicLevel.Level()
|
||||
if (cur - 1) >= zapcore.DebugLevel {
|
||||
atomicLevel.SetLevel(zapcore.Level(cur - 1))
|
||||
}
|
||||
case syscall.SIGUSR2:
|
||||
cur := atomicLevel.Level()
|
||||
if (cur + 1) <= zapcore.FatalLevel {
|
||||
atomicLevel.SetLevel(zapcore.Level(cur + 1))
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
fmt.Println("debug level change to ", atomicLevel.String())
|
||||
}
|
||||
}()
|
||||
}
|
7
lib/logger/logger_windows.go
Normal file
7
lib/logger/logger_windows.go
Normal file
@@ -0,0 +1,7 @@
|
||||
// +build windows
|
||||
|
||||
package logger
|
||||
|
||||
func logLevelSignal() {
|
||||
|
||||
}
|
736
lib/pb/bridge.pb.go
Normal file
736
lib/pb/bridge.pb.go
Normal file
@@ -0,0 +1,736 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.27.1
|
||||
// protoc v3.19.1
|
||||
// source: bridge.proto
|
||||
|
||||
package pb
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ConnType int32
|
||||
|
||||
const (
|
||||
ConnType_tcp ConnType = 0
|
||||
ConnType_udp ConnType = 1
|
||||
ConnType_unix ConnType = 2
|
||||
)
|
||||
|
||||
// Enum value maps for ConnType.
|
||||
var (
|
||||
ConnType_name = map[int32]string{
|
||||
0: "tcp",
|
||||
1: "udp",
|
||||
2: "unix",
|
||||
}
|
||||
ConnType_value = map[string]int32{
|
||||
"tcp": 0,
|
||||
"udp": 1,
|
||||
"unix": 2,
|
||||
}
|
||||
)
|
||||
|
||||
func (x ConnType) Enum() *ConnType {
|
||||
p := new(ConnType)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x ConnType) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (ConnType) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_bridge_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (ConnType) Type() protoreflect.EnumType {
|
||||
return &file_bridge_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x ConnType) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ConnType.Descriptor instead.
|
||||
func (ConnType) EnumDescriptor() ([]byte, []int) {
|
||||
return file_bridge_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
type ConnRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id string `protobuf:"bytes,1,opt,name=Id,proto3" json:"Id,omitempty"`
|
||||
// Types that are assignable to ConnType:
|
||||
// *ConnRequest_AppInfo
|
||||
// *ConnRequest_NpcInfo
|
||||
// *ConnRequest_SecretInfo
|
||||
ConnType isConnRequest_ConnType `protobuf_oneof:"connType"`
|
||||
}
|
||||
|
||||
func (x *ConnRequest) Reset() {
|
||||
*x = ConnRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_bridge_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ConnRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ConnRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ConnRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_bridge_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ConnRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ConnRequest) Descriptor() ([]byte, []int) {
|
||||
return file_bridge_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *ConnRequest) GetId() string {
|
||||
if x != nil {
|
||||
return x.Id
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ConnRequest) GetConnType() isConnRequest_ConnType {
|
||||
if m != nil {
|
||||
return m.ConnType
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ConnRequest) GetAppInfo() *AppInfo {
|
||||
if x, ok := x.GetConnType().(*ConnRequest_AppInfo); ok {
|
||||
return x.AppInfo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ConnRequest) GetNpcInfo() *NpcInfo {
|
||||
if x, ok := x.GetConnType().(*ConnRequest_NpcInfo); ok {
|
||||
return x.NpcInfo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ConnRequest) GetSecretInfo() *SecretInfo {
|
||||
if x, ok := x.GetConnType().(*ConnRequest_SecretInfo); ok {
|
||||
return x.SecretInfo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isConnRequest_ConnType interface {
|
||||
isConnRequest_ConnType()
|
||||
}
|
||||
|
||||
type ConnRequest_AppInfo struct {
|
||||
AppInfo *AppInfo `protobuf:"bytes,2,opt,name=app_info,json=appInfo,proto3,oneof"`
|
||||
}
|
||||
|
||||
type ConnRequest_NpcInfo struct {
|
||||
NpcInfo *NpcInfo `protobuf:"bytes,3,opt,name=npc_info,json=npcInfo,proto3,oneof"`
|
||||
}
|
||||
|
||||
type ConnRequest_SecretInfo struct {
|
||||
SecretInfo *SecretInfo `protobuf:"bytes,4,opt,name=secret_info,json=secretInfo,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*ConnRequest_AppInfo) isConnRequest_ConnType() {}
|
||||
|
||||
func (*ConnRequest_NpcInfo) isConnRequest_ConnType() {}
|
||||
|
||||
func (*ConnRequest_SecretInfo) isConnRequest_ConnType() {}
|
||||
|
||||
type ClientRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
// Types that are assignable to ConnType:
|
||||
// *ClientRequest_AppInfo
|
||||
// *ClientRequest_Ping
|
||||
ConnType isClientRequest_ConnType `protobuf_oneof:"connType"`
|
||||
}
|
||||
|
||||
func (x *ClientRequest) Reset() {
|
||||
*x = ClientRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_bridge_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ClientRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ClientRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ClientRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_bridge_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ClientRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ClientRequest) Descriptor() ([]byte, []int) {
|
||||
return file_bridge_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (m *ClientRequest) GetConnType() isClientRequest_ConnType {
|
||||
if m != nil {
|
||||
return m.ConnType
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientRequest) GetAppInfo() *AppInfo {
|
||||
if x, ok := x.GetConnType().(*ClientRequest_AppInfo); ok {
|
||||
return x.AppInfo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ClientRequest) GetPing() *Ping {
|
||||
if x, ok := x.GetConnType().(*ClientRequest_Ping); ok {
|
||||
return x.Ping
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type isClientRequest_ConnType interface {
|
||||
isClientRequest_ConnType()
|
||||
}
|
||||
|
||||
type ClientRequest_AppInfo struct {
|
||||
AppInfo *AppInfo `protobuf:"bytes,1,opt,name=app_info,json=appInfo,proto3,oneof"`
|
||||
}
|
||||
|
||||
type ClientRequest_Ping struct {
|
||||
Ping *Ping `protobuf:"bytes,2,opt,name=ping,proto3,oneof"`
|
||||
}
|
||||
|
||||
func (*ClientRequest_AppInfo) isClientRequest_ConnType() {}
|
||||
|
||||
func (*ClientRequest_Ping) isClientRequest_ConnType() {}
|
||||
|
||||
type AppInfo struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
AppAddr string `protobuf:"bytes,1,opt,name=app_addr,json=appAddr,proto3" json:"app_addr,omitempty"`
|
||||
NpcId string `protobuf:"bytes,2,opt,name=npc_id,json=npcId,proto3" json:"npc_id,omitempty"`
|
||||
ConnType ConnType `protobuf:"varint,3,opt,name=conn_type,json=connType,proto3,enum=ConnType" json:"conn_type,omitempty"`
|
||||
}
|
||||
|
||||
func (x *AppInfo) Reset() {
|
||||
*x = AppInfo{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_bridge_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *AppInfo) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AppInfo) ProtoMessage() {}
|
||||
|
||||
func (x *AppInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_bridge_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AppInfo.ProtoReflect.Descriptor instead.
|
||||
func (*AppInfo) Descriptor() ([]byte, []int) {
|
||||
return file_bridge_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *AppInfo) GetAppAddr() string {
|
||||
if x != nil {
|
||||
return x.AppAddr
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AppInfo) GetNpcId() string {
|
||||
if x != nil {
|
||||
return x.NpcId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AppInfo) GetConnType() ConnType {
|
||||
if x != nil {
|
||||
return x.ConnType
|
||||
}
|
||||
return ConnType_tcp
|
||||
}
|
||||
|
||||
type SecretInfo struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
IsUdpConn bool `protobuf:"varint,1,opt,name=is_udp_conn,json=isUdpConn,proto3" json:"is_udp_conn,omitempty"`
|
||||
AppAddr string `protobuf:"bytes,2,opt,name=app_addr,json=appAddr,proto3" json:"app_addr,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SecretInfo) Reset() {
|
||||
*x = SecretInfo{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_bridge_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SecretInfo) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SecretInfo) ProtoMessage() {}
|
||||
|
||||
func (x *SecretInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_bridge_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SecretInfo.ProtoReflect.Descriptor instead.
|
||||
func (*SecretInfo) Descriptor() ([]byte, []int) {
|
||||
return file_bridge_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *SecretInfo) GetIsUdpConn() bool {
|
||||
if x != nil {
|
||||
return x.IsUdpConn
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *SecretInfo) GetAppAddr() string {
|
||||
if x != nil {
|
||||
return x.AppAddr
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type NpcInfo struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
TunnelId string `protobuf:"bytes,2,opt,name=tunnel_id,json=tunnelId,proto3" json:"tunnel_id,omitempty"`
|
||||
IsControlTunnel bool `protobuf:"varint,3,opt,name=is_control_tunnel,json=isControlTunnel,proto3" json:"is_control_tunnel,omitempty"`
|
||||
}
|
||||
|
||||
func (x *NpcInfo) Reset() {
|
||||
*x = NpcInfo{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_bridge_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *NpcInfo) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*NpcInfo) ProtoMessage() {}
|
||||
|
||||
func (x *NpcInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_bridge_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use NpcInfo.ProtoReflect.Descriptor instead.
|
||||
func (*NpcInfo) Descriptor() ([]byte, []int) {
|
||||
return file_bridge_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *NpcInfo) GetTunnelId() string {
|
||||
if x != nil {
|
||||
return x.TunnelId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *NpcInfo) GetIsControlTunnel() bool {
|
||||
if x != nil {
|
||||
return x.IsControlTunnel
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type NpcResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
|
||||
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
|
||||
}
|
||||
|
||||
func (x *NpcResponse) Reset() {
|
||||
*x = NpcResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_bridge_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *NpcResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*NpcResponse) ProtoMessage() {}
|
||||
|
||||
func (x *NpcResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_bridge_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use NpcResponse.ProtoReflect.Descriptor instead.
|
||||
func (*NpcResponse) Descriptor() ([]byte, []int) {
|
||||
return file_bridge_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *NpcResponse) GetSuccess() bool {
|
||||
if x != nil {
|
||||
return x.Success
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *NpcResponse) GetMessage() string {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Ping struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Now string `protobuf:"bytes,1,opt,name=now,proto3" json:"now,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Ping) Reset() {
|
||||
*x = Ping{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_bridge_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Ping) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Ping) ProtoMessage() {}
|
||||
|
||||
func (x *Ping) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_bridge_proto_msgTypes[6]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Ping.ProtoReflect.Descriptor instead.
|
||||
func (*Ping) Descriptor() ([]byte, []int) {
|
||||
return file_bridge_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *Ping) GetNow() string {
|
||||
if x != nil {
|
||||
return x.Now
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_bridge_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_bridge_proto_rawDesc = []byte{
|
||||
0x0a, 0x0c, 0x62, 0x72, 0x69, 0x64, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa7,
|
||||
0x01, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e,
|
||||
0x0a, 0x02, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x64, 0x12, 0x25,
|
||||
0x0a, 0x08, 0x61, 0x70, 0x70, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
|
||||
0x32, 0x08, 0x2e, 0x41, 0x70, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x07, 0x61, 0x70,
|
||||
0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x25, 0x0a, 0x08, 0x6e, 0x70, 0x63, 0x5f, 0x69, 0x6e, 0x66,
|
||||
0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x4e, 0x70, 0x63, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x48, 0x00, 0x52, 0x07, 0x6e, 0x70, 0x63, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x0b,
|
||||
0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||
0x0b, 0x32, 0x0b, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00,
|
||||
0x52, 0x0a, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x0a, 0x0a, 0x08,
|
||||
0x63, 0x6f, 0x6e, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x22, 0x5f, 0x0a, 0x0d, 0x43, 0x6c, 0x69, 0x65,
|
||||
0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x08, 0x61, 0x70, 0x70,
|
||||
0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x41, 0x70,
|
||||
0x70, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x07, 0x61, 0x70, 0x70, 0x49, 0x6e, 0x66, 0x6f,
|
||||
0x12, 0x1b, 0x0a, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x05,
|
||||
0x2e, 0x70, 0x69, 0x6e, 0x67, 0x48, 0x00, 0x52, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x42, 0x0a, 0x0a,
|
||||
0x08, 0x63, 0x6f, 0x6e, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x22, 0x64, 0x0a, 0x07, 0x41, 0x70, 0x70,
|
||||
0x49, 0x6e, 0x66, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x70, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x70, 0x70, 0x41, 0x64, 0x64, 0x72, 0x12,
|
||||
0x15, 0x0a, 0x06, 0x6e, 0x70, 0x63, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x05, 0x6e, 0x70, 0x63, 0x49, 0x64, 0x12, 0x27, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x5f, 0x74,
|
||||
0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x63, 0x6f, 0x6e, 0x6e,
|
||||
0x5f, 0x74, 0x79, 0x70, 0x65, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x22,
|
||||
0x47, 0x0a, 0x0a, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1e, 0x0a,
|
||||
0x0b, 0x69, 0x73, 0x5f, 0x75, 0x64, 0x70, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x55, 0x64, 0x70, 0x43, 0x6f, 0x6e, 0x6e, 0x12, 0x19, 0x0a,
|
||||
0x08, 0x61, 0x70, 0x70, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x07, 0x61, 0x70, 0x70, 0x41, 0x64, 0x64, 0x72, 0x22, 0x52, 0x0a, 0x07, 0x4e, 0x70, 0x63, 0x49,
|
||||
0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x5f, 0x69, 0x64,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x49, 0x64,
|
||||
0x12, 0x2a, 0x0a, 0x11, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x5f, 0x74,
|
||||
0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x69, 0x73, 0x43,
|
||||
0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x22, 0x41, 0x0a, 0x0b,
|
||||
0x4e, 0x70, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73,
|
||||
0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75,
|
||||
0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
|
||||
0x18, 0x0a, 0x04, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x6e, 0x6f, 0x77, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6e, 0x6f, 0x77, 0x2a, 0x27, 0x0a, 0x09, 0x63, 0x6f, 0x6e,
|
||||
0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x74, 0x63, 0x70, 0x10, 0x00, 0x12,
|
||||
0x07, 0x0a, 0x03, 0x75, 0x64, 0x70, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x75, 0x6e, 0x69, 0x78,
|
||||
0x10, 0x02, 0x42, 0x15, 0x5a, 0x13, 0x65, 0x68, 0x61, 0x6e, 0x67, 0x2e, 0x69, 0x6f, 0x2f, 0x6e,
|
||||
0x70, 0x73, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_bridge_proto_rawDescOnce sync.Once
|
||||
file_bridge_proto_rawDescData = file_bridge_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_bridge_proto_rawDescGZIP() []byte {
|
||||
file_bridge_proto_rawDescOnce.Do(func() {
|
||||
file_bridge_proto_rawDescData = protoimpl.X.CompressGZIP(file_bridge_proto_rawDescData)
|
||||
})
|
||||
return file_bridge_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_bridge_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_bridge_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
|
||||
var file_bridge_proto_goTypes = []interface{}{
|
||||
(ConnType)(0), // 0: conn_type
|
||||
(*ConnRequest)(nil), // 1: ConnRequest
|
||||
(*ClientRequest)(nil), // 2: ClientRequest
|
||||
(*AppInfo)(nil), // 3: AppInfo
|
||||
(*SecretInfo)(nil), // 4: SecretInfo
|
||||
(*NpcInfo)(nil), // 5: NpcInfo
|
||||
(*NpcResponse)(nil), // 6: NpcResponse
|
||||
(*Ping)(nil), // 7: ping
|
||||
}
|
||||
var file_bridge_proto_depIdxs = []int32{
|
||||
3, // 0: ConnRequest.app_info:type_name -> AppInfo
|
||||
5, // 1: ConnRequest.npc_info:type_name -> NpcInfo
|
||||
4, // 2: ConnRequest.secret_info:type_name -> SecretInfo
|
||||
3, // 3: ClientRequest.app_info:type_name -> AppInfo
|
||||
7, // 4: ClientRequest.ping:type_name -> ping
|
||||
0, // 5: AppInfo.conn_type:type_name -> conn_type
|
||||
6, // [6:6] is the sub-list for method output_type
|
||||
6, // [6:6] is the sub-list for method input_type
|
||||
6, // [6:6] is the sub-list for extension type_name
|
||||
6, // [6:6] is the sub-list for extension extendee
|
||||
0, // [0:6] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_bridge_proto_init() }
|
||||
func file_bridge_proto_init() {
|
||||
if File_bridge_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_bridge_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ConnRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_bridge_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ClientRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_bridge_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*AppInfo); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_bridge_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SecretInfo); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_bridge_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*NpcInfo); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_bridge_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*NpcResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_bridge_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Ping); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_bridge_proto_msgTypes[0].OneofWrappers = []interface{}{
|
||||
(*ConnRequest_AppInfo)(nil),
|
||||
(*ConnRequest_NpcInfo)(nil),
|
||||
(*ConnRequest_SecretInfo)(nil),
|
||||
}
|
||||
file_bridge_proto_msgTypes[1].OneofWrappers = []interface{}{
|
||||
(*ClientRequest_AppInfo)(nil),
|
||||
(*ClientRequest_Ping)(nil),
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_bridge_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 7,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_bridge_proto_goTypes,
|
||||
DependencyIndexes: file_bridge_proto_depIdxs,
|
||||
EnumInfos: file_bridge_proto_enumTypes,
|
||||
MessageInfos: file_bridge_proto_msgTypes,
|
||||
}.Build()
|
||||
File_bridge_proto = out.File
|
||||
file_bridge_proto_rawDesc = nil
|
||||
file_bridge_proto_goTypes = nil
|
||||
file_bridge_proto_depIdxs = nil
|
||||
}
|
51
lib/pb/bridge.proto
Normal file
51
lib/pb/bridge.proto
Normal file
@@ -0,0 +1,51 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = "ehang.io/nps/lib/pb";
|
||||
|
||||
|
||||
message ConnRequest {
|
||||
string Id = 1;
|
||||
oneof connType {
|
||||
AppInfo app_info = 2;
|
||||
NpcInfo npc_info = 3;
|
||||
SecretInfo secret_info = 4;
|
||||
}
|
||||
}
|
||||
|
||||
message ClientRequest {
|
||||
oneof connType {
|
||||
AppInfo app_info = 1;
|
||||
ping ping = 2;
|
||||
}
|
||||
}
|
||||
|
||||
enum conn_type{
|
||||
tcp = 0;
|
||||
udp = 1 ;
|
||||
unix = 2;
|
||||
}
|
||||
|
||||
message AppInfo {
|
||||
string app_addr = 1;
|
||||
string npc_id = 2;
|
||||
conn_type conn_type = 3;
|
||||
}
|
||||
|
||||
message SecretInfo {
|
||||
bool is_udp_conn = 1;
|
||||
string app_addr = 2;
|
||||
}
|
||||
|
||||
message NpcInfo {
|
||||
string tunnel_id = 2;
|
||||
bool is_control_tunnel = 3;
|
||||
}
|
||||
|
||||
message NpcResponse {
|
||||
bool success = 1;
|
||||
string message = 2;
|
||||
}
|
||||
|
||||
message ping{
|
||||
string now = 1;
|
||||
}
|
29
lib/pb/pb.go
Normal file
29
lib/pb/pb.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package pb
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/common"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// WriteMessage is used to write a message to writer
|
||||
func WriteMessage(w io.Writer, message proto.Message) (int, error) {
|
||||
b, err := proto.Marshal(message)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "proto Marshal")
|
||||
}
|
||||
n, err := common.WriteLenBytes(w, b)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// ReadMessage is used to read a message from reader
|
||||
func ReadMessage(r io.Reader, message proto.Message) (int, error) {
|
||||
message.Reset()
|
||||
b := make([]byte, 4096)
|
||||
n, err := common.ReadLenBytes(r, b)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "read proto message")
|
||||
}
|
||||
return n, proto.Unmarshal(b[:n], message)
|
||||
}
|
25
lib/pb/pb_test.go
Normal file
25
lib/pb/pb_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package pb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
app := &AppInfo{
|
||||
ConnType: ConnType_udp,
|
||||
AppAddr: "127.0.0.1:8080",
|
||||
}
|
||||
var buf []byte
|
||||
b := bytes.NewBuffer(buf)
|
||||
|
||||
_, err := WriteMessage(b, app)
|
||||
assert.NoError(t, err)
|
||||
|
||||
appRecv := &AppInfo{}
|
||||
_, err = ReadMessage(b, appRecv)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, app.AppAddr, appRecv.AppAddr)
|
||||
assert.Equal(t, app.ConnType, appRecv.ConnType)
|
||||
}
|
76
lib/pool/goroutine.go
Normal file
76
lib/pool/goroutine.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package pool
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/common"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var connBp = NewBufferPool(MaxReadSize)
|
||||
var packetBp = NewBufferPool(1500)
|
||||
|
||||
const MaxReadSize = 32 * 1024
|
||||
|
||||
var CopyConnGoroutinePool *ants.PoolWithFunc
|
||||
var CopyPacketGoroutinePool *ants.PoolWithFunc
|
||||
|
||||
type CopyConnGpParams struct {
|
||||
Writer io.Writer
|
||||
Reader io.Reader
|
||||
Wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
type CopyPacketGpParams struct {
|
||||
RPacket net.PacketConn
|
||||
WPacket net.PacketConn
|
||||
Wg *sync.WaitGroup
|
||||
}
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
CopyConnGoroutinePool, err = ants.NewPoolWithFunc(1000000, func(i interface{}) {
|
||||
gpp, ok := i.(*CopyConnGpParams)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
buf := connBp.Get()
|
||||
_, _ = common.CopyBuffer(gpp.Writer, gpp.Reader, buf)
|
||||
connBp.Put(buf)
|
||||
gpp.Wg.Done()
|
||||
if v, ok := gpp.Reader.(*net.TCPConn); ok {
|
||||
_ = v.CloseWrite()
|
||||
}
|
||||
if v, ok := gpp.Writer.(*net.TCPConn); ok {
|
||||
_ = v.CloseRead()
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
CopyPacketGoroutinePool, err = ants.NewPoolWithFunc(1000000, func(i interface{}) {
|
||||
cpp, ok := i.(*CopyPacketGpParams)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
buf := connBp.Get()
|
||||
for {
|
||||
n, addr, err := cpp.RPacket.ReadFrom(buf)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
_, err = cpp.WPacket.WriteTo(buf[:n], addr)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
connBp.Put(buf)
|
||||
_ = cpp.RPacket.Close()
|
||||
_ = cpp.WPacket.Close()
|
||||
cpp.Wg.Done()
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
32
lib/pool/pool.go
Normal file
32
lib/pool/pool.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package pool
|
||||
|
||||
import "sync"
|
||||
|
||||
type BufferPool struct {
|
||||
pool sync.Pool
|
||||
poolSize int
|
||||
}
|
||||
|
||||
func NewBufferPool(poolSize int) *BufferPool {
|
||||
bp := &BufferPool{}
|
||||
bp.pool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, poolSize, poolSize)
|
||||
},
|
||||
}
|
||||
bp.poolSize = poolSize
|
||||
return bp
|
||||
}
|
||||
|
||||
func (bp *BufferPool) Get() []byte {
|
||||
buf := bp.pool.Get().([]byte)
|
||||
return buf[:bp.poolSize] // just like make a new slice, but data may not be 0
|
||||
}
|
||||
|
||||
func (bp *BufferPool) Put(x []byte) {
|
||||
if len(x) == bp.poolSize {
|
||||
bp.pool.Put(x)
|
||||
} else {
|
||||
x = nil // buf is not full, not allowed, New method returns a full buf
|
||||
}
|
||||
}
|
111
lib/rate/rate.go
Normal file
111
lib/rate/rate.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package rate
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Rate is an implementation of the token bucket added regularly
|
||||
type Rate struct {
|
||||
bucketSize int64
|
||||
bucketSurplusSize int64
|
||||
bucketAddSize int64
|
||||
stopChan chan bool
|
||||
nowRate int64
|
||||
cond *sync.Cond
|
||||
hasStop bool
|
||||
hasStart bool
|
||||
}
|
||||
|
||||
// NewRate return token bucket with specified rate
|
||||
func NewRate(addSize int64) *Rate {
|
||||
r := &Rate{
|
||||
bucketSize: addSize * 2,
|
||||
bucketSurplusSize: 0,
|
||||
bucketAddSize: addSize,
|
||||
stopChan: make(chan bool),
|
||||
cond: sync.NewCond(new(sync.Mutex)),
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Start is used to add token regularly
|
||||
func (r *Rate) Start() {
|
||||
if !r.hasStart {
|
||||
r.hasStart = true
|
||||
go r.session()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Rate) add(size int64) {
|
||||
if res := r.bucketSize - r.bucketSurplusSize; res < r.bucketAddSize {
|
||||
atomic.AddInt64(&r.bucketSurplusSize, res)
|
||||
return
|
||||
}
|
||||
atomic.AddInt64(&r.bucketSurplusSize, size)
|
||||
}
|
||||
|
||||
// Write is called when add token to bucket
|
||||
func (r *Rate) Write(size int64) {
|
||||
r.add(size)
|
||||
}
|
||||
|
||||
// Stop is called when not use the rate bucket
|
||||
func (r *Rate) Stop() {
|
||||
if r.hasStart {
|
||||
r.stopChan <- true
|
||||
r.hasStop = true
|
||||
r.cond.Broadcast()
|
||||
}
|
||||
}
|
||||
|
||||
// Get is called when get token from bucket
|
||||
func (r *Rate) Get(size int64) error {
|
||||
if r.hasStop {
|
||||
return errors.New("the rate has closed")
|
||||
}
|
||||
if r.bucketSurplusSize >= size {
|
||||
atomic.AddInt64(&r.bucketSurplusSize, -size)
|
||||
return nil
|
||||
}
|
||||
for {
|
||||
r.cond.L.Lock()
|
||||
r.cond.Wait()
|
||||
if r.bucketSurplusSize >= size {
|
||||
r.cond.L.Unlock()
|
||||
atomic.AddInt64(&r.bucketSurplusSize, -size)
|
||||
return nil
|
||||
}
|
||||
if r.hasStop {
|
||||
return errors.New("the rate has closed")
|
||||
}
|
||||
r.cond.L.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// GetNowRate returns the current rate
|
||||
// Just a rough number
|
||||
func (r *Rate) GetNowRate() int64 {
|
||||
return r.nowRate
|
||||
}
|
||||
|
||||
func (r *Rate) session() {
|
||||
ticker := time.NewTicker(time.Second * 1)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if rs := r.bucketAddSize - r.bucketSurplusSize; rs > 0 {
|
||||
r.nowRate = rs
|
||||
} else {
|
||||
r.nowRate = r.bucketSize - r.bucketSurplusSize
|
||||
}
|
||||
r.add(r.bucketAddSize)
|
||||
r.cond.Broadcast()
|
||||
case <-r.stopChan:
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
52
lib/rate/rate_test.go
Normal file
52
lib/rate/rate_test.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package rate
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestRate_GetWrite(t *testing.T) {
|
||||
r := NewRate(1024)
|
||||
r.Write(2048)
|
||||
n := 0
|
||||
go func() {
|
||||
for {
|
||||
r.Get(1024)
|
||||
n += 1024
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-time.After(time.Second):
|
||||
r.Stop()
|
||||
if n != 2048 {
|
||||
t.Fatal("get token error", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRate_StartGetRate(t *testing.T) {
|
||||
r := NewRate(1024)
|
||||
r.Start()
|
||||
n := 0
|
||||
go func() {
|
||||
for {
|
||||
err := r.Get(1024)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += 1024
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-time.After(time.Second * 5):
|
||||
r.Stop()
|
||||
time.Sleep(time.Second * 2)
|
||||
if n < 4*1024 || n > 1024*5 {
|
||||
t.Fatal("get error", n)
|
||||
}
|
||||
if math.Abs(float64(r.GetNowRate()-1024)) > 100 {
|
||||
t.Fatal("rate error", n)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user