diff --git a/cmd/helloserver/main.go b/cmd/helloserver/main.go new file mode 100644 index 0000000..f24af3f --- /dev/null +++ b/cmd/helloserver/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "flag" + "fmt" + "net/http" +) + +var ( + port = flag.String("port", "9090", "HTTP port to listen on") +) + +func main() { + flag.Parse() + http.ListenAndServe(":"+*port, http.HandlerFunc(handle)) +} + +func handle(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, "Hello garukun, it's me xena. Hire me and I can make something like this for vungle.") +} diff --git a/main.go b/main.go index c009de3..027db9e 100644 --- a/main.go +++ b/main.go @@ -31,7 +31,7 @@ func main() { flagenv.Parse() rand.Seed(time.Now().Unix()) - s, err := server.New(&server.ServerConfig{ + s, err := server.New(server.Config{ ControlHost: *controlHost, ControlKeyFile: *controlKeyFile, RethinkDBHost: *rethinkDBHost, diff --git a/server/server.go b/server/server.go index a6196b0..52086a4 100644 --- a/server/server.go +++ b/server/server.go @@ -1,28 +1,26 @@ package server import ( + "crypto/rsa" "crypto/x509" "encoding/pem" "errors" - "fmt" "io/ioutil" "log" - "math/rand" "net" "net/http" "net/rpc" "os" "path/filepath" - "strconv" "time" "git.xeserv.us/xena/route/database" "git.xeserv.us/xena/route/lib/elfs" "git.xeserv.us/xena/route/routerpc" + "git.xeserv.us/xena/route/utils" "github.com/Xe/uuid" "github.com/Yawning/bulb" "github.com/koding/tunnel" - "github.com/sycamoreone/orc/tor" ) // RPC constants @@ -32,13 +30,10 @@ const ( // Server is the main server type type Server struct { - cfg *ServerConfig + cfg *Config - db *database.DB - - torCon *bulb.Conn - torCmd *tor.Cmd - torControlPath string + db *database.DB + tor *Tor rpcS *rpc.Server rpcAddr string @@ -46,8 +41,8 @@ type Server struct { ts *tunnel.Server } -// ServerConfig configures Server -type ServerConfig struct { +// Config configures Server +type Config struct { ControlHost, ControlKeyFile string RethinkDBHost, RethinkDBDatabase string TorDataDir, TorHashedPassword, TorPassword string @@ -55,7 +50,7 @@ type ServerConfig struct { } // New creates a new Server -func New(cfg *ServerConfig) (*Server, error) { +func New(cfg Config) (*Server, error) { db, err := database.New(cfg.RethinkDBHost, cfg.RethinkDBDatabase) if err != nil { return nil, err @@ -66,36 +61,12 @@ func New(cfg *ServerConfig) (*Server, error) { return nil, err } - torControlPath := filepath.Join(cfg.TorDataDir, fmt.Sprintf("%d.sock", rand.Int63())) - - tc := tor.NewConfig() - tc.Set("DataDirectory", cfg.TorDataDir) - tc.Set("HashedControlPassword", cfg.TorHashedPassword) - tc.Set("SocksPort", "0") - cp := rand.Intn(64512) // 64k - 1k - tc.Set("ControlPort", cp) - - tc.Timeout = 30 * time.Second - log.Println(tc.ToCmdLineFormat()) - - tcmd, err := tor.NewCmd(tc) - if err != nil { - return nil, err - } - - err = tcmd.Start() - if err != nil { - return nil, err - } - - time.Sleep(5 * time.Second) - - bc, err := bulb.Dial("tcp", "127.0.0.1:"+strconv.Itoa(cp)) - if err != nil { - return nil, err - } - - err = bc.Authenticate(cfg.TorPassword) + t, err := StartTor(TorConfig{ + DataDir: cfg.TorDataDir, + HashedControlPassword: cfg.TorHashedPassword, + ClearPassword: cfg.TorPassword, + Timeout: 30 * time.Second, + }) if err != nil { return nil, err } @@ -111,20 +82,12 @@ func New(cfg *ServerConfig) (*Server, error) { return nil, err } - var block *pem.Block - - block, _ = pem.Decode([]byte(data)) - pKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + pKey, err := utils.PemToRSAPrivateKey(data) if err != nil { return nil, err } - _, err = bc.AddOnion([]bulb.OnionPortSpec{ - bulb.OnionPortSpec{ - VirtPort: RPCPort, - Target: l.Addr().String(), - }, - }, pKey, false) + _, err = t.AddOnion(pKey, RPCPort, l.Addr().String()) if err != nil { return nil, err } @@ -137,13 +100,10 @@ func New(cfg *ServerConfig) (*Server, error) { } s := &Server{ - cfg: cfg, + cfg: &cfg, - db: db, - - torCon: bc, - torCmd: tcmd, - torControlPath: torControlPath, + db: db, + tor: t, rpcS: rpcs, rpcAddr: l.Addr().String(), @@ -189,11 +149,7 @@ func (s *Server) restore() error { return err } - ports := []bulb.OnionPortSpec{ - genPortSpec(80, "127.0.0.1:"+s.cfg.WebPort), - } - - _, err = s.torCon.AddOnion(ports, pKey, false) + _, err = s.tor.AddOnion(pKey, 80, "127.0.0.1:"+s.cfg.WebPort) if err != nil { return err } @@ -219,6 +175,7 @@ type RPCServer struct { *Server } +// AddHost adds a host to the server. func (rs *RPCServer) AddHost(req routerpc.AddHostRequest, resp *routerpc.AddHostResponse) error { if req.APIKey != "hunter2" { return errors.New("invalid api key") @@ -226,12 +183,13 @@ func (rs *RPCServer) AddHost(req routerpc.AddHostRequest, resp *routerpc.AddHost token := uuid.New() - oi, err := rs.torCon.AddOnion([]bulb.OnionPortSpec{ - bulb.OnionPortSpec{ - VirtPort: 80, - Target: "127.0.0.1:" + rs.cfg.WebPort, - }, - }, req.PrivKey, false) + var pKey *rsa.PrivateKey + + if kk, ok := req.PrivKey.(*rsa.PrivateKey); ok { + pKey = kk + } + + oi, err := rs.tor.AddOnion(pKey, 80, "127.0.0.1:"+rs.cfg.WebPort) if err != nil { return err } @@ -248,12 +206,6 @@ func (rs *RPCServer) AddHost(req routerpc.AddHostRequest, resp *routerpc.AddHost } rs.Server.ts.AddHost(resp.OnionHostname, token) - if oi.PrivateKey != nil { - resp.PrivKey = oi.PrivateKey - } else { - resp.PrivKey = req.PrivKey - } - err = rs.db.SaveRoute(resp) if err != nil { return err diff --git a/server/tor.go b/server/tor.go new file mode 100644 index 0000000..ef309b0 --- /dev/null +++ b/server/tor.go @@ -0,0 +1,80 @@ +package server + +import ( + "crypto/rsa" + "log" + "math/rand" + "strconv" + "time" + + "github.com/Yawning/bulb" + "github.com/sycamoreone/orc/tor" +) + +// TorConfig is a wrapper struct for tor configuration. +type TorConfig struct { + DataDir string + HashedControlPassword string + ClearPassword string + Timeout time.Duration +} + +// StartTor starts a new instance of tor or doesn't with the reason why. +func StartTor(cfg TorConfig) (*Tor, error) { + tc := tor.NewConfig() + tc.Set("DataDirectory", cfg.DataDir) + tc.Set("HashedControlPassword", cfg.HashedControlPassword) + tc.Set("SocksPort", "0") + cp := rand.Intn(64512) + tc.Set("ControlPort", cp) + tc.Timeout = cfg.Timeout + + tcmd, err := tor.NewCmd(tc) + if err != nil { + return nil, err + } + + err = tcmd.Start() + if err != nil { + return nil, err + } + + log.Println("tor started, sleeping for a few seconds for it to settle...") + time.Sleep(5 * time.Second) + + bc, err := bulb.Dial("tcp", "127.0.0.1:"+strconv.Itoa(cp)) + if err != nil { + return nil, err + } + + err = bc.Authenticate(cfg.ClearPassword) + if err != nil { + return nil, err + } + + t := &Tor{ + tc: tc, + tcmd: tcmd, + bc: bc, + } + + return t, nil +} + +// Tor is a higher level wrapper to a child tor process +type Tor struct { + tc *tor.Config + tcmd *tor.Cmd + bc *bulb.Conn +} + +// AddOnion adds an onion service to this machine with the given private key +// (can be nil for an auto-generated key), virtual onion port and TCP destunation. +func (t *Tor) AddOnion(pKey *rsa.PrivateKey, virtPort uint16, destination string) (*bulb.OnionInfo, error) { + return t.bc.AddOnion([]bulb.OnionPortSpec{ + { + VirtPort: virtPort, + Target: destination, + }, + }, pKey, false) +} diff --git a/utils/crypto.go b/utils/crypto.go new file mode 100644 index 0000000..0d26e82 --- /dev/null +++ b/utils/crypto.go @@ -0,0 +1,29 @@ +package utils + +import ( + "crypto/rsa" + "crypto/x509" + "encoding/pem" +) + +// RSAPrivateKeyToPem takes an RSA private key and formats it in PEM-encoded bytes. +func RSAPrivateKeyToPem(k *rsa.PrivateKey) []byte { + pemblock := &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(k), + } + return pem.EncodeToMemory(pemblock) +} + +// PemToRSAPrivateKey takes a PEM-encoded rsa private key and returns it. +func PemToRSAPrivateKey(data []byte) (*rsa.PrivateKey, error) { + var block *pem.Block + + block, _ = pem.Decode([]byte(data)) + pKey, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, err + } + + return pKey, nil +}