mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 17:50:12 +00:00
116 lines
3.2 KiB
Go
116 lines
3.2 KiB
Go
package rpc
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/goccy/go-json"
|
|
|
|
"github.com/hashicorp/go-uuid"
|
|
"github.com/nezhahq/nezha/model"
|
|
"github.com/nezhahq/nezha/pkg/utils"
|
|
"github.com/nezhahq/nezha/proto"
|
|
serviceRPC "github.com/nezhahq/nezha/service/rpc"
|
|
"github.com/nezhahq/nezha/service/singleton"
|
|
)
|
|
|
|
func ServeNAT(w http.ResponseWriter, r *http.Request, natConfig *model.NAT) {
|
|
capabilityLease, capabilityErr := prepareNATCapability(r, natConfig)
|
|
if capabilityErr != nil {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
w.Write([]byte("NAT capability unavailable"))
|
|
return
|
|
}
|
|
handler := serviceRPC.NezhaHandlerSingleton
|
|
streamId := ""
|
|
legacyStreamOwned := false
|
|
relayCompleted := false
|
|
defer func() {
|
|
if capabilityLease.active {
|
|
if !relayCompleted {
|
|
capabilityLease.cleanup(handler)
|
|
}
|
|
return
|
|
}
|
|
if legacyStreamOwned {
|
|
_ = handler.CloseStream(streamId)
|
|
}
|
|
}()
|
|
server, _ := singleton.ServerShared.Get(natConfig.ServerID)
|
|
if server == nil {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
w.Write([]byte("server not found or not connected"))
|
|
return
|
|
}
|
|
if server.GetTaskStream() == nil {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
w.Write([]byte("server not found or not connected"))
|
|
return
|
|
}
|
|
|
|
streamId, err := uuid.GenerateUUID()
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
w.Write(fmt.Appendf(nil, "stream id error: %v", err))
|
|
return
|
|
}
|
|
if capabilityLease.active {
|
|
capabilityLease.streamLease, err = handler.CreateAgentCompatNATStream(capabilityLease.handle, streamId)
|
|
} else {
|
|
err = handler.CreateStream(streamId, 0, server.ID)
|
|
}
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusTooManyRequests)
|
|
w.Write(fmt.Appendf(nil, "stream limit: %v", err))
|
|
return
|
|
}
|
|
legacyStreamOwned = !capabilityLease.active
|
|
taskData, err := json.Marshal(model.TaskNAT{StreamID: streamId, Host: natConfig.Host})
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
w.Write(fmt.Appendf(nil, "task data error: %v", err))
|
|
return
|
|
}
|
|
|
|
if err := server.SendTask(&proto.Task{Type: model.TaskTypeNAT, Data: string(taskData)}); err != nil {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
w.Write(fmt.Appendf(nil, "send task error: %v", err))
|
|
return
|
|
}
|
|
|
|
// Authorization authenticates Dashboard access before NAT ingress; it must not become an origin credential for the configured NAT backend.
|
|
wWrapped, err := utils.NewRequestWrapper(r, w)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if err := handler.UserConnected(streamId, wWrapped); err != nil {
|
|
if closeErr := wWrapped.Close(); closeErr != nil {
|
|
log.Printf("NEZHA>> NAT request wrapper close error after user connection failure: %v", closeErr)
|
|
}
|
|
return
|
|
}
|
|
|
|
if capabilityLease.active {
|
|
if err := handler.PublishAgentCompatNATStream(capabilityLease.handle, serviceRPC.AgentCompatNATPublication{
|
|
Purpose: serviceRPC.AgentCompatCapabilityNAT,
|
|
TargetServerID: server.ID,
|
|
ResourceID: natConfig.ID,
|
|
StreamID: streamId,
|
|
}); err != nil {
|
|
return
|
|
}
|
|
}
|
|
if capabilityLease.active {
|
|
capabilityLease.publicationOwned, err = handler.StartAgentCompatNATStream(capabilityLease.handle, time.Second*10)
|
|
if err != nil {
|
|
return
|
|
}
|
|
} else if err := handler.StartStream(streamId, time.Second*10); err != nil {
|
|
return
|
|
}
|
|
relayCompleted = true
|
|
}
|