mirror of
https://github.com/boringproxy/boringproxy.git
synced 2025-02-25 18:55:29 -06:00
Run go fmt
This commit is contained in:
parent
17b37ab2ed
commit
58bd38befd
@ -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
|
||||
}
|
||||
|
209
auth.go
209
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
|
||||
}
|
||||
|
405
boringproxy.go
405
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()
|
||||
}
|
||||
|
||||
|
||||
|
9
main.go
9
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()
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
30
utils.go
30
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)
|
||||
|
Loading…
Reference in New Issue
Block a user