diff options
| author | Christine Dodrill <me@christine.website> | 2018-10-05 10:02:55 -0700 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2018-10-05 14:31:22 -0700 |
| commit | f84de97aac5865fc17ee28cb1af7f25005ee321b (patch) | |
| tree | 92c767bacd170ba8fbff07794abb5f0951af5eb2 | |
| parent | d0007f122fe353a6d3796b12f8c7dbdb753811ac (diff) | |
| download | x-f84de97aac5865fc17ee28cb1af7f25005ee321b.tar.xz x-f84de97aac5865fc17ee28cb1af7f25005ee321b.zip | |
import libraries
| -rw-r--r-- | localca/doc.go | 10 | ||||
| -rw-r--r-- | localca/localca.go | 127 | ||||
| -rw-r--r-- | localca/localca_test.go | 82 | ||||
| -rw-r--r-- | localca/minica.go | 234 | ||||
| -rw-r--r-- | localca/utils.go | 83 | ||||
| -rw-r--r-- | namegen/elfs/elfs.go | 506 | ||||
| -rw-r--r-- | namegen/elfs/elfs_test.go | 10 | ||||
| -rw-r--r-- | namegen/tarot/doc.go | 3 | ||||
| -rw-r--r-- | namegen/tarot/namegen.go | 40 | ||||
| -rw-r--r-- | namegen/tarot/namegen_test.go | 10 | ||||
| -rw-r--r-- | tun2/backend.go | 78 | ||||
| -rw-r--r-- | tun2/backend_test.go | 211 | ||||
| -rw-r--r-- | tun2/client.go | 171 | ||||
| -rw-r--r-- | tun2/client_test.go | 21 | ||||
| -rw-r--r-- | tun2/connection.go | 162 | ||||
| -rw-r--r-- | tun2/doc.go | 11 | ||||
| -rw-r--r-- | tun2/server.go | 480 | ||||
| -rw-r--r-- | tun2/server_test.go | 171 | ||||
| -rw-r--r-- | tun2/storage_test.go | 100 | ||||
| -rw-r--r-- | web/tokiponatokens/toki_pona_test.go | 2 |
20 files changed, 2511 insertions, 1 deletions
diff --git a/localca/doc.go b/localca/doc.go new file mode 100644 index 0000000..fb4e829 --- /dev/null +++ b/localca/doc.go @@ -0,0 +1,10 @@ +// Package localca uses an autocert.Cache to store and generate TLS certificates +// for domains on demand. +// +// This is kind of powerful, and as such it is limited to only generate +// certificates as subdomains of a given domain. +// +// The design and implementation of this is kinda stolen from minica[1]. +// +// [1]: https://github.com/jsha/minica +package localca diff --git a/localca/localca.go b/localca/localca.go new file mode 100644 index 0000000..abc0f35 --- /dev/null +++ b/localca/localca.go @@ -0,0 +1,127 @@ +package localca + +import ( + "context" + "crypto/tls" + "encoding/pem" + "errors" + "strings" + "time" + + "github.com/Xe/ln" + "github.com/Xe/ln/opname" + "golang.org/x/crypto/acme/autocert" +) + +var ( + ErrBadData = errors.New("localca: certificate data is bad") + ErrDomainDoesntHaveSuffix = errors.New("localca: domain doesn't have the given suffix") +) + +// Manager automatically provisions and caches TLS certificates in a given +// autocert Cache. If it cannot fetch a certificate on demand, the certificate +// is dynamically generated with a lifetime of 100 years, which should be good +// enough. +type Manager struct { + Cache autocert.Cache + DomainSuffix string + + *issuer +} + +// New creates a new Manager with the given key filename, certificate filename, +// allowed domain suffix and autocert cache. All given certificates will be +// created if they don't already exist. +func New(keyFile, certFile, suffix string, cache autocert.Cache) (Manager, error) { + iss, err := getIssuer(keyFile, certFile, true) + + if err != nil { + return Manager{}, err + } + + result := Manager{ + DomainSuffix: suffix, + Cache: cache, + issuer: iss, + } + + return result, nil +} + +func (m Manager) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) { + name := hello.ServerName + if !strings.Contains(strings.Trim(name, "."), ".") { + return nil, errors.New("localca: server name component count invalid") + } + + if !strings.HasSuffix(name, m.DomainSuffix) { + return nil, ErrDomainDoesntHaveSuffix + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + ctx = opname.With(ctx, "localca.Manager.GetCertificate") + ctx = ln.WithF(ctx, ln.F{"server_name": name}) + + data, err := m.Cache.Get(ctx, name) + if err != nil && err != autocert.ErrCacheMiss { + return nil, err + } + + if err == autocert.ErrCacheMiss { + data, _, err = m.issuer.sign([]string{name}, nil) + if err != nil { + return nil, err + } + err = m.Cache.Put(ctx, name, data) + if err != nil { + return nil, err + } + } + + cert, err := loadCertificate(name, data) + if err != nil { + return nil, err + } + + ln.Log(ctx, ln.Info("returned cert successfully")) + + return cert, nil +} + +func loadCertificate(name string, data []byte) (*tls.Certificate, error) { + priv, pub := pem.Decode(data) + if priv == nil || !strings.Contains(priv.Type, "PRIVATE") { + return nil, ErrBadData + } + privKey, err := parsePrivateKey(priv.Bytes) + if err != nil { + return nil, err + } + + // public + var pubDER [][]byte + for len(pub) > 0 { + var b *pem.Block + b, pub = pem.Decode(pub) + if b == nil { + break + } + pubDER = append(pubDER, b.Bytes) + } + if len(pub) > 0 { + return nil, ErrBadData + } + + // verify and create TLS cert + leaf, err := validCert(name, pubDER, privKey, time.Now()) + if err != nil { + return nil, err + } + tlscert := &tls.Certificate{ + Certificate: pubDER, + PrivateKey: privKey, + Leaf: leaf, + } + return tlscert, nil +} diff --git a/localca/localca_test.go b/localca/localca_test.go new file mode 100644 index 0000000..233253d --- /dev/null +++ b/localca/localca_test.go @@ -0,0 +1,82 @@ +package localca + +import ( + "context" + "crypto/tls" + "io" + "io/ioutil" + "os" + "path" + "testing" + "time" + + "golang.org/x/crypto/acme/autocert" +) + +func TestLocalCA(t *testing.T) { + dir, err := ioutil.TempDir("", "localca-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + cache := autocert.DirCache(dir) + + keyFile := path.Join(dir, "key.pem") + certFile := path.Join(dir, "cert.pem") + const suffix = "club" + + m, err := New(keyFile, certFile, suffix, cache) + if err != nil { + t.Fatal(err) + } + + t.Run("local", func(t *testing.T) { + _, err = m.GetCertificate(&tls.ClientHelloInfo{ + ServerName: "foo.local.cetacean.club", + }) + if err != nil { + t.Fatal(err) + } + }) + + t.Run("network", func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + tc := &tls.Config{ + GetCertificate: m.GetCertificate, + } + + go func() { + lis, err := tls.Listen("tcp", ":9293", tc) + if err != nil { + t.Fatal(err) + } + defer lis.Close() + + for { + select { + case <-ctx.Done(): + return + default: + } + + cli, err := lis.Accept() + if err != nil { + t.Fatal(err) + } + defer cli.Close() + + go io.Copy(cli, cli) + } + }() + + time.Sleep(130 * time.Millisecond) + cli, err := tls.Dial("tcp", "foo.local.cetacean.club:9293", &tls.Config{InsecureSkipVerify: true}) + if err != nil { + t.Fatal(err) + } + defer cli.Close() + + cli.Write([]byte("butts")) + }) +} diff --git a/localca/minica.go b/localca/minica.go new file mode 100644 index 0000000..d47330b --- /dev/null +++ b/localca/minica.go @@ -0,0 +1,234 @@ +package localca + +import ( + "bytes" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/hex" + "encoding/pem" + "fmt" + "io/ioutil" + "math" + "math/big" + "net" + "os" + "strings" + "time" +) + +type issuer struct { + key crypto.Signer + cert *x509.Certificate +} + +func getIssuer(keyFile, certFile string, autoCreate bool) (*issuer, error) { + keyContents, keyErr := ioutil.ReadFile(keyFile) + certContents, certErr := ioutil.ReadFile(certFile) + if os.IsNotExist(keyErr) && os.IsNotExist(certErr) { + err := makeIssuer(keyFile, certFile) + if err != nil { + return nil, err + } + return getIssuer(keyFile, certFile, false) + } else if keyErr != nil { + return nil, fmt.Errorf("%s (but %s exists)", keyErr, certFile) + } else if certErr != nil { + return nil, fmt.Errorf("%s (but %s exists)", certErr, keyFile) + } + key, err := readPrivateKey(keyContents) + if err != nil { + return nil, fmt.Errorf("reading private key from %s: %s", keyFile, err) + } + + cert, err := readCert(certContents) + if err != nil { + return nil, fmt.Errorf("reading CA certificate from %s: %s", certFile, err) + } + + equal, err := publicKeysEqual(key.Public(), cert.PublicKey) + if err != nil { + return nil, fmt.Errorf("comparing public keys: %s", err) + } else if !equal { + return nil, fmt.Errorf("public key in CA certificate %s doesn't match private key in %s", + certFile, keyFile) + } + return &issuer{key, cert}, nil +} + +func readPrivateKey(keyContents []byte) (crypto.Signer, error) { + block, _ := pem.Decode(keyContents) + if block == nil { + return nil, fmt.Errorf("no PEM found") + } else if block.Type != "RSA PRIVATE KEY" && block.Type != "ECDSA PRIVATE KEY" { + return nil, fmt.Errorf("incorrect PEM type %s", block.Type) + } + return x509.ParsePKCS1PrivateKey(block.Bytes) +} + +func readCert(certContents []byte) (*x509.Certificate, error) { + block, _ := pem.Decode(certContents) + if block == nil { + return nil, fmt.Errorf("no PEM found") + } else if block.Type != "CERTIFICATE" { + return nil, fmt.Errorf("incorrect PEM type %s", block.Type) + } + return x509.ParseCertificate(block.Bytes) +} + +func makeIssuer(keyFile, certFile string) error { + keyData, key, err := makeKey() + if err != nil { + return err + } + ioutil.WriteFile(keyFile, keyData, 0600) + certData, _, err := makeRootCert(key, certFile) + if err != nil { + return err + } + ioutil.WriteFile(certFile, certData, 0600) + return nil +} + +func makeKey() ([]byte, *rsa.PrivateKey, error) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, nil, err + } + der := x509.MarshalPKCS1PrivateKey(key) + if err != nil { + return nil, nil, err + } + buf := bytes.NewBuffer([]byte{}) + err = pem.Encode(buf, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: der, + }) + if err != nil { + return nil, nil, err + } + return buf.Bytes(), key, nil +} + +func makeRootCert(key crypto.Signer, filename string) ([]byte, *x509.Certificate, error) { + serial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + return nil, nil, err + } + template := &x509.Certificate{ + Subject: pkix.Name{ + CommonName: "localca root ca " + hex.EncodeToString(serial.Bytes()[:3]), + }, + SerialNumber: serial, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(100, 0, 0), + + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + BasicConstraintsValid: true, + IsCA: true, + MaxPathLenZero: true, + } + + der, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key) + if err != nil { + return nil, nil, err + } + buf := bytes.NewBuffer([]byte{}) + err = pem.Encode(buf, &pem.Block{ + Type: "CERTIFICATE", + Bytes: der, + }) + if err != nil { + return nil, nil, err + } + result, err := x509.ParseCertificate(der) + return buf.Bytes(), result, err +} + +func parseIPs(ipAddresses []string) ([]net.IP, error) { + var parsed []net.IP + for _, s := range ipAddresses { + p := net.ParseIP(s) + if p == nil { + return nil, fmt.Errorf("invalid IP address %s", s) + } + parsed = append(parsed, p) + } + return parsed, nil +} + +func publicKeysEqual(a, b interface{}) (bool, error) { + aBytes, err := x509.MarshalPKIXPublicKey(a) + if err != nil { + return false, err + } + bBytes, err := x509.MarshalPKIXPublicKey(b) + if err != nil { + return false, err + } + return bytes.Compare(aBytes, bBytes) == 0, nil +} + +func (iss *issuer) sign(domains []string, ipAddresses []string) ([]byte, *x509.Certificate, error) { + var cn string + if len(domains) > 0 { + cn = domains[0] + } else if len(ipAddresses) > 0 { + cn = ipAddresses[0] + } else { + return nil, nil, fmt.Errorf("must specify at least one domain name or IP address") + } + keyData, key, err := makeKey() + if err != nil { + return nil, nil, err + } + buf := bytes.NewBuffer([]byte{}) + buf.Write(keyData) + + parsedIPs, err := parseIPs(ipAddresses) + if err != nil { + return nil, nil, err + } + serial, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt64)) + if err != nil { + return nil, nil, err + } + template := &x509.Certificate{ + DNSNames: domains, + IPAddresses: parsedIPs, + Subject: pkix.Name{ + CommonName: cn, + }, + SerialNumber: serial, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(90, 0, 0), + + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + BasicConstraintsValid: true, + IsCA: false, + } + der, err := x509.CreateCertificate(rand.Reader, template, iss.cert, key.Public(), iss.key) + if err != nil { + return nil, nil, err + } + err = pem.Encode(buf, &pem.Block{ + Type: "CERTIFICATE", + Bytes: der, + }) + if err != nil { + return nil, nil, err + } + result, err := x509.ParseCertificate(der) + return buf.Bytes(), result, err +} + +func split(s string) (results []string) { + if len(s) > 0 { + return strings.Split(s, ",") + } + return nil +} diff --git a/localca/utils.go b/localca/utils.go new file mode 100644 index 0000000..efc06f3 --- /dev/null +++ b/localca/utils.go @@ -0,0 +1,83 @@ +package localca + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "crypto/x509" + "errors" + "time" +) + +// validCert parses a cert chain provided as der argument and verifies the leaf and der[0] +// correspond to the private key, the domain and key type match, and expiration dates +// are valid. It doesn't do any revocation checking. +// +// The returned value is the verified leaf cert. +func validCert(name string, der [][]byte, key crypto.Signer, now time.Time) (leaf *x509.Certificate, err error) { + // parse public part(s) + var n int + for _, b := range der { + n += len(b) + } + pub := make([]byte, n) + n = 0 + for _, b := range der { + n += copy(pub[n:], b) + } + x509Cert, err := x509.ParseCertificates(pub) + if err != nil || len(x509Cert) == 0 { + return nil, errors.New("localca: no public key found") + } + // verify the leaf is not expired and matches the domain name + leaf = x509Cert[0] + if now.Before(leaf.NotBefore) { + return nil, errors.New("localca: certificate is not valid yet") + } + if now.After(leaf.NotAfter) { + return nil, errors.New("localca: expired certificate") + } + if err := leaf.VerifyHostname(name); err != nil { + return nil, err + } + // ensure the leaf corresponds to the private key and matches the certKey type + switch pub := leaf.PublicKey.(type) { + case *rsa.PublicKey: + prv, ok := key.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("localca: private key type does not match public key type") + } + if pub.N.Cmp(prv.N) != 0 { + return nil, errors.New("localca: private key does not match public key") + } + default: + return nil, errors.New("localca: unknown public key algorithm") + } + return leaf, nil +} + +// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates +// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys. +// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three. +// +// Inspired by parsePrivateKey in crypto/tls/tls.go. +func parsePrivateKey(der []byte) (crypto.Signer, error) { + if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { + return key, nil + } + if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { + switch key := key.(type) { + case *rsa.PrivateKey: + return key, nil + case *ecdsa.PrivateKey: + return key, nil + default: + return nil, errors.New("localca: unknown private key type in PKCS#8 wrapping") + } + } + if key, err := x509.ParseECPrivateKey(der); err == nil { + return key, nil + } + + return nil, errors.New("localca: failed to parse private key") +} diff --git a/namegen/elfs/elfs.go b/namegen/elfs/elfs.go new file mode 100644 index 0000000..8607e4b --- /dev/null +++ b/namegen/elfs/elfs.go @@ -0,0 +1,506 @@ +/* +Package elfs is this project's heroku style name generator. +*/ +package elfs + +import ( + "fmt" + "math/rand" + "strings" +) + +// Names is the name of every Pokemon from Pokemon Vietnamese Crystal. +var Names = []string{ + "SEED", + "GRASS", + "FLOWE", + "SHAD", + "CABR", + "SNAKE", + "GOLD", + "COW", + "GUIKI", + "PEDAL", + "DELAN", + "B-FLY", + "BIDE", + "KEYU", + "FORK", + "LAP", + "PIGE", + "PIJIA", + "CAML", + "LAT", + "BIRD", + "BABOO", + "VIV", + "ABOKE", + "PIKAQ", + "RYE", + "SAN", + "BREAD", + "LIDEL", + "LIDE", + "PIP", + "PIKEX", + "ROK", + "JUGEN", + "PUD", + "BUDE", + "ZHIB", + "GELU", + "GRAS", + "FLOW", + "LAFUL", + "ATH", + "BALA", + "CORN", + "MOLUF", + "DESP", + "DAKED", + "MIMI", + "BOLUX", + "KODA", + "GELUD", + "MONK", + "SUMOY", + "GEDI", + "WENDI", + "NILEM", + "NILE", + "NILEC", + "KEZI", + "YONGL", + "HUDE", + "WANLI", + "GELI", + "GUAIL", + "MADAQ", + "WUCI", + "WUCI", + "MUJEF", + "JELLY", + "SICIB", + "GELU", + "NELUO", + "BOLI", + "JIALE", + "YED", + "YEDE", + "CLO", + "SCARE", + "AOCO", + "DEDE", + "DEDEI", + "BAWU", + "JIUG", + "BADEB", + "BADEB", + "HOLE", + "BALUX", + "GES", + "FANT", + "QUAR", + "YIHE", + "SWAB", + "SLIPP", + "CLU", + "DEPOS", + "BILIY", + "YUANO", + "SOME", + "NO", + "YELA", + "EMPT", + "ZECUN", + "XIAHE", + "BOLEL", + "DEJI", + "MACID", + "XIHON", + "XITO", + "LUCK", + "MENJI", + "GELU", + "DECI", + "XIDE", + "DASAJ", + "DONGN", + "RICUL", + "MINXI", + "BALIY", + "ZENDA", + "LUZEL", + "HELE5", + "0FENB", + "KAIL", + "JIAND", + "CARP", + "JINDE", + "LAPU", + "MUDE", + "YIFU", + "LINLI", + "SANDI", + "HUSI", + "JINC", + "OUMU", + "OUMUX", + "CAP", + "KUIZA", + "PUD", + "TIAO", + "FRMAN", + "CLAU", + "SPARK", + "DRAGO", + "BOLIU", + "GUAIL", + "MIYOU", + "MIY", + "QIAOK", + "BEIL", + "MUKEI", + "RIDED", + "MADAM", + "BAGEP", + "CROC", + "ALIGE", + "OUDAL", + "OUD", + "DADA", + "HEHE", + "YEDEA", + "NUXI", + "NUXIN", + "ROUY", + "ALIAD", + "STICK", + "QIANG", + "LAAND", + "PIQI", + "PI", + "PUPI", + "DEKE", + "DEKEJ", + "NADI", + "NADIO", + "MALI", + "PEA", + "ELECT", + "FLOWE", + "MAL", + "MALI", + "HUSHU", + "NILEE", + "YUZI", + "POPOZ", + "DUZI", + "HEBA", + "XIAN", + "SHAN", + "YEYEA", + "WUY", + "LUO", + "KEFE", + "HULA", + "CROW", + "YADEH", + "MOW", + "ANNAN", + "SUONI", + "KYLI", + "HULU", + "HUDEL", + "YEHE", + "GULAE", + "YEHE", + "BLU", + "GELAN", + "BOAT", + "NIP", + "POIT", + "HELAK", + "XINL", + "BEAR", + "LINB", + "MAGEH", + "MAGEJ", + "WULI", + "YIDE", + "RIVE", + "FISH", + "AOGU", + "DELIE", + "MANTE", + "KONMU", + "DELU", + "HELU", + "HUAN", + "HUMA", + "DONGF", + "JINCA", + "HEDE", + "DEFU", + "LIBY", + "JIAPA", + "MEJI", + "HELE", + "BUHU", + "MILK", + "HABI", + "THUN", + "GARD", + "DON", + "YANGQ", + "SANAQ", + "BANQ", + "LUJ", + "PHIX", + "SIEI", + "EGG", +} + +// Moves is every single move from Pokemon Vietnamese Crystal. +var Moves = []string{ + "ABLE", + "ABNORMA", + "AGAIN", + "AIREXPL", + "ANG", + "ANGER", + "ASAIL", + "ATTACK", + "AURORA", + "AWL", + "BAN", + "BAND", + "BARE", + "BEAT", + "BEATED", + "BELLY", + "BIND", + "BITE", + "BLOC", + "BLOOD", + "BODY", + "BOOK", + "BREATH", + "BUMP", + "CAST", + "CHAM", + "CLAMP", + "CLAP", + "CLAW", + "CLEAR", + "CLI", + "CLIP", + "CLOUD", + "CONTRO", + "CONVY", + "COOLHIT", + "CRASH", + "CRY", + "CUT", + "DESCRI", + "D-FIGHT", + "DIG", + "DITCH", + "DIV", + "DOZ", + "DRE", + "DUL", + "DU-PIN", + "DYE", + "EARTH", + "EDU", + "EG-BOMB", + "EGG", + "ELEGY", + "ELE-HIT", + "EMBODY", + "EMPLI", + "ENGL", + "ERUPT", + "EVENS", + "EXPLOR", + "EYES", + "FALL", + "FAST", + "F-CAR", + "F-DANCE", + "FEARS", + "F-FIGHT", + "FIGHT", + "FIR", + "FIRE", + "FIREHIT", + "FLAME", + "FLAP", + "FLASH", + "FLEW", + "FORCE", + "FRA", + "FREEZE", + "FROG", + "G-BIRD", + "GENKISS", + "GIFT", + "G-KISS", + "G-MOUSE", + "GRADE", + "GROW", + "HAMMER", + "HARD", + "HAT", + "HATE", + "H-BOMB", + "HELL-R", + "HEMP", + "HINT", + "HIT", + "HU", + "HUNT", + "HYPNOSI", + "INHA", + "IRO", + "IRONBAR", + "IR-WING", + "J-GUN", + "KEE", + "KICK", + "KNIF", + "KNIFE", + "KNOCK", + "LEVEL", + "LIGH", + "LIGHHIT", + "LIGHT", + "LIVE", + "L-WALL", + "MAD", + "MAJUS", + "MEL", + "MELO", + "MESS", + "MILK", + "MIMI", + "MISS", + "MIXING", + "MOVE", + "MUD", + "NI-BED", + "NOISY", + "NOONLI", + "NULL", + "N-WAVE", + "PAT", + "PEACE", + "PIN", + "PLAN", + "PLANE", + "POIS", + "POL", + "POWDE", + "POWE", + "POWER", + "PRIZE", + "PROTECT", + "PROUD", + "RAGE", + "RECOR", + "REFLAC", + "REFREC", + "REGR", + "RELIV", + "RENEW", + "R-FIGHT", + "RING", + "RKICK", + "ROCK", + "ROUND", + "RUS", + "RUSH", + "SAND", + "SAW", + "SCISSOR", + "SCRA", + "SCRIPT", + "SEEN", + "SERVER", + "SHADOW", + "SHELL", + "SHINE", + "SHO", + "SIGHT", + "SIN", + "SMALL", + "SMELT", + "SMOK", + "SNAKE", + "SNO", + "SNOW", + "SOU", + "SO-WAVE", + "SPAR", + "SPEC", + "SPID", + "S-PIN", + "SPRA", + "STAM", + "STARE", + "STEA", + "STONE", + "STORM", + "STRU", + "STRUG", + "STUDEN", + "SUBS", + "SUCID", + "SUN-LIG", + "SUNRIS", + "SUPLY", + "S-WAVE", + "TAILS", + "TANGL", + "TASTE", + "TELLI", + "THANK", + "TONKICK", + "TOOTH", + "TORL", + "TRAIN", + "TRIKICK", + "TUNGE", + "VOLT", + "WA-GUN", + "WATCH", + "WAVE", + "W-BOMB", + "WFALL", + "WFING", + "WHIP", + "WHIRL", + "WIND", + "WOLF", + "WOOD", + "WOR", + "YUJA", +} + +func randomMove() string { + return Moves[rand.Intn(len(Moves))] +} + +func randomName() string { + return Names[rand.Intn(len(Names))] +} + +// Next generates a new domain name based on the moves and Pokemon names +// from Pokemon Vietnamese Crystal. +func Next() string { + move1 := randomMove() + move2 := randomMove() + poke := randomName() + return strings.ToLower(fmt.Sprintf("%s-%s-%s", move1, move2, poke)) +} diff --git a/namegen/elfs/elfs_test.go b/namegen/elfs/elfs_test.go new file mode 100644 index 0000000..dd8a2d4 --- /dev/null +++ b/namegen/elfs/elfs_test.go @@ -0,0 +1,10 @@ +package elfs + +import "testing" + +func TestNext(t *testing.T) { + n := Next() + if len(n) == 0 { + t.Fatalf("MakeName had a zero output") + } +} diff --git a/namegen/tarot/doc.go b/namegen/tarot/doc.go new file mode 100644 index 0000000..9fb79cc --- /dev/null +++ b/namegen/tarot/doc.go @@ -0,0 +1,3 @@ +// Package tarot is an automatic name generator. It generates names that could +// pass for tarot cards if you squint hard enough. +package tarot diff --git a/namegen/tarot/namegen.go b/namegen/tarot/namegen.go new file mode 100644 index 0000000..ec18d11 --- /dev/null +++ b/namegen/tarot/namegen.go @@ -0,0 +1,40 @@ +package tarot + +import ( + "fmt" + "math/rand" + "time" +) + +func init() { + rand.Seed(time.Now().Unix()) +} + +// The ranks and suits of this name generator. +var ( + Ranks = []string{ + "one", "two", "three", "four", "five", + "six", "seven", "eight", "nine", "ten", + "ace", "king", "page", "princess", "queen", "jack", + "king", "magus", "prince", "knight", "challenger", + "daughter", "son", "priestess", "shaman", + } + + Suits = []string{ + "clubs", "hearts", "spades", "diamonds", // common playing cards + "swords", "cups", "pentacles", "wands", // tarot + "disks", // thoth tarot + "coins", // karma + "earth", "wind", "water", "air", // classical elements + "aether", "spirits", "nirvana", // new age sounding things + "chakras", "dilutions", "goings", + } +) + +// Next creates a name. +func Next() string { + rank := Ranks[rand.Int()%len(Ranks)] + suit := Suits[rand.Int()%len(Suits)] + + return fmt.Sprintf("%s-of-%s-%d", rank, suit, rand.Int63()%100000) +} diff --git a/namegen/tarot/namegen_test.go b/namegen/tarot/namegen_test.go new file mode 100644 index 0000000..e4f2640 --- /dev/null +++ b/namegen/tarot/namegen_test.go @@ -0,0 +1,10 @@ +package tarot + +import ( + "log" + "testing" +) + +func TestNext(t *testing.T) { + log.Println(Next()) +} diff --git a/tun2/backend.go b/tun2/backend.go new file mode 100644 index 0000000..d94a1a8 --- /dev/null +++ b/tun2/backend.go @@ -0,0 +1,78 @@ +package tun2 + +import "time" + +// Backend is the public state of an individual Connection. +type Backend struct { + ID string + Proto string + User string + Domain string + Phi float32 + Host string + Usable bool +} + +type backendMatcher func(*Connection) bool + +func (s *Server) getBackendsForMatcher(bm backendMatcher) []Backend { + s.connlock.Lock() + defer s.connlock.Unlock() + + var result []Backend + + for _, c := range s.conns { + if !bm(c) { + continue + } + + result = append(result, Backend{ + ID: c.id, + Proto: c.conn.LocalAddr().Network(), + User: c.user, + Domain: c.domain, + Phi: float32(c.detector.Phi(time.Now())), + Host: c.conn.RemoteAddr().String(), + Usable: c.usable, + }) + } + + return result +} + +// KillBackend forcibly disconnects a given backend but doesn't offer a way to +// "ban" it from reconnecting. +func (s *Server) KillBackend(id string) error { + s.connlock.Lock() + defer s.connlock.Unlock() + + for _, c := range s.conns { + if c.id == id { + c.cancel() + return nil + } + } + + return ErrNoSuchBackend +} + +// GetBackendsForDomain fetches all backends connected to this server associated +// to a single public domain name. +func (s *Server) GetBackendsForDomain(domain string) []Backend { + return s.getBackendsForMatcher(func(c *Connection) bool { + return c.domain == domain + }) +} + +// GetBackendsForUser fetches all backends connected to this server owned by a +// given user by username. +func (s *Server) GetBackendsForUser(uname string) []Backend { + return s.getBackendsForMatcher(func(c *Connection) bool { + return c.user == uname + }) |
