2019-12-08 16:59:58 +08:00
|
|
|
|
package rpc
|
2019-12-07 18:14:40 +08:00
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2021-01-13 22:30:28 +08:00
|
|
|
|
"fmt"
|
2024-02-24 21:10:27 +08:00
|
|
|
|
"log"
|
2024-07-28 13:59:58 +08:00
|
|
|
|
"net"
|
2024-07-14 12:47:36 +08:00
|
|
|
|
"sync"
|
2020-10-24 21:29:05 +08:00
|
|
|
|
"time"
|
2019-12-07 18:14:40 +08:00
|
|
|
|
|
2024-12-05 00:11:34 +08:00
|
|
|
|
"github.com/jinzhu/copier"
|
2024-11-28 19:38:54 +08:00
|
|
|
|
"github.com/nezhahq/nezha/pkg/ddns"
|
|
|
|
|
geoipx "github.com/nezhahq/nezha/pkg/geoip"
|
|
|
|
|
"github.com/nezhahq/nezha/pkg/grpcx"
|
2024-07-14 12:47:36 +08:00
|
|
|
|
|
2024-11-28 19:38:54 +08:00
|
|
|
|
"github.com/nezhahq/nezha/model"
|
|
|
|
|
pb "github.com/nezhahq/nezha/proto"
|
|
|
|
|
"github.com/nezhahq/nezha/service/singleton"
|
2019-12-07 18:14:40 +08:00
|
|
|
|
)
|
|
|
|
|
|
2024-11-23 12:43:02 +08:00
|
|
|
|
var _ pb.NezhaServiceServer = (*NezhaHandler)(nil)
|
|
|
|
|
|
2024-07-14 12:47:36 +08:00
|
|
|
|
var NezhaHandlerSingleton *NezhaHandler
|
|
|
|
|
|
2019-12-07 18:14:40 +08:00
|
|
|
|
type NezhaHandler struct {
|
2024-07-14 12:47:36 +08:00
|
|
|
|
Auth *authHandler
|
|
|
|
|
ioStreams map[string]*ioStreamContext
|
|
|
|
|
ioStreamMutex *sync.RWMutex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewNezhaHandler() *NezhaHandler {
|
|
|
|
|
return &NezhaHandler{
|
|
|
|
|
Auth: &authHandler{},
|
|
|
|
|
ioStreamMutex: new(sync.RWMutex),
|
|
|
|
|
ioStreams: make(map[string]*ioStreamContext),
|
|
|
|
|
}
|
2019-12-07 18:14:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-05 00:11:34 +08:00
|
|
|
|
func (s *NezhaHandler) RequestTask(stream pb.NezhaService_RequestTaskServer) error {
|
2021-01-08 21:04:50 +08:00
|
|
|
|
var clientID uint64
|
2019-12-09 18:14:31 +08:00
|
|
|
|
var err error
|
|
|
|
|
if clientID, err = s.Auth.Check(stream.Context()); err != nil {
|
2019-12-07 18:14:40 +08:00
|
|
|
|
return err
|
|
|
|
|
}
|
2024-12-05 00:11:34 +08:00
|
|
|
|
|
2022-01-09 11:54:14 +08:00
|
|
|
|
singleton.ServerLock.RLock()
|
|
|
|
|
singleton.ServerList[clientID].TaskStream = stream
|
|
|
|
|
singleton.ServerLock.RUnlock()
|
2024-12-05 00:11:34 +08:00
|
|
|
|
|
|
|
|
|
var result *pb.TaskResult
|
|
|
|
|
for {
|
|
|
|
|
result, err = stream.Recv()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("NEZHA>> RequestTask error: %v, clientID: %d\n", err, clientID)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if result.GetType() == model.TaskTypeCommand {
|
|
|
|
|
// 处理上报的计划任务
|
|
|
|
|
singleton.CronLock.RLock()
|
|
|
|
|
cr := singleton.Crons[result.GetId()]
|
|
|
|
|
singleton.CronLock.RUnlock()
|
|
|
|
|
if cr != nil {
|
|
|
|
|
// 保存当前服务器状态信息
|
|
|
|
|
var curServer model.Server
|
|
|
|
|
singleton.ServerLock.RLock()
|
|
|
|
|
copier.Copy(&curServer, singleton.ServerList[clientID])
|
|
|
|
|
singleton.ServerLock.RUnlock()
|
|
|
|
|
if cr.PushSuccessful && result.GetSuccessful() {
|
|
|
|
|
singleton.SendNotification(cr.NotificationGroupID, fmt.Sprintf("[%s] %s, %s\n%s", singleton.Localizer.T("Scheduled Task Executed Successfully"),
|
|
|
|
|
cr.Name, singleton.ServerList[clientID].Name, result.GetData()), nil, &curServer)
|
|
|
|
|
}
|
|
|
|
|
if !result.GetSuccessful() {
|
|
|
|
|
singleton.SendNotification(cr.NotificationGroupID, fmt.Sprintf("[%s] %s, %s\n%s", singleton.Localizer.T("Scheduled Task Executed Failed"),
|
|
|
|
|
cr.Name, singleton.ServerList[clientID].Name, result.GetData()), nil, &curServer)
|
|
|
|
|
}
|
|
|
|
|
singleton.DB.Model(cr).Updates(model.Cron{
|
|
|
|
|
LastExecutedAt: time.Now().Add(time.Second * -1 * time.Duration(result.GetDelay())),
|
|
|
|
|
LastResult: result.GetSuccessful(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
} else if model.IsServiceSentinelNeeded(result.GetType()) {
|
|
|
|
|
singleton.ServiceSentinelShared.Dispatch(singleton.ReportData{
|
|
|
|
|
Data: result,
|
|
|
|
|
Reporter: clientID,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-07 18:14:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-11-23 12:43:02 +08:00
|
|
|
|
func (s *NezhaHandler) ReportSystemState(stream pb.NezhaService_ReportSystemStateServer) error {
|
2021-01-16 00:45:49 +08:00
|
|
|
|
var err error
|
2024-11-23 12:43:02 +08:00
|
|
|
|
var clientID uint64
|
|
|
|
|
if clientID, err = s.Auth.Check(stream.Context()); err != nil {
|
|
|
|
|
return err
|
2021-01-16 00:45:49 +08:00
|
|
|
|
}
|
2024-11-23 12:43:02 +08:00
|
|
|
|
var state *pb.State
|
|
|
|
|
for {
|
|
|
|
|
state, err = stream.Recv()
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("NEZHA>> ReportSystemState eror: %v, clientID: %d\n", err, clientID)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
state := model.PB2State(state)
|
|
|
|
|
|
|
|
|
|
singleton.ServerLock.RLock()
|
2024-12-13 22:27:30 +08:00
|
|
|
|
|
|
|
|
|
if singleton.ServerList[clientID] == nil {
|
|
|
|
|
singleton.ServerLock.RUnlock()
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-23 12:43:02 +08:00
|
|
|
|
singleton.ServerList[clientID].LastActive = time.Now()
|
|
|
|
|
singleton.ServerList[clientID].State = &state
|
|
|
|
|
// 应对 dashboard 重启的情况,如果从未记录过,先打点,等到小时时间点时入库
|
|
|
|
|
if singleton.ServerList[clientID].PrevTransferInSnapshot == 0 || singleton.ServerList[clientID].PrevTransferOutSnapshot == 0 {
|
|
|
|
|
singleton.ServerList[clientID].PrevTransferInSnapshot = int64(state.NetInTransfer)
|
|
|
|
|
singleton.ServerList[clientID].PrevTransferOutSnapshot = int64(state.NetOutTransfer)
|
|
|
|
|
}
|
|
|
|
|
singleton.ServerLock.RUnlock()
|
2021-07-14 23:53:37 +08:00
|
|
|
|
|
2024-11-23 12:43:02 +08:00
|
|
|
|
stream.Send(&pb.Receipt{Proced: true})
|
2021-07-14 23:53:37 +08:00
|
|
|
|
}
|
2021-01-16 00:45:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-07 11:06:42 +08:00
|
|
|
|
func (s *NezhaHandler) onReportSystemInfo(c context.Context, r *pb.Host) error {
|
2021-01-08 21:04:50 +08:00
|
|
|
|
var clientID uint64
|
2019-12-09 18:14:31 +08:00
|
|
|
|
var err error
|
|
|
|
|
if clientID, err = s.Auth.Check(c); err != nil {
|
2024-12-07 11:06:42 +08:00
|
|
|
|
return err
|
2019-12-07 18:14:40 +08:00
|
|
|
|
}
|
2019-12-13 17:56:14 +08:00
|
|
|
|
host := model.PB2Host(r)
|
2022-01-09 11:54:14 +08:00
|
|
|
|
singleton.ServerLock.RLock()
|
|
|
|
|
defer singleton.ServerLock.RUnlock()
|
2024-02-24 21:10:27 +08:00
|
|
|
|
|
2024-08-11 10:35:19 +08:00
|
|
|
|
/**
|
|
|
|
|
* 这里的 singleton 中的数据都是关机前的旧数据
|
|
|
|
|
* 当 agent 重启时,bootTime 变大,agent 端会先上报 host 信息,然后上报 state 信息
|
|
|
|
|
* 这是可以借助上报顺序的空档,将停机前的流量统计数据标记下来,加到下一个小时的数据点上
|
|
|
|
|
*/
|
|
|
|
|
if singleton.ServerList[clientID].Host != nil && singleton.ServerList[clientID].Host.BootTime < host.BootTime {
|
|
|
|
|
singleton.ServerList[clientID].PrevTransferInSnapshot = singleton.ServerList[clientID].PrevTransferInSnapshot - int64(singleton.ServerList[clientID].State.NetInTransfer)
|
|
|
|
|
singleton.ServerList[clientID].PrevTransferOutSnapshot = singleton.ServerList[clientID].PrevTransferOutSnapshot - int64(singleton.ServerList[clientID].State.NetOutTransfer)
|
2021-07-16 18:09:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-01-09 11:54:14 +08:00
|
|
|
|
singleton.ServerList[clientID].Host = &host
|
2024-12-07 11:06:42 +08:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Receipt, error) {
|
|
|
|
|
s.onReportSystemInfo(c, r)
|
2019-12-07 18:14:40 +08:00
|
|
|
|
return &pb.Receipt{Proced: true}, nil
|
|
|
|
|
}
|
2024-07-14 12:47:36 +08:00
|
|
|
|
|
2024-12-07 12:19:48 +08:00
|
|
|
|
func (s *NezhaHandler) ReportSystemInfo2(c context.Context, r *pb.Host) (*pb.Uint64Receipt, error) {
|
2024-12-07 11:06:42 +08:00
|
|
|
|
s.onReportSystemInfo(c, r)
|
2024-12-07 12:19:48 +08:00
|
|
|
|
return &pb.Uint64Receipt{Data: singleton.DashboardBootTime}, nil
|
2024-12-07 11:06:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-14 12:47:36 +08:00
|
|
|
|
func (s *NezhaHandler) IOStream(stream pb.NezhaService_IOStreamServer) error {
|
|
|
|
|
if _, err := s.Auth.Check(stream.Context()); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
id, err := stream.Recv()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-12-05 00:11:34 +08:00
|
|
|
|
|
|
|
|
|
// ff05ff05 是 Nezha 的魔数,用于标识流 ID
|
2024-07-14 12:47:36 +08:00
|
|
|
|
if id == nil || len(id.Data) < 4 || (id.Data[0] != 0xff && id.Data[1] != 0x05 && id.Data[2] != 0xff && id.Data[3] == 0x05) {
|
|
|
|
|
return fmt.Errorf("invalid stream id")
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-05 00:11:34 +08:00
|
|
|
|
go func() {
|
|
|
|
|
for {
|
|
|
|
|
if err := stream.Send(&pb.IOStreamData{Data: []byte{}}); err != nil {
|
|
|
|
|
log.Printf("NEZHA>> IOStream keepAlive error: %v\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(time.Second * 30)
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
2024-07-14 12:47:36 +08:00
|
|
|
|
streamId := string(id.Data[4:])
|
|
|
|
|
|
|
|
|
|
if _, err := s.GetStream(streamId); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
iw := grpcx.NewIOStreamWrapper(stream)
|
|
|
|
|
if err := s.AgentConnected(streamId, iw); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
iw.Wait()
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2024-07-28 13:59:58 +08:00
|
|
|
|
|
2024-11-22 22:40:43 +08:00
|
|
|
|
func (s *NezhaHandler) ReportGeoIP(c context.Context, r *pb.GeoIP) (*pb.GeoIP, error) {
|
2024-07-28 13:59:58 +08:00
|
|
|
|
var clientID uint64
|
|
|
|
|
var err error
|
|
|
|
|
if clientID, err = s.Auth.Check(c); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-22 22:40:43 +08:00
|
|
|
|
geoip := model.PB2GeoIP(r)
|
|
|
|
|
joinedIP := geoip.IP.Join()
|
|
|
|
|
use6 := r.GetUse6()
|
|
|
|
|
|
|
|
|
|
singleton.ServerLock.RLock()
|
|
|
|
|
// 检查并更新DDNS
|
|
|
|
|
if singleton.ServerList[clientID].EnableDDNS && joinedIP != "" &&
|
|
|
|
|
(singleton.ServerList[clientID].GeoIP == nil || singleton.ServerList[clientID].GeoIP.IP != geoip.IP) {
|
|
|
|
|
ipv4 := geoip.IP.IPv4Addr
|
|
|
|
|
ipv6 := geoip.IP.IPv6Addr
|
|
|
|
|
providers, err := singleton.GetDDNSProvidersFromProfiles(singleton.ServerList[clientID].DDNSProfiles, &ddns.IP{Ipv4Addr: ipv4, Ipv6Addr: ipv6})
|
|
|
|
|
if err == nil {
|
|
|
|
|
for _, provider := range providers {
|
|
|
|
|
go func(provider *ddns.Provider) {
|
|
|
|
|
provider.UpdateDomain(context.Background())
|
|
|
|
|
}(provider)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
log.Printf("NEZHA>> 获取DDNS配置时发生错误: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 发送IP变动通知
|
|
|
|
|
if singleton.ServerList[clientID].GeoIP != nil && singleton.Conf.EnableIPChangeNotification &&
|
|
|
|
|
((singleton.Conf.Cover == model.ConfigCoverAll && !singleton.Conf.IgnoredIPNotificationServerIDs[clientID]) ||
|
|
|
|
|
(singleton.Conf.Cover == model.ConfigCoverIgnoreAll && singleton.Conf.IgnoredIPNotificationServerIDs[clientID])) &&
|
|
|
|
|
singleton.ServerList[clientID].GeoIP.IP.Join() != "" &&
|
|
|
|
|
joinedIP != "" &&
|
|
|
|
|
singleton.ServerList[clientID].GeoIP.IP != geoip.IP {
|
|
|
|
|
|
|
|
|
|
singleton.SendNotification(singleton.Conf.IPChangeNotificationGroupID,
|
|
|
|
|
fmt.Sprintf(
|
|
|
|
|
"[%s] %s, %s => %s",
|
|
|
|
|
singleton.Localizer.T("IP Changed"),
|
|
|
|
|
singleton.ServerList[clientID].Name, singleton.IPDesensitize(singleton.ServerList[clientID].GeoIP.IP.Join()),
|
|
|
|
|
singleton.IPDesensitize(joinedIP),
|
|
|
|
|
),
|
|
|
|
|
nil)
|
|
|
|
|
}
|
|
|
|
|
singleton.ServerLock.RUnlock()
|
|
|
|
|
|
2024-07-28 13:59:58 +08:00
|
|
|
|
// 根据内置数据库查询 IP 地理位置
|
2024-11-22 22:40:43 +08:00
|
|
|
|
var ip string
|
|
|
|
|
if geoip.IP.IPv6Addr != "" && (use6 || geoip.IP.IPv4Addr == "") {
|
|
|
|
|
ip = geoip.IP.IPv6Addr
|
|
|
|
|
} else {
|
|
|
|
|
ip = geoip.IP.IPv4Addr
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-28 13:59:58 +08:00
|
|
|
|
netIP := net.ParseIP(ip)
|
2024-11-22 22:40:43 +08:00
|
|
|
|
location, err := geoipx.Lookup(netIP)
|
2024-07-28 13:59:58 +08:00
|
|
|
|
if err != nil {
|
2024-11-22 22:40:43 +08:00
|
|
|
|
log.Printf("NEZHA>> geoip.Lookup: %v", err)
|
2024-07-28 13:59:58 +08:00
|
|
|
|
}
|
2024-11-22 22:40:43 +08:00
|
|
|
|
geoip.CountryCode = location
|
2024-07-28 13:59:58 +08:00
|
|
|
|
|
|
|
|
|
// 将地区码写入到 Host
|
2024-11-22 22:40:43 +08:00
|
|
|
|
singleton.ServerLock.Lock()
|
|
|
|
|
defer singleton.ServerLock.Unlock()
|
|
|
|
|
singleton.ServerList[clientID].GeoIP = &geoip
|
2024-07-28 13:59:58 +08:00
|
|
|
|
|
2024-11-23 12:43:02 +08:00
|
|
|
|
return &pb.GeoIP{Ip: nil, CountryCode: location}, nil
|
2024-07-28 13:59:58 +08:00
|
|
|
|
}
|