diff --git a/admin_listener.go b/admin_listener.go index 0a63845..224767b 100644 --- a/admin_listener.go +++ b/admin_listener.go @@ -1,33 +1,32 @@ package main -import( - "fmt" +import ( + "fmt" "net" ) - type AdminListener struct { - connChan chan(net.Conn) + connChan chan (net.Conn) } func NewAdminListener() *AdminListener { - connChan := make(chan(net.Conn)) - return &AdminListener{connChan} + connChan := make(chan (net.Conn)) + return &AdminListener{connChan} } // implement net.Listener func (l *AdminListener) Accept() (net.Conn, error) { - // TODO: error conditions? - conn := <-l.connChan - return conn, nil + // TODO: error conditions? + conn := <-l.connChan + return conn, nil } func (l *AdminListener) Close() error { - // TODO - fmt.Println("AdminListener Close") - return nil + // TODO + fmt.Println("AdminListener Close") + return nil } func (l *AdminListener) Addr() net.Addr { - // TODO - fmt.Println("AdminListener Addr") - return nil + // TODO + fmt.Println("AdminListener Addr") + return nil } diff --git a/auth.go b/auth.go index 9acf601..f25e553 100644 --- a/auth.go +++ b/auth.go @@ -1,156 +1,155 @@ package main import ( + "crypto/rand" + "encoding/json" + "errors" + "fmt" + "io/ioutil" "log" - "fmt" - "errors" - "crypto/rand" - "math/big" - "net/smtp" - "sync" - "time" - "io/ioutil" - "encoding/json" + "math/big" + "net/smtp" + "sync" + "time" ) - type Auth struct { - pendingRequests map[string]chan struct{} - sessions map[string]*Session - mutex *sync.Mutex + pendingRequests map[string]chan struct{} + sessions map[string]*Session + mutex *sync.Mutex } type Session struct { - Id string `json:"id"` + Id string `json:"id"` } func NewAuth() *Auth { - sessionsJson, err := ioutil.ReadFile("sessions.json") - if err != nil { - log.Println("failed reading sessions.json") - sessionsJson = []byte("{}") - } + sessionsJson, err := ioutil.ReadFile("sessions.json") + if err != nil { + log.Println("failed reading sessions.json") + sessionsJson = []byte("{}") + } - var sessions map[string]*Session + var sessions map[string]*Session - err = json.Unmarshal(sessionsJson, &sessions) - if err != nil { - log.Println(err) - sessions = make(map[string]*Session) - } + err = json.Unmarshal(sessionsJson, &sessions) + if err != nil { + log.Println(err) + sessions = make(map[string]*Session) + } - pendingRequests := make(map[string]chan struct{}) - mutex := &sync.Mutex{} + pendingRequests := make(map[string]chan struct{}) + mutex := &sync.Mutex{} - return &Auth{pendingRequests, sessions, mutex} + return &Auth{pendingRequests, sessions, mutex} } func (a *Auth) Authorized(token string) bool { - a.mutex.Lock() - _, exists := a.sessions[token] - a.mutex.Unlock() + a.mutex.Lock() + _, exists := a.sessions[token] + a.mutex.Unlock() - if exists { - return true - } + if exists { + return true + } - return false + return false } func (a *Auth) Login(email string, config *BoringProxyConfig, canceled <-chan struct{}) (string, error) { - key, err := genRandomKey() - if err != nil { - return "", errors.New("Error generating key") - } + key, err := genRandomKey() + if err != nil { + return "", errors.New("Error generating key") + } - link := fmt.Sprintf("https://%s/verify?key=%s", config.AdminDomain, key) + link := fmt.Sprintf("https://%s/verify?key=%s", config.AdminDomain, key) - bodyTemplate := "From: %s <%s>\r\n" + - "To: %s\r\n" + - "Subject: Email Verification\r\n" + - "\r\n" + - "This is an email verification request from %s. Please click the following link to complete the verification:\r\n" + - "\r\n" + - "%s\r\n" + bodyTemplate := "From: %s <%s>\r\n" + + "To: %s\r\n" + + "Subject: Email Verification\r\n" + + "\r\n" + + "This is an email verification request from %s. Please click the following link to complete the verification:\r\n" + + "\r\n" + + "%s\r\n" - fromText := "boringproxy email verifier" - fromEmail := fmt.Sprintf("auth@%s", config.AdminDomain) - emailBody := fmt.Sprintf(bodyTemplate, fromText, fromEmail, email, config.AdminDomain, link) + fromText := "boringproxy email verifier" + fromEmail := fmt.Sprintf("auth@%s", config.AdminDomain) + emailBody := fmt.Sprintf(bodyTemplate, fromText, fromEmail, email, config.AdminDomain, link) - emailAuth := smtp.PlainAuth("", config.Smtp.Username, config.Smtp.Password, config.Smtp.Server) - srv := fmt.Sprintf("%s:%d", config.Smtp.Server, config.Smtp.Port) - msg := []byte(emailBody) - err = smtp.SendMail(srv, emailAuth, fromEmail, []string{email}, msg) - if err != nil { - return "", errors.New("Sending email failed. Probably a bad email address.") - } + emailAuth := smtp.PlainAuth("", config.Smtp.Username, config.Smtp.Password, config.Smtp.Server) + srv := fmt.Sprintf("%s:%d", config.Smtp.Server, config.Smtp.Port) + msg := []byte(emailBody) + err = smtp.SendMail(srv, emailAuth, fromEmail, []string{email}, msg) + if err != nil { + return "", errors.New("Sending email failed. Probably a bad email address.") + } - doneChan := make(chan struct{}) + doneChan := make(chan struct{}) - a.mutex.Lock() - a.pendingRequests[key] = doneChan - a.mutex.Unlock() + a.mutex.Lock() + a.pendingRequests[key] = doneChan + a.mutex.Unlock() - // Starting timeout here after sending the email, because sometimes it takes - // a few seconds to send. - timeout := time.After(time.Duration(60) * time.Second) + // Starting timeout here after sending the email, because sometimes it takes + // a few seconds to send. + timeout := time.After(time.Duration(60) * time.Second) - select { - case <-doneChan: - token, err := genRandomKey() - if err != nil { - return "", errors.New("Error generating key") - } + select { + case <-doneChan: + token, err := genRandomKey() + if err != nil { + return "", errors.New("Error generating key") + } - a.mutex.Lock() - a.sessions[token] = &Session{Id: email} - a.mutex.Unlock() + a.mutex.Lock() + a.sessions[token] = &Session{Id: email} + a.mutex.Unlock() - saveJson(a.sessions, "sessions.json") + saveJson(a.sessions, "sessions.json") - return token, nil - case <-timeout: - a.mutex.Lock() - delete(a.pendingRequests, key) - a.mutex.Unlock() - return "", errors.New("Timeout") - case <-canceled: - a.mutex.Lock() - delete(a.pendingRequests, key) - a.mutex.Unlock() - } + return token, nil + case <-timeout: + a.mutex.Lock() + delete(a.pendingRequests, key) + a.mutex.Unlock() + return "", errors.New("Timeout") + case <-canceled: + a.mutex.Lock() + delete(a.pendingRequests, key) + a.mutex.Unlock() + } - return "", nil + return "", nil } func (a *Auth) Verify(key string) error { - a.mutex.Lock() - defer a.mutex.Unlock() + a.mutex.Lock() + defer a.mutex.Unlock() - doneChan, ok := a.pendingRequests[key] + doneChan, ok := a.pendingRequests[key] - if !ok { - return errors.New("No pending request for that key. It may have expired.") - } + if !ok { + return errors.New("No pending request for that key. It may have expired.") + } - delete(a.pendingRequests, key) - close(doneChan) + delete(a.pendingRequests, key) + close(doneChan) - return nil + return nil } +const chars string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" -const chars string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; func genRandomKey() (string, error) { - id := "" - for i := 0; i < 32; i++ { - randIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(chars)))) - if err != nil { - return "", err - } - id += string(chars[randIndex.Int64()]) - } - return id, nil + id := "" + for i := 0; i < 32; i++ { + randIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(chars)))) + if err != nil { + return "", err + } + id += string(chars[randIndex.Int64()]) + } + return id, nil } diff --git a/boringproxy.go b/boringproxy.go index 80d5001..3c6e0c3 100644 --- a/boringproxy.go +++ b/boringproxy.go @@ -1,300 +1,295 @@ package main import ( - "fmt" + "crypto/tls" + "encoding/json" + "fmt" + "github.com/caddyserver/certmagic" + "io" + "io/ioutil" "log" "net" "net/http" - "crypto/tls" - "io" - "sync" - "strconv" - "encoding/json" - "io/ioutil" - "github.com/caddyserver/certmagic" + "strconv" + "sync" ) - type BoringProxyConfig struct { - AdminDomain string `json:"admin_domain"` - Smtp *SmtpConfig `json:"smtp"` + AdminDomain string `json:"admin_domain"` + Smtp *SmtpConfig `json:"smtp"` } type SmtpConfig struct { - Server string - Port int - Username string - Password string + Server string + Port int + Username string + Password string } - type BoringProxy struct { - config *BoringProxyConfig - auth *Auth - tunMan *TunnelManager - adminListener *AdminListener - certConfig *certmagic.Config + config *BoringProxyConfig + auth *Auth + tunMan *TunnelManager + adminListener *AdminListener + certConfig *certmagic.Config } func NewBoringProxy() *BoringProxy { - config := &BoringProxyConfig{} + config := &BoringProxyConfig{} - configJson, err := ioutil.ReadFile("boringproxy_config.json") - if err != nil { - log.Println(err) - } + configJson, err := ioutil.ReadFile("boringproxy_config.json") + if err != nil { + log.Println(err) + } - err = json.Unmarshal(configJson, config) - if err != nil { - log.Println(err) - config = &BoringProxyConfig{} - } + err = json.Unmarshal(configJson, config) + if err != nil { + log.Println(err) + config = &BoringProxyConfig{} + } - //certmagic.DefaultACME.DisableHTTPChallenge = true - certmagic.DefaultACME.DisableTLSALPNChallenge = true - //certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA - certConfig := certmagic.NewDefault() + //certmagic.DefaultACME.DisableHTTPChallenge = true + certmagic.DefaultACME.DisableTLSALPNChallenge = true + //certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA + certConfig := certmagic.NewDefault() - tunMan := NewTunnelManager(certConfig) - adminListener := NewAdminListener() + tunMan := NewTunnelManager(certConfig) + adminListener := NewAdminListener() - err = certConfig.ManageSync([]string{config.AdminDomain}) - if err != nil { - log.Println("CertMagic error") - log.Println(err) - } + err = certConfig.ManageSync([]string{config.AdminDomain}) + if err != nil { + log.Println("CertMagic error") + log.Println(err) + } - auth := NewAuth() + auth := NewAuth() - - p := &BoringProxy{config, auth, tunMan, adminListener, certConfig} + p := &BoringProxy{config, auth, tunMan, adminListener, certConfig} http.HandleFunc("/", p.handleAdminRequest) - go http.Serve(adminListener, nil) + go http.Serve(adminListener, nil) - log.Println("BoringProxy ready") + log.Println("BoringProxy ready") - return p + return p } func (p *BoringProxy) Run() { - listener, err := net.Listen("tcp", ":443") - if err != nil { - log.Fatal(err) - } + listener, err := net.Listen("tcp", ":443") + if err != nil { + log.Fatal(err) + } - for { - conn, err := listener.Accept() - if err != nil { - log.Print(err) - continue - } - go p.handleConnection(conn) - } + for { + conn, err := listener.Accept() + if err != nil { + log.Print(err) + continue + } + go p.handleConnection(conn) + } } func (p *BoringProxy) handleAdminRequest(w http.ResponseWriter, r *http.Request) { - switch r.URL.Path { - case "/login": - p.handleLogin(w, r) - case "/verify": - p.handleVerify(w, r) - case "/tunnels": + switch r.URL.Path { + case "/login": + p.handleLogin(w, r) + case "/verify": + p.handleVerify(w, r) + case "/tunnels": - token, err := extractToken("access_token", r) - if err != nil { - w.WriteHeader(401) - w.Write([]byte("No token provided")) - return - } + token, err := extractToken("access_token", r) + if err != nil { + w.WriteHeader(401) + w.Write([]byte("No token provided")) + return + } - if !p.auth.Authorized(token) { - w.WriteHeader(403) - w.Write([]byte("Not authorized")) - return - } + if !p.auth.Authorized(token) { + w.WriteHeader(403) + w.Write([]byte("Not authorized")) + return + } - p.handleTunnels(w, r) - default: - w.WriteHeader(400) - w.Write([]byte("Invalid endpoint")) - return - } + p.handleTunnels(w, r) + default: + w.WriteHeader(400) + w.Write([]byte("Invalid endpoint")) + return + } } func (p *BoringProxy) handleTunnels(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query() + query := r.URL.Query() - if r.Method == "GET" { - body, err := json.Marshal(p.tunMan.tunnels) - if err != nil { - w.WriteHeader(500) - w.Write([]byte("Error encoding tunnels")) - return - } - w.Write([]byte(body)) - } else if r.Method == "POST" { - p.handleCreateTunnel(w, r) - } else if r.Method == "DELETE" { - if len(query["host"]) != 1 { - w.WriteHeader(400) - w.Write([]byte("Invalid host parameter")) - return - } - host := query["host"][0] + if r.Method == "GET" { + body, err := json.Marshal(p.tunMan.tunnels) + if err != nil { + w.WriteHeader(500) + w.Write([]byte("Error encoding tunnels")) + return + } + w.Write([]byte(body)) + } else if r.Method == "POST" { + p.handleCreateTunnel(w, r) + } else if r.Method == "DELETE" { + if len(query["host"]) != 1 { + w.WriteHeader(400) + w.Write([]byte("Invalid host parameter")) + return + } + host := query["host"][0] - p.tunMan.DeleteTunnel(host) - } + p.tunMan.DeleteTunnel(host) + } } func (p *BoringProxy) handleLogin(w http.ResponseWriter, r *http.Request) { - //io.WriteString(w, "login") - if r.Method != "POST" { - w.WriteHeader(405) - w.Write([]byte("Invalid method for login")) - return - } + //io.WriteString(w, "login") + if r.Method != "POST" { + w.WriteHeader(405) + w.Write([]byte("Invalid method for login")) + return + } - query := r.URL.Query() + query := r.URL.Query() - toEmail, ok := query["email"] + toEmail, ok := query["email"] - if !ok { - w.WriteHeader(400) - w.Write([]byte("Email required for login")) - return - } + if !ok { + w.WriteHeader(400) + w.Write([]byte("Email required for login")) + return + } - token, err := p.auth.Login(toEmail[0], p.config, r.Context().Done()) + token, err := p.auth.Login(toEmail[0], p.config, r.Context().Done()) - if err != nil { - w.WriteHeader(400) - //w.Write(err) - return - } + if err != nil { + w.WriteHeader(400) + //w.Write(err) + return + } - w.Write([]byte(token)) + w.Write([]byte(token)) } func (p *BoringProxy) handleVerify(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query() - key, exists := query["key"] + query := r.URL.Query() + key, exists := query["key"] - if !exists { - w.WriteHeader(400) - fmt.Fprintf(w, "Must provide key for verification") - return - } + if !exists { + w.WriteHeader(400) + fmt.Fprintf(w, "Must provide key for verification") + return + } - err := p.auth.Verify(key[0]) + err := p.auth.Verify(key[0]) - if err != nil { - w.WriteHeader(400) - fmt.Fprintf(w, "Invalid key") - return - } + if err != nil { + w.WriteHeader(400) + fmt.Fprintf(w, "Invalid key") + return + } - fmt.Fprintf(w, "Verification successful. You can close this tab and return to your original session.") + fmt.Fprintf(w, "Verification successful. You can close this tab and return to your original session.") } func (p *BoringProxy) handleCreateTunnel(w http.ResponseWriter, r *http.Request) { - query := r.URL.Query() + query := r.URL.Query() - if len(query["host"]) != 1 { - w.WriteHeader(400) - w.Write([]byte("Invalid host parameter")) - return - } - host := query["host"][0] + if len(query["host"]) != 1 { + w.WriteHeader(400) + w.Write([]byte("Invalid host parameter")) + return + } + host := query["host"][0] - if len(query["port"]) != 1 { - w.WriteHeader(400) - w.Write([]byte("Invalid port parameter")) - return - } + if len(query["port"]) != 1 { + w.WriteHeader(400) + w.Write([]byte("Invalid port parameter")) + return + } - port, err := strconv.Atoi(query["port"][0]) - if err != nil { - w.WriteHeader(400) - w.Write([]byte("Invalid port parameter")) - return - } + port, err := strconv.Atoi(query["port"][0]) + if err != nil { + w.WriteHeader(400) + w.Write([]byte("Invalid port parameter")) + return + } - p.tunMan.SetTunnel(host, port) + p.tunMan.SetTunnel(host, port) } func (p *BoringProxy) handleConnection(clientConn net.Conn) { - // TODO: does this need to be closed manually, or is it handled when decryptedConn is closed? - //defer clientConn.Close() + // TODO: does this need to be closed manually, or is it handled when decryptedConn is closed? + //defer clientConn.Close() - var serverName string + var serverName string - decryptedConn := tls.Server(clientConn, &tls.Config{ - GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { + decryptedConn := tls.Server(clientConn, &tls.Config{ + GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { - serverName = clientHello.ServerName + serverName = clientHello.ServerName - return p.certConfig.GetCertificate(clientHello) - }, - }) - //defer decryptedConn.Close() + return p.certConfig.GetCertificate(clientHello) + }, + }) + //defer decryptedConn.Close() - // Need to manually do handshake to ensure serverName is populated by this point. Usually Handshake() - // is automatically called on first read/write - decryptedConn.Handshake() + // Need to manually do handshake to ensure serverName is populated by this point. Usually Handshake() + // is automatically called on first read/write + decryptedConn.Handshake() - if serverName == p.config.AdminDomain { - p.handleAdminConnection(decryptedConn) - } else { - p.handleTunnelConnection(decryptedConn, serverName) - } + if serverName == p.config.AdminDomain { + p.handleAdminConnection(decryptedConn) + } else { + p.handleTunnelConnection(decryptedConn, serverName) + } } func (p *BoringProxy) handleAdminConnection(decryptedConn net.Conn) { - p.adminListener.connChan <- decryptedConn + p.adminListener.connChan <- decryptedConn } func (p *BoringProxy) handleTunnelConnection(decryptedConn net.Conn, serverName string) { - defer decryptedConn.Close() + defer decryptedConn.Close() - port, err := p.tunMan.GetPort(serverName) - if err != nil { - log.Print(err) - errMessage := fmt.Sprintf("HTTP/1.1 500 Internal server error\n\nNo tunnel attached to %s", serverName) - decryptedConn.Write([]byte(errMessage)) - return - } + port, err := p.tunMan.GetPort(serverName) + if err != nil { + log.Print(err) + errMessage := fmt.Sprintf("HTTP/1.1 500 Internal server error\n\nNo tunnel attached to %s", serverName) + decryptedConn.Write([]byte(errMessage)) + return + } - upstreamAddr := fmt.Sprintf("127.0.0.1:%d", port) + upstreamAddr := fmt.Sprintf("127.0.0.1:%d", port) - upstreamConn, err := net.Dial("tcp", upstreamAddr) - if err != nil { - log.Print(err) - return - } - defer upstreamConn.Close() + upstreamConn, err := net.Dial("tcp", upstreamAddr) + if err != nil { + log.Print(err) + return + } + defer upstreamConn.Close() - var wg sync.WaitGroup - wg.Add(2) + var wg sync.WaitGroup + wg.Add(2) - go func() { - io.Copy(decryptedConn, upstreamConn) - //decryptedConn.(*net.TCPConn).CloseWrite() - wg.Done() - }() - go func() { - io.Copy(upstreamConn, decryptedConn) - //upstreamConn.(*net.TCPConn).CloseWrite() - wg.Done() - }() + go func() { + io.Copy(decryptedConn, upstreamConn) + //decryptedConn.(*net.TCPConn).CloseWrite() + wg.Done() + }() + go func() { + io.Copy(upstreamConn, decryptedConn) + //upstreamConn.(*net.TCPConn).CloseWrite() + wg.Done() + }() - wg.Wait() + wg.Wait() } - - diff --git a/main.go b/main.go index eb8bccb..aa72178 100644 --- a/main.go +++ b/main.go @@ -1,13 +1,12 @@ package main import ( - "log" + "log" ) - func main() { - log.Println("Starting up") + log.Println("Starting up") - proxy := NewBoringProxy() - proxy.Run() + proxy := NewBoringProxy() + proxy.Run() } diff --git a/tunnel_manager.go b/tunnel_manager.go index 9d9c756..6949d70 100644 --- a/tunnel_manager.go +++ b/tunnel_manager.go @@ -1,88 +1,87 @@ package main import ( - "log" - "errors" - "sync" - "encoding/json" - "io/ioutil" - "github.com/caddyserver/certmagic" + "encoding/json" + "errors" + "github.com/caddyserver/certmagic" + "io/ioutil" + "log" + "sync" ) - type Tunnel struct { - Port int `json:"port"` + Port int `json:"port"` } type Tunnels map[string]*Tunnel func NewTunnels() Tunnels { - return make(map[string]*Tunnel) + return make(map[string]*Tunnel) } type TunnelManager struct { - tunnels Tunnels - mutex *sync.Mutex - certConfig *certmagic.Config + tunnels Tunnels + mutex *sync.Mutex + certConfig *certmagic.Config } func NewTunnelManager(certConfig *certmagic.Config) *TunnelManager { - tunnelsJson, err := ioutil.ReadFile("tunnels.json") - if err != nil { - log.Println("failed reading tunnels.json") - tunnelsJson = []byte("{}") - } + tunnelsJson, err := ioutil.ReadFile("tunnels.json") + if err != nil { + log.Println("failed reading tunnels.json") + tunnelsJson = []byte("{}") + } - var tunnels Tunnels + var tunnels Tunnels - err = json.Unmarshal(tunnelsJson, &tunnels) - if err != nil { - log.Println(err) - tunnels = NewTunnels() - } + err = json.Unmarshal(tunnelsJson, &tunnels) + if err != nil { + log.Println(err) + tunnels = NewTunnels() + } - for domainName := range tunnels { - err = certConfig.ManageSync([]string{domainName}) - if err != nil { - log.Println("CertMagic error at startup") - log.Println(err) - } - } + for domainName := range tunnels { + err = certConfig.ManageSync([]string{domainName}) + if err != nil { + log.Println("CertMagic error at startup") + log.Println(err) + } + } - mutex := &sync.Mutex{} - return &TunnelManager{tunnels, mutex, certConfig} + mutex := &sync.Mutex{} + return &TunnelManager{tunnels, mutex, certConfig} } func (m *TunnelManager) SetTunnel(host string, port int) { - err := m.certConfig.ManageSync([]string{host}) - if err != nil { - log.Println("CertMagic error") - log.Println(err) - } + err := m.certConfig.ManageSync([]string{host}) + if err != nil { + log.Println("CertMagic error") + log.Println(err) + } - tunnel := &Tunnel{port} - m.mutex.Lock() - m.tunnels[host] = tunnel - saveJson(m.tunnels, "tunnels.json") - m.mutex.Unlock() + tunnel := &Tunnel{port} + m.mutex.Lock() + m.tunnels[host] = tunnel + saveJson(m.tunnels, "tunnels.json") + m.mutex.Unlock() } func (m *TunnelManager) DeleteTunnel(host string) { - m.mutex.Lock() - delete(m.tunnels, host) - saveJson(m.tunnels, "tunnels.json") - m.mutex.Unlock() + m.mutex.Lock() + delete(m.tunnels, host) + saveJson(m.tunnels, "tunnels.json") + m.mutex.Unlock() } func (m *TunnelManager) GetPort(serverName string) (int, error) { - m.mutex.Lock() - tunnel, exists := m.tunnels[serverName] - m.mutex.Unlock() + m.mutex.Lock() + tunnel, exists := m.tunnels[serverName] + m.mutex.Unlock() - if !exists { - return 0, errors.New("Doesn't exist") - } + if !exists { + return 0, errors.New("Doesn't exist") + } - return tunnel.Port, nil + return tunnel.Port, nil } diff --git a/utils.go b/utils.go index f7b1fe7..e6a3ec0 100644 --- a/utils.go +++ b/utils.go @@ -1,28 +1,26 @@ package main import ( - "io/ioutil" + "encoding/json" + "errors" + "io/ioutil" "net/http" - "errors" - "strings" - "encoding/json" + "strings" ) - func saveJson(data interface{}, filePath string) error { - jsonStr, err := json.MarshalIndent(data, "", " ") - if err != nil { - return errors.New("Error serializing JSON") - } else { - err := ioutil.WriteFile(filePath, jsonStr, 0644) - if err != nil { - return errors.New("Error saving JSON") - } - } - return nil + jsonStr, err := json.MarshalIndent(data, "", " ") + if err != nil { + return errors.New("Error serializing JSON") + } else { + err := ioutil.WriteFile(filePath, jsonStr, 0644) + if err != nil { + return errors.New("Error saving JSON") + } + } + return nil } - // Looks for auth token in cookie, then header, then query string func extractToken(tokenName string, r *http.Request) (string, error) { tokenCookie, err := r.Cookie(tokenName)