Fixed memory leaking

+ Patch on memory leaking for Windows netstat module (do not effect any of the previous non Windows builds)
+ Fixed potential memory leak in acme handler logic
+ Added "do you want to get a TLS certificate for this subdomain?" dialog when a new subdomain proxy rule is created
This commit is contained in:
Toby Chui 2023-07-26 19:17:43 +08:00
parent 5038429a70
commit 52d3b2f8c2
14 changed files with 101 additions and 77 deletions

View File

@ -66,6 +66,7 @@ func acmeRegisterSpecialRoutingRule() {
} }
resBody, err := ioutil.ReadAll(res.Body) resBody, err := ioutil.ReadAll(res.Body)
defer res.Body.Close()
if err != nil { if err != nil {
fmt.Printf("error reading: %s\n", err) fmt.Printf("error reading: %s\n", err)
return return

View File

@ -3,6 +3,7 @@ package main
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"net/http/pprof"
"imuslab.com/zoraxy/mod/acme/acmewizard" "imuslab.com/zoraxy/mod/acme/acmewizard"
"imuslab.com/zoraxy/mod/auth" "imuslab.com/zoraxy/mod/auth"
@ -166,6 +167,9 @@ func initAPIs() {
http.HandleFunc("/api/conf/export", ExportConfigAsZip) http.HandleFunc("/api/conf/export", ExportConfigAsZip)
http.HandleFunc("/api/conf/import", ImportConfigFromZip) http.HandleFunc("/api/conf/import", ImportConfigFromZip)
//Debug
authRouter.HandleFunc("/api/info/pprof", pprof.Index)
//If you got APIs to add, append them here //If you got APIs to add, append them here
} }

View File

@ -1,39 +0,0 @@
package main
import (
"net"
"net/http"
"strings"
"github.com/oschwald/geoip2-golang"
)
func getCountryCodeFromRequest(r *http.Request) string {
countryCode := ""
// Get the IP address of the user from the request headers
ipAddress := r.Header.Get("X-Forwarded-For")
if ipAddress == "" {
ipAddress = strings.Split(r.RemoteAddr, ":")[0]
}
// Open the GeoIP database
db, err := geoip2.Open("./tmp/GeoIP2-Country.mmdb")
if err != nil {
// Handle the error
return countryCode
}
defer db.Close()
// Look up the country code for the IP address
record, err := db.Country(net.ParseIP(ipAddress))
if err != nil {
// Handle the error
return countryCode
}
// Get the ISO country code from the record
countryCode = record.Country.IsoCode
return countryCode
}

View File

@ -41,7 +41,7 @@ var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)") var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)")
var ( var (
name = "Zoraxy" name = "Zoraxy"
version = "2.6.5" version = "2.6.6"
nodeUUID = "generic" nodeUUID = "generic"
development = false //Set this to false to use embedded web fs development = false //Set this to false to use embedded web fs
bootTime = time.Now().Unix() bootTime = time.Now().Unix()

View File

@ -14,10 +14,6 @@ import (
var onExitFlushLoop func() var onExitFlushLoop func()
const (
defaultTimeout = time.Minute * 5
)
// ReverseProxy is an HTTP Handler that takes an incoming request and // ReverseProxy is an HTTP Handler that takes an incoming request and
// sends it to another server, proxying the response back to the // sends it to another server, proxying the response back to the
// client, support http, also support https tunnel using http.hijacker // client, support http, also support https tunnel using http.hijacker

View File

@ -183,6 +183,5 @@ func (h *ProxyHandler) logRequest(r *http.Request, succ bool, statusCode int, fo
} }
h.Parent.Option.StatisticCollector.RecordRequest(requestInfo) h.Parent.Option.StatisticCollector.RecordRequest(requestInfo)
}() }()
} }
} }

View File

@ -213,6 +213,7 @@ func GetNetworkInterfaceStats() (int64, int64, error) {
out, err := cmd.Output() out, err := cmd.Output()
if err != nil { if err != nil {
callbackChan <- wmicResult{0, 0, err} callbackChan <- wmicResult{0, 0, err}
return
} }
//Filter out the first line //Filter out the first line
@ -251,18 +252,16 @@ func GetNetworkInterfaceStats() (int64, int64, error) {
go func() { go func() {
//Spawn a timer to terminate the cmd process if timeout //Spawn a timer to terminate the cmd process if timeout
var timer *time.Timer time.Sleep(3 * time.Second)
timer = time.AfterFunc(3*time.Second, func() { if cmd != nil && cmd.Process != nil {
timer.Stop() cmd.Process.Kill()
if cmd != nil && cmd.Process != nil {
cmd.Process.Kill()
}
callbackChan <- wmicResult{0, 0, errors.New("wmic execution timeout")} callbackChan <- wmicResult{0, 0, errors.New("wmic execution timeout")}
}) }
}() }()
result := wmicResult{} result := wmicResult{}
result = <-callbackChan result = <-callbackChan
cmd = nil
if result.Err != nil { if result.Err != nil {
log.Println("Unable to extract NIC info from wmic: " + result.Err.Error()) log.Println("Unable to extract NIC info from wmic: " + result.Err.Error())
} }

View File

@ -93,8 +93,6 @@ func (m *Monitor) ExecuteUptimeCheck() {
Latency: laterncy, Latency: laterncy,
} }
//fmt.Println(thisRecord)
} else { } else {
log.Println("Unknown protocol: " + target.Protocol + ". Skipping") log.Println("Unknown protocol: " + target.Protocol + ". Skipping")
continue continue
@ -238,9 +236,11 @@ func getWebsiteStatus(url string) (int, error) {
} }
return 0, err return 0, err
} }
defer resp.Body.Close()
status_code := resp.StatusCode
return status_code, nil
} }
defer resp.Body.Close()
status_code := resp.StatusCode status_code := resp.StatusCode
resp.Body.Close()
return status_code, nil return status_code, nil
} }

View File

@ -1,10 +1,7 @@
package utils package utils
import ( import (
"bufio"
"encoding/base64"
"errors" "errors"
"io"
"log" "log"
"net/http" "net/http"
"os" "os"
@ -131,17 +128,6 @@ func TimeToString(targetTime time.Time) string {
return targetTime.Format("2006-01-02 15:04:05") return targetTime.Format("2006-01-02 15:04:05")
} }
func LoadImageAsBase64(filepath string) (string, error) {
if !FileExists(filepath) {
return "", errors.New("File not exists")
}
f, _ := os.Open(filepath)
reader := bufio.NewReader(f)
content, _ := io.ReadAll(reader)
encoded := base64.StdEncoding.EncodeToString(content)
return string(encoded), nil
}
// Use for redirections // Use for redirections
func ConstructRelativePathFromRequestURL(requestURI string, redirectionLocation string) string { func ConstructRelativePathFromRequestURL(requestURI string, redirectionLocation string) string {
if strings.Count(requestURI, "/") == 1 { if strings.Count(requestURI, "/") == 1 {

View File

@ -128,7 +128,8 @@ func startupSequence() {
BuildVersion: version, BuildVersion: version,
}, "") }, "")
if err != nil { if err != nil {
panic(err) log.Println("Unable to startup mDNS service.")
log.Fatal(err)
} }
//Start initial scanning //Start initial scanning

View File

@ -308,7 +308,7 @@
<div class="ui message"> <div class="ui message">
<i class="ui info circle icon"></i> IP Address support the following formats <i class="ui info circle icon"></i> IP Address support the following formats
<div class="ui bulleted list"> <div class="ui bulleted list">
<div class="item">Fixed IP Address (e.g. 192.128.4.100)</div> <div class="item">Fixed IP Address (e.g. 192.128.4.100 or fe80::210:5aff:feaa:20a2)</div>
<div class="item">IP Wildcard (e.g. 172.164.*.*)</div> <div class="item">IP Wildcard (e.g. 172.164.*.*)</div>
<div class="item">CIDR String (e.g. 128.32.0.1/16)</div> <div class="item">CIDR String (e.g. 128.32.0.1/16)</div>
</div> </div>
@ -625,7 +625,7 @@
<div class="ui message"> <div class="ui message">
<i class="ui info circle icon"></i> IP Address support the following formats <i class="ui info circle icon"></i> IP Address support the following formats
<div class="ui bulleted list"> <div class="ui bulleted list">
<div class="item">Fixed IP Address (e.g. 192.128.4.100)</div> <div class="item">Fixed IP Address (e.g. 192.128.4.100 or fe80::210:5aff:feaa:20a2)</div>
<div class="item">IP Wildcard (e.g. 172.164.*.*)</div> <div class="item">IP Wildcard (e.g. 172.164.*.*)</div>
<div class="item">CIDR String (e.g. 128.32.0.1/16)</div> <div class="item">CIDR String (e.g. 128.32.0.1/16)</div>
</div> </div>

View File

@ -172,13 +172,27 @@
//OK //OK
listVdirs(); listVdirs();
listSubd(); listSubd();
msgbox("Proxy Endpoint Added");
//Clear old data //Clear old data
$("#rootname").val(""); $("#rootname").val("");
$("#proxyDomain").val(""); $("#proxyDomain").val("");
credentials = []; credentials = [];
updateTable(); updateTable();
//Check if it is a new subdomain and TLS enabled
if (type == "subd" && $("#tls").checkbox("is checked")){
confirmBox("Request new SSL Cert for this subdomain?", function(choice){
if (choice == true){
//Get a new cert using ACME
msgbox("Requesting certificate via Let's Encrypt...");
console.log("Trying to get a new certificate via ACME");
obtainCertificate(rootname);
}
});
}else{
msgbox("Proxy Endpoint Added");
}
} }
} }
}); });
@ -437,4 +451,67 @@
})); }));
showSideWrapper("snippet/basicAuthEditor.html?t=" + Date.now() + "#" + payload); showSideWrapper("snippet/basicAuthEditor.html?t=" + Date.now() + "#" + payload);
} }
/*
Obtain Certificate via ACME
*/
//Load the ACME email from server side
let acmeEmail = "";
$.get("/api/acme/autoRenew/email", function(data){
if (data != "" && data != undefined && data != null){
acmeEmail = data;
}
});
// Obtain certificate from API, only support one domain
function obtainCertificate(domains) {
let filename = "";
let email = acmeEmail;
if (acmeEmail == ""){
let rootDomain = domains.split(".").pop();
email = "admin@" + rootDomain;
}
if (filename.trim() == "" && !domains.includes(",")){
//Zoraxy filename are the matching name for domains.
//Use the same as domains
filename = domains;
}else if (filename != "" && !domains.includes(",")){
//Invalid settings. Force the filename to be same as domain
//if there are only 1 domain
filename = domains;
}else{
parent.msgbox("Filename cannot be empty for certs containing multiple domains.")
return;
}
$.ajax({
url: "/api/acme/obtainCert",
method: "GET",
data: {
domains: domains,
filename: filename,
email: email,
ca: "Let's Encrypt",
},
success: function(response) {
if (response.error) {
console.log("Error:", response.error);
// Show error message
msgbox(response.error, false, 12000);
} else {
console.log("Certificate installed successfully");
// Show success message
msgbox("Certificate installed successfully");
// Renew the parent certificate list
initManagedDomainCertificateList();
}
},
error: function(error) {
console.log("Failed to install certificate:", error);
}
});
}
</script> </script>

View File

@ -116,7 +116,7 @@ body{
} }
#confirmBox .ui.progress .bar{ #confirmBox .ui.progress .bar{
background: #ffe32b !important; background: #a9d1f3 !important;
} }
#confirmBox .confirmBoxBody .button{ #confirmBox .confirmBoxBody .button{

View File

@ -83,8 +83,8 @@
</div> </div>
</div> </div>
<div class="ui divider"></div> <div class="ui divider"></div>
<h3>Manual Renew</h3> <h3>Generate New Certificate</h3>
<p>Pick a certificate below to force renew</p> <p>Enter a new / existing domain(s) to request new certificate(s)</p>
<div class="ui form"> <div class="ui form">
<div class="field"> <div class="field">
<label>Domain(s)</label> <label>Domain(s)</label>