Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
wizche committed Mar 21, 2022
0 parents commit b1f9edf
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea/
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# NetworkSimulator in Go

Minimal network simulator written in Go. Inspired by [INetSim](https://www.inetsim.org/).

**Features:**
* Generate a CA certificate on start (you can import that as [trusted certificate authority](https://asu.secure.force.com/kb/articles/FAQ/How-Do-I-Add-Certificates-to-the-Trusted-Root-Certification-Authorities-Store-for-a-Local-Computer))
* Create DNS server on all addresses and respond to all queries with its own IP
* Create HTTP server responding to all requests `200 OK`
* Create HTTPs server responding to all requests `200 OK`. Generates on the fly server certificate (based on requested server name)

## Thanks
Shamelessly used following resources:
* https://gist.github.com/walm/0d67b4fb2d5daf3edd4fad3e13b162cb
* https://shaneutt.com/blog/golang-ca-and-signed-cert-go/
20 changes: 20 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package main

import (
"gonetsim/internal/dns"
"gonetsim/internal/web"
"log"
"os"
"os/signal"
"syscall"
)

func main() {
dns.StartDNSServer(53)
web.StartHttpServer(8080)
web.StartHttpsServer(8443)
sig := make(chan os.Signal)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
s := <-sig
log.Fatalf("Signal (%v) received, stopping\n", s)
}
12 changes: 12 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module gonetsim

go 1.18

require (
github.com/miekg/dns v1.1.47 // indirect
golang.org/x/mod v0.4.2 // indirect
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)
34 changes: 34 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
github.com/miekg/dns v1.1.47 h1:J9bWiXbqMbnZPcY8Qi2E3EWIBsIm6MZzzJB9VRg5gL8=
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985 h1:4CSI6oo7cOjJKajidEljs9h+uP0rRZBPPPhcCbj5mw8=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
55 changes: 55 additions & 0 deletions internal/dns/dns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package dns

import (
"fmt"
"github.com/miekg/dns"
"log"
"strconv"
)

func parseQuery(m *dns.Msg) {
for _, q := range m.Question {
switch q.Qtype {
case dns.TypeA:
log.Printf("Query for %s\n", q.Name)
rr, err := dns.NewRR(fmt.Sprintf("%s A %s", q.Name, "127.0.0.1"))
if err == nil {
m.Answer = append(m.Answer, rr)
}
}
}
}

func handleDnsRequest(w dns.ResponseWriter, r *dns.Msg) {
m := new(dns.Msg)
m.SetReply(r)
m.Compress = false
switch r.Opcode {
case dns.OpcodeQuery:
parseQuery(m)
}
w.WriteMsg(m)
}

func StartDNSServer(port int) {
log.Printf("Starting DNS server at port %d\n", port)

// attach request handler func
dns.HandleFunc(".", handleDnsRequest)

go func() {
srv := &dns.Server{Addr: "127.0.0.1:" + strconv.Itoa(port), Net: "udp"}
if err := srv.ListenAndServe(); err != nil {
log.Fatalf("Failed to set udp listener %s\n", err.Error())
}
defer srv.Shutdown()
}()

go func() {
srv := &dns.Server{Addr: ":" + strconv.Itoa(port), Net: "tcp"}
if err := srv.ListenAndServe(); err != nil {
log.Fatalf("Failed to set tcp listener %s\n", err.Error())
}
defer srv.Shutdown()
}()
}
89 changes: 89 additions & 0 deletions internal/web/cert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package web

import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"log"
"math/big"
"time"
)

func GenerateCACertificate() (caCert *x509.Certificate, caBytes []byte, caKey *rsa.PrivateKey) {
// set up our CA certificate
ca := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: pkix.Name{
Organization: []string{"Gonetsim"},
Country: []string{"CH"},
Province: []string{""},
Locality: []string{"Zurich"},
StreetAddress: []string{"Paradeplatz"},
PostalCode: []string{"8000"},
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}

// create our private and public key
caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
log.Fatalf("Failed to generate CA key %s\n", err.Error())
}

// create the CA
caFinal, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey)
if err != nil {
log.Fatalf("Failed to generate CA certificate %s\n", err.Error())
}

return ca, caFinal, caPrivKey
}

func SignCertificate(ca *x509.Certificate, caPrivKey *rsa.PrivateKey, certPrivKey *rsa.PrivateKey, commonName string) (serverTLSConf tls.Certificate) {

// set up our server certificate
cert := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: pkix.Name{
CommonName: commonName,
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
SubjectKeyId: []byte{1, 2, 3, 4, 6},
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}

certBytes, err := x509.CreateCertificate(rand.Reader, cert, ca, &certPrivKey.PublicKey, caPrivKey)
if err != nil {
log.Fatalf("Failed to generate certificate %s\n", err.Error())
}

certPEM := new(bytes.Buffer)
pem.Encode(certPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
})

certPrivKeyPEM := new(bytes.Buffer)
pem.Encode(certPrivKeyPEM, &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(certPrivKey),
})

serverCert, err := tls.X509KeyPair(certPEM.Bytes(), certPrivKeyPEM.Bytes())
if err != nil {
log.Fatalf("Failed to generate certificate keypair %s\n", err.Error())
}

return serverCert
}
73 changes: 73 additions & 0 deletions internal/web/web.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package web

import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"log"
"net/http"
)

func StartHttpServer(port int) {

log.Printf("Starting HTTP server at port %d\n", port)

http.HandleFunc("/", handleRequest())

go func() {
if err := http.ListenAndServe(fmt.Sprint(":", port), nil); err != nil {
log.Fatalf("Failed to start http server %s\n", err.Error())
}
}()
}

func handleRequest() func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
io.WriteString(w, "@_@\n")
log.Printf("Responded to request %s %s%s", r.Method, r.Host, r.RequestURI)
}
}

func StartHttpsServer(port int) {
log.Printf("Starting HTTPs server at port %d\n", port)
caCertificate, caBytes, caKey := GenerateCACertificate()

caCert, _ := x509.ParseCertificate(caBytes)
publicKeyDer, _ := x509.MarshalPKIXPublicKey(caCert.PublicKey)
publicKeyBlock := pem.Block{
Type: "PUBLIC KEY",
Bytes: publicKeyDer,
}
publicKeyPem := string(pem.EncodeToMemory(&publicKeyBlock))
log.Printf("CA Certificate (DER format):\n%s", publicKeyPem)

// lets generate a key here and reuse it everytime!
certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
log.Fatalf("Failed to generate caCertificate caKey %s\n", err.Error())
}

serverTLSConf := &tls.Config{
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
cert := SignCertificate(caCertificate, caKey, certPrivKey, info.ServerName)
return &cert, nil
},
}

s := &http.Server{
Addr: fmt.Sprint(":", port),
TLSConfig: serverTLSConf,
}

go func() {
defer s.Close()
if err := s.ListenAndServeTLS("", ""); err != nil {
log.Fatalf("Failed to start HTTPS server %s\n", err.Error())
}
}()
}

0 comments on commit b1f9edf

Please sign in to comment.