Skip to content

Commit 0dfe704

Browse files
authored
Merge pull request #18 from projectdiscovery/feature-tcp-server
Feature tcp server
2 parents 52b211d + 9be5b1a commit 0dfe704

File tree

9 files changed

+399
-3
lines changed

9 files changed

+399
-3
lines changed

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@ module github.com/projectdiscovery/simplehttpserver
22

33
go 1.15
44

5-
require github.com/projectdiscovery/gologger v1.1.4
5+
require (
6+
github.com/projectdiscovery/gologger v1.1.4
7+
gopkg.in/yaml.v2 v2.4.0
8+
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
3030
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3131
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
3232
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
33+
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
34+
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
3335
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
3436
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
3537
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

pkg/sslcert/options.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package sslcert
2+
3+
import "time"
4+
5+
type Options struct {
6+
Host string // Comma-separated hostnames and IPs to generate a certificate for")
7+
Organization string
8+
ValidFrom string // Creation date formatted as Jan 1 15:04:05 2011
9+
ValidFor time.Duration // 365*24*time.Hour Duration that certificate is valid for
10+
IsCA bool // whether this cert should be its own Certificate Authority
11+
RSABits int // 2048 Size of RSA key to generate. Ignored if --ecdsa-curve is set
12+
EcdsaCurve string // ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521
13+
Ed25519Key bool // Generate an Ed25519 key
14+
}
15+
16+
var DefaultOptions = Options{
17+
ValidFor: time.Duration(365 * 24 * time.Hour),
18+
IsCA: false,
19+
RSABits: 2048,
20+
Organization: "Acme Co",
21+
}

pkg/sslcert/sslcert.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// Package sslcert contains a reworked version of https://golang.org/src/crypto/tls/generate_cert.go
2+
package sslcert
3+
4+
import (
5+
"bufio"
6+
"bytes"
7+
"crypto/ecdsa"
8+
"crypto/ed25519"
9+
"crypto/elliptic"
10+
"crypto/rand"
11+
"crypto/rsa"
12+
"crypto/x509"
13+
"crypto/x509/pkix"
14+
"encoding/pem"
15+
"errors"
16+
"fmt"
17+
"math/big"
18+
"net"
19+
"strings"
20+
"time"
21+
)
22+
23+
func pubKey(priv interface{}) interface{} {
24+
switch k := priv.(type) {
25+
case *rsa.PrivateKey:
26+
return &k.PublicKey
27+
case *ecdsa.PrivateKey:
28+
return &k.PublicKey
29+
case ed25519.PrivateKey:
30+
return k.Public().(ed25519.PublicKey)
31+
default:
32+
return nil
33+
}
34+
}
35+
36+
func Generate(options Options) (privateKey, publicKey []byte, err error) {
37+
if options.Host == "" {
38+
return nil, nil, errors.New("Empty host value")
39+
}
40+
41+
var priv interface{}
42+
switch options.EcdsaCurve {
43+
case "":
44+
if options.Ed25519Key {
45+
_, priv, err = ed25519.GenerateKey(rand.Reader)
46+
} else {
47+
priv, err = rsa.GenerateKey(rand.Reader, options.RSABits)
48+
}
49+
case "P224":
50+
priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
51+
case "P256":
52+
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
53+
case "P384":
54+
priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
55+
case "P521":
56+
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
57+
default:
58+
err = fmt.Errorf("Unrecognized elliptic curve: %q", options.EcdsaCurve)
59+
return
60+
}
61+
if err != nil {
62+
err = fmt.Errorf("Failed to generate private key: %v", err)
63+
return
64+
}
65+
66+
// ECDSA, ED25519 and RSA subject keys should have the DigitalSignature
67+
// KeyUsage bits set in the x509.Certificate template
68+
keyUsage := x509.KeyUsageDigitalSignature
69+
// Only RSA subject keys should have the KeyEncipherment KeyUsage bits set. In
70+
// the context of TLS this KeyUsage is particular to RSA key exchange and
71+
// authentication.
72+
if _, isRSA := priv.(*rsa.PrivateKey); isRSA {
73+
keyUsage |= x509.KeyUsageKeyEncipherment
74+
}
75+
76+
var notBefore time.Time
77+
if len(options.ValidFrom) == 0 {
78+
notBefore = time.Now()
79+
} else {
80+
notBefore, err = time.Parse("Jan 2 15:04:05 2006", options.ValidFrom)
81+
if err != nil {
82+
err = fmt.Errorf("Failed to parse creation date: %v", err)
83+
return
84+
}
85+
}
86+
87+
notAfter := notBefore.Add(options.ValidFor)
88+
89+
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
90+
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
91+
if err != nil {
92+
err = fmt.Errorf("Failed to generate serial number: %v", err)
93+
return
94+
}
95+
96+
template := x509.Certificate{
97+
SerialNumber: serialNumber,
98+
Subject: pkix.Name{
99+
Organization: []string{options.Organization},
100+
},
101+
NotBefore: notBefore,
102+
NotAfter: notAfter,
103+
104+
KeyUsage: keyUsage,
105+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
106+
BasicConstraintsValid: true,
107+
}
108+
109+
hosts := strings.Split(options.Host, ",")
110+
for _, h := range hosts {
111+
if ip := net.ParseIP(h); ip != nil {
112+
template.IPAddresses = append(template.IPAddresses, ip)
113+
} else {
114+
template.DNSNames = append(template.DNSNames, h)
115+
}
116+
}
117+
118+
if options.IsCA {
119+
template.IsCA = true
120+
template.KeyUsage |= x509.KeyUsageCertSign
121+
}
122+
123+
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, pubKey(priv), priv)
124+
if err != nil {
125+
err = fmt.Errorf("Failed to create certificate: %v", err)
126+
return
127+
}
128+
129+
var pubKeyBuf bytes.Buffer
130+
pubKeyBufb := bufio.NewWriter(&pubKeyBuf)
131+
err = pem.Encode(pubKeyBufb, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
132+
if err != nil {
133+
err = fmt.Errorf("Failed to write data to cert.pem: %v", err)
134+
return
135+
}
136+
pubKeyBufb.Flush()
137+
138+
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
139+
if err != nil {
140+
err = fmt.Errorf("Unable to marshal private key: %v", err)
141+
return
142+
}
143+
var privKeyBuf bytes.Buffer
144+
privKeyBufb := bufio.NewWriter(&privKeyBuf)
145+
err = pem.Encode(privKeyBufb, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes})
146+
if err != nil {
147+
err = fmt.Errorf("Failed to write data to key.pem: %v", err)
148+
return
149+
}
150+
privKeyBufb.Flush()
151+
152+
return pubKeyBuf.Bytes(), privKeyBuf.Bytes(), nil
153+
}

pkg/sslcert/tlsconfig.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package sslcert
2+
3+
import (
4+
"crypto/tls"
5+
)
6+
7+
func NewTLSConfig(options Options) (*tls.Config, error) {
8+
pubKey, privKey, err := Generate(options)
9+
if err != nil {
10+
return nil, err
11+
}
12+
13+
cert, err := tls.X509KeyPair(pubKey, privKey)
14+
if err != nil {
15+
return nil, err
16+
}
17+
18+
return &tls.Config{Certificates: []tls.Certificate{cert}}, nil
19+
}

pkg/tcpserver/responseengine.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package tcpserver
2+
3+
import (
4+
"errors"
5+
)
6+
7+
func (t *TCPServer) BuildResponse(data []byte) ([]byte, error) {
8+
// Process all the rules
9+
for _, rule := range t.options.rules {
10+
if rule.matchRegex.Match(data) {
11+
return []byte(rule.Response), nil
12+
}
13+
}
14+
return nil, errors.New("No matched rule")
15+
}

pkg/tcpserver/rule.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package tcpserver
2+
3+
import "regexp"
4+
5+
type RulesConfiguration struct {
6+
Rules []Rule `yaml:"rules"`
7+
}
8+
9+
type Rule struct {
10+
Match string `yaml:"match,omitempty"`
11+
matchRegex *regexp.Regexp
12+
Response string `yaml:"response,omitempty"`
13+
}
14+
15+
func NewRule(match string, response string) (*Rule, error) {
16+
regxp, err := regexp.Compile(match)
17+
if err != nil {
18+
return nil, err
19+
}
20+
21+
return &Rule{Match: match, matchRegex: regxp, Response: response}, nil
22+
}

pkg/tcpserver/tcpserver.go

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package tcpserver
2+
3+
import (
4+
"crypto/tls"
5+
"io/ioutil"
6+
"net"
7+
"time"
8+
9+
"github.com/projectdiscovery/simplehttpserver/pkg/sslcert"
10+
"gopkg.in/yaml.v2"
11+
)
12+
13+
type Options struct {
14+
Listen string
15+
TLS bool
16+
Certificate string
17+
Key string
18+
Domain string
19+
rules []Rule
20+
}
21+
22+
type TCPServer struct {
23+
options Options
24+
listener net.Listener
25+
}
26+
27+
func New(options Options) (*TCPServer, error) {
28+
return &TCPServer{options: options}, nil
29+
}
30+
31+
func (t *TCPServer) AddRule(rule Rule) error {
32+
t.options.rules = append(t.options.rules, rule)
33+
return nil
34+
}
35+
36+
func (t *TCPServer) ListenAndServe() error {
37+
listener, err := net.Listen("tcp4", t.options.Listen)
38+
if err != nil {
39+
return err
40+
}
41+
t.listener = listener
42+
return t.run()
43+
}
44+
45+
func (t *TCPServer) handleConnection(conn net.Conn) error {
46+
defer conn.Close()
47+
48+
buf := make([]byte, 4096)
49+
for {
50+
conn.SetReadDeadline(time.Now().Add(time.Duration(5 * time.Second)))
51+
_, err := conn.Read(buf)
52+
if err != nil {
53+
return err
54+
}
55+
56+
resp, err := t.BuildResponse(buf)
57+
if err != nil {
58+
return err
59+
}
60+
61+
conn.Write(resp)
62+
}
63+
}
64+
65+
func (t *TCPServer) ListenAndServeTLS() error {
66+
var tlsConfig *tls.Config
67+
if t.options.Certificate != "" && t.options.Key != "" {
68+
cert, err := tls.LoadX509KeyPair(t.options.Certificate, t.options.Key)
69+
if err != nil {
70+
return err
71+
}
72+
tlsConfig = &tls.Config{Certificates: []tls.Certificate{cert}}
73+
} else {
74+
tlsOptions := sslcert.DefaultOptions
75+
tlsOptions.Host = t.options.Domain
76+
cfg, err := sslcert.NewTLSConfig(tlsOptions)
77+
if err != nil {
78+
return err
79+
}
80+
tlsConfig = cfg
81+
}
82+
83+
listener, err := tls.Listen("tcp", t.options.Listen, tlsConfig)
84+
if err != nil {
85+
return err
86+
}
87+
t.listener = listener
88+
return t.run()
89+
}
90+
91+
func (t *TCPServer) run() error {
92+
for {
93+
c, err := t.listener.Accept()
94+
if err != nil {
95+
return err
96+
}
97+
go t.handleConnection(c)
98+
}
99+
}
100+
101+
func (t *TCPServer) Close() error {
102+
return t.listener.Close()
103+
}
104+
105+
func (t *TCPServer) LoadTemplate(templatePath string) error {
106+
var config RulesConfiguration
107+
yamlFile, err := ioutil.ReadFile(templatePath)
108+
if err != nil {
109+
return err
110+
}
111+
err = yaml.Unmarshal(yamlFile, &config)
112+
if err != nil {
113+
return err
114+
}
115+
116+
for _, ruleTemplate := range config.Rules {
117+
rule, err := NewRule(ruleTemplate.Match, ruleTemplate.Response)
118+
if err != nil {
119+
return err
120+
}
121+
t.options.rules = append(t.options.rules, *rule)
122+
}
123+
124+
return nil
125+
}

0 commit comments

Comments
 (0)