Run go fmt

This commit is contained in:
Anders Pitman 2020-09-29 20:12:54 -06:00
parent 17b37ab2ed
commit 58bd38befd
6 changed files with 388 additions and 399 deletions

View File

@ -1,33 +1,32 @@
package main package main
import( import (
"fmt" "fmt"
"net" "net"
) )
type AdminListener struct { type AdminListener struct {
connChan chan(net.Conn) connChan chan (net.Conn)
} }
func NewAdminListener() *AdminListener { func NewAdminListener() *AdminListener {
connChan := make(chan(net.Conn)) connChan := make(chan (net.Conn))
return &AdminListener{connChan} return &AdminListener{connChan}
} }
// implement net.Listener // implement net.Listener
func (l *AdminListener) Accept() (net.Conn, error) { func (l *AdminListener) Accept() (net.Conn, error) {
// TODO: error conditions? // TODO: error conditions?
conn := <-l.connChan conn := <-l.connChan
return conn, nil return conn, nil
} }
func (l *AdminListener) Close() error { func (l *AdminListener) Close() error {
// TODO // TODO
fmt.Println("AdminListener Close") fmt.Println("AdminListener Close")
return nil return nil
} }
func (l *AdminListener) Addr() net.Addr { func (l *AdminListener) Addr() net.Addr {
// TODO // TODO
fmt.Println("AdminListener Addr") fmt.Println("AdminListener Addr")
return nil return nil
} }

209
auth.go
View File

@ -1,156 +1,155 @@
package main package main
import ( import (
"crypto/rand"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log" "log"
"fmt" "math/big"
"errors" "net/smtp"
"crypto/rand" "sync"
"math/big" "time"
"net/smtp"
"sync"
"time"
"io/ioutil"
"encoding/json"
) )
type Auth struct { type Auth struct {
pendingRequests map[string]chan struct{} pendingRequests map[string]chan struct{}
sessions map[string]*Session sessions map[string]*Session
mutex *sync.Mutex mutex *sync.Mutex
} }
type Session struct { type Session struct {
Id string `json:"id"` Id string `json:"id"`
} }
func NewAuth() *Auth { func NewAuth() *Auth {
sessionsJson, err := ioutil.ReadFile("sessions.json") sessionsJson, err := ioutil.ReadFile("sessions.json")
if err != nil { if err != nil {
log.Println("failed reading sessions.json") log.Println("failed reading sessions.json")
sessionsJson = []byte("{}") sessionsJson = []byte("{}")
} }
var sessions map[string]*Session var sessions map[string]*Session
err = json.Unmarshal(sessionsJson, &sessions) err = json.Unmarshal(sessionsJson, &sessions)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
sessions = make(map[string]*Session) sessions = make(map[string]*Session)
} }
pendingRequests := make(map[string]chan struct{}) pendingRequests := make(map[string]chan struct{})
mutex := &sync.Mutex{} mutex := &sync.Mutex{}
return &Auth{pendingRequests, sessions, mutex} return &Auth{pendingRequests, sessions, mutex}
} }
func (a *Auth) Authorized(token string) bool { func (a *Auth) Authorized(token string) bool {
a.mutex.Lock() a.mutex.Lock()
_, exists := a.sessions[token] _, exists := a.sessions[token]
a.mutex.Unlock() a.mutex.Unlock()
if exists { if exists {
return true return true
} }
return false return false
} }
func (a *Auth) Login(email string, config *BoringProxyConfig, canceled <-chan struct{}) (string, error) { func (a *Auth) Login(email string, config *BoringProxyConfig, canceled <-chan struct{}) (string, error) {
key, err := genRandomKey() key, err := genRandomKey()
if err != nil { if err != nil {
return "", errors.New("Error generating key") 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" + bodyTemplate := "From: %s <%s>\r\n" +
"To: %s\r\n" + "To: %s\r\n" +
"Subject: Email Verification\r\n" + "Subject: Email Verification\r\n" +
"\r\n" + "\r\n" +
"This is an email verification request from %s. Please click the following link to complete the verification:\r\n" + "This is an email verification request from %s. Please click the following link to complete the verification:\r\n" +
"\r\n" + "\r\n" +
"%s\r\n" "%s\r\n"
fromText := "boringproxy email verifier" fromText := "boringproxy email verifier"
fromEmail := fmt.Sprintf("auth@%s", config.AdminDomain) fromEmail := fmt.Sprintf("auth@%s", config.AdminDomain)
emailBody := fmt.Sprintf(bodyTemplate, fromText, fromEmail, email, config.AdminDomain, link) emailBody := fmt.Sprintf(bodyTemplate, fromText, fromEmail, email, config.AdminDomain, link)
emailAuth := smtp.PlainAuth("", config.Smtp.Username, config.Smtp.Password, config.Smtp.Server) emailAuth := smtp.PlainAuth("", config.Smtp.Username, config.Smtp.Password, config.Smtp.Server)
srv := fmt.Sprintf("%s:%d", config.Smtp.Server, config.Smtp.Port) srv := fmt.Sprintf("%s:%d", config.Smtp.Server, config.Smtp.Port)
msg := []byte(emailBody) msg := []byte(emailBody)
err = smtp.SendMail(srv, emailAuth, fromEmail, []string{email}, msg) err = smtp.SendMail(srv, emailAuth, fromEmail, []string{email}, msg)
if err != nil { if err != nil {
return "", errors.New("Sending email failed. Probably a bad email address.") return "", errors.New("Sending email failed. Probably a bad email address.")
} }
doneChan := make(chan struct{}) doneChan := make(chan struct{})
a.mutex.Lock() a.mutex.Lock()
a.pendingRequests[key] = doneChan a.pendingRequests[key] = doneChan
a.mutex.Unlock() a.mutex.Unlock()
// Starting timeout here after sending the email, because sometimes it takes // Starting timeout here after sending the email, because sometimes it takes
// a few seconds to send. // a few seconds to send.
timeout := time.After(time.Duration(60) * time.Second) timeout := time.After(time.Duration(60) * time.Second)
select { select {
case <-doneChan: case <-doneChan:
token, err := genRandomKey() token, err := genRandomKey()
if err != nil { if err != nil {
return "", errors.New("Error generating key") return "", errors.New("Error generating key")
} }
a.mutex.Lock() a.mutex.Lock()
a.sessions[token] = &Session{Id: email} a.sessions[token] = &Session{Id: email}
a.mutex.Unlock() a.mutex.Unlock()
saveJson(a.sessions, "sessions.json") saveJson(a.sessions, "sessions.json")
return token, nil return token, nil
case <-timeout: case <-timeout:
a.mutex.Lock() a.mutex.Lock()
delete(a.pendingRequests, key) delete(a.pendingRequests, key)
a.mutex.Unlock() a.mutex.Unlock()
return "", errors.New("Timeout") return "", errors.New("Timeout")
case <-canceled: case <-canceled:
a.mutex.Lock() a.mutex.Lock()
delete(a.pendingRequests, key) delete(a.pendingRequests, key)
a.mutex.Unlock() a.mutex.Unlock()
} }
return "", nil return "", nil
} }
func (a *Auth) Verify(key string) error { func (a *Auth) Verify(key string) error {
a.mutex.Lock() a.mutex.Lock()
defer a.mutex.Unlock() defer a.mutex.Unlock()
doneChan, ok := a.pendingRequests[key] doneChan, ok := a.pendingRequests[key]
if !ok { if !ok {
return errors.New("No pending request for that key. It may have expired.") return errors.New("No pending request for that key. It may have expired.")
} }
delete(a.pendingRequests, key) delete(a.pendingRequests, key)
close(doneChan) close(doneChan)
return nil return nil
} }
const chars string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const chars string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
func genRandomKey() (string, error) { func genRandomKey() (string, error) {
id := "" id := ""
for i := 0; i < 32; i++ { for i := 0; i < 32; i++ {
randIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(chars)))) randIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(chars))))
if err != nil { if err != nil {
return "", err return "", err
} }
id += string(chars[randIndex.Int64()]) id += string(chars[randIndex.Int64()])
} }
return id, nil return id, nil
} }

View File

@ -1,300 +1,295 @@
package main package main
import ( import (
"fmt" "crypto/tls"
"encoding/json"
"fmt"
"github.com/caddyserver/certmagic"
"io"
"io/ioutil"
"log" "log"
"net" "net"
"net/http" "net/http"
"crypto/tls" "strconv"
"io" "sync"
"sync"
"strconv"
"encoding/json"
"io/ioutil"
"github.com/caddyserver/certmagic"
) )
type BoringProxyConfig struct { type BoringProxyConfig struct {
AdminDomain string `json:"admin_domain"` AdminDomain string `json:"admin_domain"`
Smtp *SmtpConfig `json:"smtp"` Smtp *SmtpConfig `json:"smtp"`
} }
type SmtpConfig struct { type SmtpConfig struct {
Server string Server string
Port int Port int
Username string Username string
Password string Password string
} }
type BoringProxy struct { type BoringProxy struct {
config *BoringProxyConfig config *BoringProxyConfig
auth *Auth auth *Auth
tunMan *TunnelManager tunMan *TunnelManager
adminListener *AdminListener adminListener *AdminListener
certConfig *certmagic.Config certConfig *certmagic.Config
} }
func NewBoringProxy() *BoringProxy { func NewBoringProxy() *BoringProxy {
config := &BoringProxyConfig{} config := &BoringProxyConfig{}
configJson, err := ioutil.ReadFile("boringproxy_config.json") configJson, err := ioutil.ReadFile("boringproxy_config.json")
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }
err = json.Unmarshal(configJson, config) err = json.Unmarshal(configJson, config)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
config = &BoringProxyConfig{} config = &BoringProxyConfig{}
} }
//certmagic.DefaultACME.DisableHTTPChallenge = true //certmagic.DefaultACME.DisableHTTPChallenge = true
certmagic.DefaultACME.DisableTLSALPNChallenge = true certmagic.DefaultACME.DisableTLSALPNChallenge = true
//certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA //certmagic.DefaultACME.CA = certmagic.LetsEncryptStagingCA
certConfig := certmagic.NewDefault() certConfig := certmagic.NewDefault()
tunMan := NewTunnelManager(certConfig) tunMan := NewTunnelManager(certConfig)
adminListener := NewAdminListener() adminListener := NewAdminListener()
err = certConfig.ManageSync([]string{config.AdminDomain}) err = certConfig.ManageSync([]string{config.AdminDomain})
if err != nil { if err != nil {
log.Println("CertMagic error") log.Println("CertMagic error")
log.Println(err) 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) 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() { func (p *BoringProxy) Run() {
listener, err := net.Listen("tcp", ":443") listener, err := net.Listen("tcp", ":443")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
for { for {
conn, err := listener.Accept() conn, err := listener.Accept()
if err != nil { if err != nil {
log.Print(err) log.Print(err)
continue continue
} }
go p.handleConnection(conn) go p.handleConnection(conn)
} }
} }
func (p *BoringProxy) handleAdminRequest(w http.ResponseWriter, r *http.Request) { func (p *BoringProxy) handleAdminRequest(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path { switch r.URL.Path {
case "/login": case "/login":
p.handleLogin(w, r) p.handleLogin(w, r)
case "/verify": case "/verify":
p.handleVerify(w, r) p.handleVerify(w, r)
case "/tunnels": case "/tunnels":
token, err := extractToken("access_token", r) token, err := extractToken("access_token", r)
if err != nil { if err != nil {
w.WriteHeader(401) w.WriteHeader(401)
w.Write([]byte("No token provided")) w.Write([]byte("No token provided"))
return return
} }
if !p.auth.Authorized(token) { if !p.auth.Authorized(token) {
w.WriteHeader(403) w.WriteHeader(403)
w.Write([]byte("Not authorized")) w.Write([]byte("Not authorized"))
return return
} }
p.handleTunnels(w, r) p.handleTunnels(w, r)
default: default:
w.WriteHeader(400) w.WriteHeader(400)
w.Write([]byte("Invalid endpoint")) w.Write([]byte("Invalid endpoint"))
return return
} }
} }
func (p *BoringProxy) handleTunnels(w http.ResponseWriter, r *http.Request) { func (p *BoringProxy) handleTunnels(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() query := r.URL.Query()
if r.Method == "GET" { if r.Method == "GET" {
body, err := json.Marshal(p.tunMan.tunnels) body, err := json.Marshal(p.tunMan.tunnels)
if err != nil { if err != nil {
w.WriteHeader(500) w.WriteHeader(500)
w.Write([]byte("Error encoding tunnels")) w.Write([]byte("Error encoding tunnels"))
return return
} }
w.Write([]byte(body)) w.Write([]byte(body))
} else if r.Method == "POST" { } else if r.Method == "POST" {
p.handleCreateTunnel(w, r) p.handleCreateTunnel(w, r)
} else if r.Method == "DELETE" { } else if r.Method == "DELETE" {
if len(query["host"]) != 1 { if len(query["host"]) != 1 {
w.WriteHeader(400) w.WriteHeader(400)
w.Write([]byte("Invalid host parameter")) w.Write([]byte("Invalid host parameter"))
return return
} }
host := query["host"][0] host := query["host"][0]
p.tunMan.DeleteTunnel(host) p.tunMan.DeleteTunnel(host)
} }
} }
func (p *BoringProxy) handleLogin(w http.ResponseWriter, r *http.Request) { func (p *BoringProxy) handleLogin(w http.ResponseWriter, r *http.Request) {
//io.WriteString(w, "login") //io.WriteString(w, "login")
if r.Method != "POST" { if r.Method != "POST" {
w.WriteHeader(405) w.WriteHeader(405)
w.Write([]byte("Invalid method for login")) w.Write([]byte("Invalid method for login"))
return return
} }
query := r.URL.Query() query := r.URL.Query()
toEmail, ok := query["email"] toEmail, ok := query["email"]
if !ok { if !ok {
w.WriteHeader(400) w.WriteHeader(400)
w.Write([]byte("Email required for login")) w.Write([]byte("Email required for login"))
return 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 { if err != nil {
w.WriteHeader(400) w.WriteHeader(400)
//w.Write(err) //w.Write(err)
return return
} }
w.Write([]byte(token)) w.Write([]byte(token))
} }
func (p *BoringProxy) handleVerify(w http.ResponseWriter, r *http.Request) { func (p *BoringProxy) handleVerify(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() query := r.URL.Query()
key, exists := query["key"] key, exists := query["key"]
if !exists { if !exists {
w.WriteHeader(400) w.WriteHeader(400)
fmt.Fprintf(w, "Must provide key for verification") fmt.Fprintf(w, "Must provide key for verification")
return return
} }
err := p.auth.Verify(key[0]) err := p.auth.Verify(key[0])
if err != nil { if err != nil {
w.WriteHeader(400) w.WriteHeader(400)
fmt.Fprintf(w, "Invalid key") fmt.Fprintf(w, "Invalid key")
return 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) { func (p *BoringProxy) handleCreateTunnel(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() query := r.URL.Query()
if len(query["host"]) != 1 { if len(query["host"]) != 1 {
w.WriteHeader(400) w.WriteHeader(400)
w.Write([]byte("Invalid host parameter")) w.Write([]byte("Invalid host parameter"))
return return
} }
host := query["host"][0] host := query["host"][0]
if len(query["port"]) != 1 { if len(query["port"]) != 1 {
w.WriteHeader(400) w.WriteHeader(400)
w.Write([]byte("Invalid port parameter")) w.Write([]byte("Invalid port parameter"))
return return
} }
port, err := strconv.Atoi(query["port"][0]) port, err := strconv.Atoi(query["port"][0])
if err != nil { if err != nil {
w.WriteHeader(400) w.WriteHeader(400)
w.Write([]byte("Invalid port parameter")) w.Write([]byte("Invalid port parameter"))
return return
} }
p.tunMan.SetTunnel(host, port) p.tunMan.SetTunnel(host, port)
} }
func (p *BoringProxy) handleConnection(clientConn net.Conn) { func (p *BoringProxy) handleConnection(clientConn net.Conn) {
// TODO: does this need to be closed manually, or is it handled when decryptedConn is closed? // TODO: does this need to be closed manually, or is it handled when decryptedConn is closed?
//defer clientConn.Close() //defer clientConn.Close()
var serverName string var serverName string
decryptedConn := tls.Server(clientConn, &tls.Config{ decryptedConn := tls.Server(clientConn, &tls.Config{
GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { GetCertificate: func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
serverName = clientHello.ServerName serverName = clientHello.ServerName
return p.certConfig.GetCertificate(clientHello) return p.certConfig.GetCertificate(clientHello)
}, },
}) })
//defer decryptedConn.Close() //defer decryptedConn.Close()
// Need to manually do handshake to ensure serverName is populated by this point. Usually Handshake() // Need to manually do handshake to ensure serverName is populated by this point. Usually Handshake()
// is automatically called on first read/write // is automatically called on first read/write
decryptedConn.Handshake() decryptedConn.Handshake()
if serverName == p.config.AdminDomain { if serverName == p.config.AdminDomain {
p.handleAdminConnection(decryptedConn) p.handleAdminConnection(decryptedConn)
} else { } else {
p.handleTunnelConnection(decryptedConn, serverName) p.handleTunnelConnection(decryptedConn, serverName)
} }
} }
func (p *BoringProxy) handleAdminConnection(decryptedConn net.Conn) { func (p *BoringProxy) handleAdminConnection(decryptedConn net.Conn) {
p.adminListener.connChan <- decryptedConn p.adminListener.connChan <- decryptedConn
} }
func (p *BoringProxy) handleTunnelConnection(decryptedConn net.Conn, serverName string) { func (p *BoringProxy) handleTunnelConnection(decryptedConn net.Conn, serverName string) {
defer decryptedConn.Close() defer decryptedConn.Close()
port, err := p.tunMan.GetPort(serverName) port, err := p.tunMan.GetPort(serverName)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
errMessage := fmt.Sprintf("HTTP/1.1 500 Internal server error\n\nNo tunnel attached to %s", serverName) errMessage := fmt.Sprintf("HTTP/1.1 500 Internal server error\n\nNo tunnel attached to %s", serverName)
decryptedConn.Write([]byte(errMessage)) decryptedConn.Write([]byte(errMessage))
return 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) upstreamConn, err := net.Dial("tcp", upstreamAddr)
if err != nil { if err != nil {
log.Print(err) log.Print(err)
return return
} }
defer upstreamConn.Close() defer upstreamConn.Close()
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(2) wg.Add(2)
go func() { go func() {
io.Copy(decryptedConn, upstreamConn) io.Copy(decryptedConn, upstreamConn)
//decryptedConn.(*net.TCPConn).CloseWrite() //decryptedConn.(*net.TCPConn).CloseWrite()
wg.Done() wg.Done()
}() }()
go func() { go func() {
io.Copy(upstreamConn, decryptedConn) io.Copy(upstreamConn, decryptedConn)
//upstreamConn.(*net.TCPConn).CloseWrite() //upstreamConn.(*net.TCPConn).CloseWrite()
wg.Done() wg.Done()
}() }()
wg.Wait() wg.Wait()
} }

View File

@ -1,13 +1,12 @@
package main package main
import ( import (
"log" "log"
) )
func main() { func main() {
log.Println("Starting up") log.Println("Starting up")
proxy := NewBoringProxy() proxy := NewBoringProxy()
proxy.Run() proxy.Run()
} }

View File

@ -1,88 +1,87 @@
package main package main
import ( import (
"log" "encoding/json"
"errors" "errors"
"sync" "github.com/caddyserver/certmagic"
"encoding/json" "io/ioutil"
"io/ioutil" "log"
"github.com/caddyserver/certmagic" "sync"
) )
type Tunnel struct { type Tunnel struct {
Port int `json:"port"` Port int `json:"port"`
} }
type Tunnels map[string]*Tunnel type Tunnels map[string]*Tunnel
func NewTunnels() Tunnels { func NewTunnels() Tunnels {
return make(map[string]*Tunnel) return make(map[string]*Tunnel)
} }
type TunnelManager struct { type TunnelManager struct {
tunnels Tunnels tunnels Tunnels
mutex *sync.Mutex mutex *sync.Mutex
certConfig *certmagic.Config certConfig *certmagic.Config
} }
func NewTunnelManager(certConfig *certmagic.Config) *TunnelManager { func NewTunnelManager(certConfig *certmagic.Config) *TunnelManager {
tunnelsJson, err := ioutil.ReadFile("tunnels.json") tunnelsJson, err := ioutil.ReadFile("tunnels.json")
if err != nil { if err != nil {
log.Println("failed reading tunnels.json") log.Println("failed reading tunnels.json")
tunnelsJson = []byte("{}") tunnelsJson = []byte("{}")
} }
var tunnels Tunnels var tunnels Tunnels
err = json.Unmarshal(tunnelsJson, &tunnels) err = json.Unmarshal(tunnelsJson, &tunnels)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
tunnels = NewTunnels() tunnels = NewTunnels()
} }
for domainName := range tunnels { for domainName := range tunnels {
err = certConfig.ManageSync([]string{domainName}) err = certConfig.ManageSync([]string{domainName})
if err != nil { if err != nil {
log.Println("CertMagic error at startup") log.Println("CertMagic error at startup")
log.Println(err) log.Println(err)
} }
} }
mutex := &sync.Mutex{} mutex := &sync.Mutex{}
return &TunnelManager{tunnels, mutex, certConfig} return &TunnelManager{tunnels, mutex, certConfig}
} }
func (m *TunnelManager) SetTunnel(host string, port int) { func (m *TunnelManager) SetTunnel(host string, port int) {
err := m.certConfig.ManageSync([]string{host}) err := m.certConfig.ManageSync([]string{host})
if err != nil { if err != nil {
log.Println("CertMagic error") log.Println("CertMagic error")
log.Println(err) log.Println(err)
} }
tunnel := &Tunnel{port} tunnel := &Tunnel{port}
m.mutex.Lock() m.mutex.Lock()
m.tunnels[host] = tunnel m.tunnels[host] = tunnel
saveJson(m.tunnels, "tunnels.json") saveJson(m.tunnels, "tunnels.json")
m.mutex.Unlock() m.mutex.Unlock()
} }
func (m *TunnelManager) DeleteTunnel(host string) { func (m *TunnelManager) DeleteTunnel(host string) {
m.mutex.Lock() m.mutex.Lock()
delete(m.tunnels, host) delete(m.tunnels, host)
saveJson(m.tunnels, "tunnels.json") saveJson(m.tunnels, "tunnels.json")
m.mutex.Unlock() m.mutex.Unlock()
} }
func (m *TunnelManager) GetPort(serverName string) (int, error) { func (m *TunnelManager) GetPort(serverName string) (int, error) {
m.mutex.Lock() m.mutex.Lock()
tunnel, exists := m.tunnels[serverName] tunnel, exists := m.tunnels[serverName]
m.mutex.Unlock() m.mutex.Unlock()
if !exists { if !exists {
return 0, errors.New("Doesn't exist") return 0, errors.New("Doesn't exist")
} }
return tunnel.Port, nil return tunnel.Port, nil
} }

View File

@ -1,28 +1,26 @@
package main package main
import ( import (
"io/ioutil" "encoding/json"
"errors"
"io/ioutil"
"net/http" "net/http"
"errors" "strings"
"strings"
"encoding/json"
) )
func saveJson(data interface{}, filePath string) error { func saveJson(data interface{}, filePath string) error {
jsonStr, err := json.MarshalIndent(data, "", " ") jsonStr, err := json.MarshalIndent(data, "", " ")
if err != nil { if err != nil {
return errors.New("Error serializing JSON") return errors.New("Error serializing JSON")
} else { } else {
err := ioutil.WriteFile(filePath, jsonStr, 0644) err := ioutil.WriteFile(filePath, jsonStr, 0644)
if err != nil { if err != nil {
return errors.New("Error saving JSON") return errors.New("Error saving JSON")
} }
} }
return nil return nil
} }
// Looks for auth token in cookie, then header, then query string // Looks for auth token in cookie, then header, then query string
func extractToken(tokenName string, r *http.Request) (string, error) { func extractToken(tokenName string, r *http.Request) (string, error) {
tokenCookie, err := r.Cookie(tokenName) tokenCookie, err := r.Cookie(tokenName)