2019-12-08 03:59:58 -05:00
|
|
|
|
package rpc
|
2019-12-07 05:14:40 -05:00
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2021-01-13 09:30:28 -05:00
|
|
|
|
"fmt"
|
2024-02-24 08:10:27 -05:00
|
|
|
|
"log"
|
2024-07-28 01:59:58 -04:00
|
|
|
|
"net"
|
2024-07-14 00:47:36 -04:00
|
|
|
|
"sync"
|
2020-10-24 09:29:05 -04:00
|
|
|
|
"time"
|
2019-12-07 05:14:40 -05:00
|
|
|
|
|
2024-07-14 00:47:36 -04:00
|
|
|
|
"github.com/naiba/nezha/pkg/ddns"
|
2024-07-28 01:59:58 -04:00
|
|
|
|
"github.com/naiba/nezha/pkg/geoip"
|
2024-07-14 00:47:36 -04:00
|
|
|
|
"github.com/naiba/nezha/pkg/grpcx"
|
|
|
|
|
"github.com/naiba/nezha/pkg/utils"
|
|
|
|
|
|
2022-04-30 07:23:19 -04:00
|
|
|
|
"github.com/jinzhu/copier"
|
|
|
|
|
"github.com/nicksnyder/go-i18n/v2/i18n"
|
|
|
|
|
|
2020-11-10 21:07:45 -05:00
|
|
|
|
"github.com/naiba/nezha/model"
|
|
|
|
|
pb "github.com/naiba/nezha/proto"
|
2022-01-08 22:54:14 -05:00
|
|
|
|
"github.com/naiba/nezha/service/singleton"
|
2019-12-07 05:14:40 -05:00
|
|
|
|
)
|
|
|
|
|
|
2024-07-14 00:47:36 -04:00
|
|
|
|
var NezhaHandlerSingleton *NezhaHandler
|
|
|
|
|
|
2019-12-07 05:14:40 -05:00
|
|
|
|
type NezhaHandler struct {
|
2024-07-14 00:47:36 -04: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 05:14:40 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-15 11:45:49 -05:00
|
|
|
|
func (s *NezhaHandler) ReportTask(c context.Context, r *pb.TaskResult) (*pb.Receipt, error) {
|
2019-12-09 05:14:31 -05:00
|
|
|
|
var err error
|
2021-01-18 20:59:04 -05:00
|
|
|
|
var clientID uint64
|
|
|
|
|
if clientID, err = s.Auth.Check(c); err != nil {
|
2021-01-15 11:45:49 -05:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-09-27 09:18:09 -04:00
|
|
|
|
if r.GetType() == model.TaskTypeCommand {
|
2021-01-18 20:59:04 -05:00
|
|
|
|
// 处理上报的计划任务
|
2022-01-08 22:54:14 -05:00
|
|
|
|
singleton.CronLock.RLock()
|
|
|
|
|
defer singleton.CronLock.RUnlock()
|
|
|
|
|
cr := singleton.Crons[r.GetId()]
|
2021-01-23 02:32:04 -05:00
|
|
|
|
if cr != nil {
|
2022-01-08 22:54:14 -05:00
|
|
|
|
singleton.ServerLock.RLock()
|
|
|
|
|
defer singleton.ServerLock.RUnlock()
|
2022-04-18 07:59:42 -04:00
|
|
|
|
// 保存当前服务器状态信息
|
|
|
|
|
curServer := model.Server{}
|
|
|
|
|
copier.Copy(&curServer, singleton.ServerList[clientID])
|
2021-01-23 02:32:04 -05:00
|
|
|
|
if cr.PushSuccessful && r.GetSuccessful() {
|
2022-04-30 07:23:19 -04:00
|
|
|
|
singleton.SendNotification(cr.NotificationTag, fmt.Sprintf("[%s] %s, %s\n%s", singleton.Localizer.MustLocalize(
|
|
|
|
|
&i18n.LocalizeConfig{
|
|
|
|
|
MessageID: "ScheduledTaskExecutedSuccessfully",
|
|
|
|
|
},
|
2022-10-31 09:54:37 -04:00
|
|
|
|
), cr.Name, singleton.ServerList[clientID].Name, r.GetData()), nil, &curServer)
|
2021-01-23 02:32:04 -05:00
|
|
|
|
}
|
|
|
|
|
if !r.GetSuccessful() {
|
2022-04-30 07:23:19 -04:00
|
|
|
|
singleton.SendNotification(cr.NotificationTag, fmt.Sprintf("[%s] %s, %s\n%s", singleton.Localizer.MustLocalize(
|
|
|
|
|
&i18n.LocalizeConfig{
|
|
|
|
|
MessageID: "ScheduledTaskExecutedFailed",
|
|
|
|
|
},
|
2022-10-31 09:54:37 -04:00
|
|
|
|
), cr.Name, singleton.ServerList[clientID].Name, r.GetData()), nil, &curServer)
|
2021-01-23 02:32:04 -05:00
|
|
|
|
}
|
2022-01-08 22:54:14 -05:00
|
|
|
|
singleton.DB.Model(cr).Updates(model.Cron{
|
2021-01-23 02:32:04 -05:00
|
|
|
|
LastExecutedAt: time.Now().Add(time.Second * -1 * time.Duration(r.GetDelay())),
|
|
|
|
|
LastResult: r.GetSuccessful(),
|
|
|
|
|
})
|
2021-01-18 20:59:04 -05:00
|
|
|
|
}
|
2021-09-27 09:18:09 -04:00
|
|
|
|
} else if model.IsServiceSentinelNeeded(r.GetType()) {
|
2022-01-08 22:54:14 -05:00
|
|
|
|
singleton.ServiceSentinelShared.Dispatch(singleton.ReportData{
|
2021-09-27 09:18:09 -04:00
|
|
|
|
Data: r,
|
|
|
|
|
Reporter: clientID,
|
|
|
|
|
})
|
2021-01-15 11:45:49 -05:00
|
|
|
|
}
|
2019-12-07 05:14:40 -05:00
|
|
|
|
return &pb.Receipt{Proced: true}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-15 11:45:49 -05:00
|
|
|
|
func (s *NezhaHandler) RequestTask(h *pb.Host, stream pb.NezhaService_RequestTaskServer) error {
|
2021-01-08 08:04:50 -05:00
|
|
|
|
var clientID uint64
|
2019-12-09 05:14:31 -05:00
|
|
|
|
var err error
|
|
|
|
|
if clientID, err = s.Auth.Check(stream.Context()); err != nil {
|
2019-12-07 05:14:40 -05:00
|
|
|
|
return err
|
|
|
|
|
}
|
2019-12-10 04:57:57 -05:00
|
|
|
|
closeCh := make(chan error)
|
2022-01-08 22:54:14 -05:00
|
|
|
|
singleton.ServerLock.RLock()
|
2024-08-11 22:06:55 -04:00
|
|
|
|
singleton.ServerList[clientID].TaskCloseLock.Lock()
|
2021-11-11 08:40:13 -05:00
|
|
|
|
// 修复不断的请求 task 但是没有 return 导致内存泄漏
|
2022-01-08 22:54:14 -05:00
|
|
|
|
if singleton.ServerList[clientID].TaskClose != nil {
|
|
|
|
|
close(singleton.ServerList[clientID].TaskClose)
|
2021-11-11 08:40:13 -05:00
|
|
|
|
}
|
2022-01-08 22:54:14 -05:00
|
|
|
|
singleton.ServerList[clientID].TaskStream = stream
|
|
|
|
|
singleton.ServerList[clientID].TaskClose = closeCh
|
2024-08-11 22:06:55 -04:00
|
|
|
|
singleton.ServerList[clientID].TaskCloseLock.Unlock()
|
2022-01-08 22:54:14 -05:00
|
|
|
|
singleton.ServerLock.RUnlock()
|
2021-04-17 11:36:37 -04:00
|
|
|
|
return <-closeCh
|
2019-12-07 05:14:40 -05:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-15 11:45:49 -05:00
|
|
|
|
func (s *NezhaHandler) ReportSystemState(c context.Context, r *pb.State) (*pb.Receipt, error) {
|
|
|
|
|
var clientID uint64
|
|
|
|
|
var err error
|
|
|
|
|
if clientID, err = s.Auth.Check(c); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
state := model.PB2State(r)
|
2022-01-08 22:54:14 -05:00
|
|
|
|
singleton.ServerLock.RLock()
|
|
|
|
|
defer singleton.ServerLock.RUnlock()
|
|
|
|
|
singleton.ServerList[clientID].LastActive = time.Now()
|
|
|
|
|
singleton.ServerList[clientID].State = &state
|
2021-07-14 11:53:37 -04:00
|
|
|
|
|
2024-08-10 22:35:19 -04:00
|
|
|
|
// 应对 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)
|
2021-07-14 11:53:37 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-15 11:45:49 -05:00
|
|
|
|
return &pb.Receipt{Proced: true}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *NezhaHandler) ReportSystemInfo(c context.Context, r *pb.Host) (*pb.Receipt, error) {
|
2021-01-08 08:04:50 -05:00
|
|
|
|
var clientID uint64
|
2024-04-27 01:36:36 -04:00
|
|
|
|
var provider ddns.Provider
|
2019-12-09 05:14:31 -05:00
|
|
|
|
var err error
|
|
|
|
|
if clientID, err = s.Auth.Check(c); err != nil {
|
2019-12-07 05:14:40 -05:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2019-12-13 04:56:14 -05:00
|
|
|
|
host := model.PB2Host(r)
|
2022-01-08 22:54:14 -05:00
|
|
|
|
singleton.ServerLock.RLock()
|
|
|
|
|
defer singleton.ServerLock.RUnlock()
|
2024-02-24 08:10:27 -05:00
|
|
|
|
|
|
|
|
|
// 检查并更新DDNS
|
|
|
|
|
if singleton.Conf.DDNS.Enable &&
|
|
|
|
|
singleton.ServerList[clientID].EnableDDNS &&
|
|
|
|
|
host.IP != "" &&
|
2024-08-10 22:35:19 -04:00
|
|
|
|
(singleton.ServerList[clientID].Host == nil || singleton.ServerList[clientID].Host.IP != host.IP) {
|
2024-02-24 08:10:27 -05:00
|
|
|
|
serverDomain := singleton.ServerList[clientID].DDNSDomain
|
2024-04-27 01:36:36 -04:00
|
|
|
|
if singleton.Conf.DDNS.Provider == "" {
|
|
|
|
|
provider, err = singleton.GetDDNSProviderFromProfile(singleton.ServerList[clientID].DDNSProfile)
|
|
|
|
|
} else {
|
|
|
|
|
provider, err = singleton.GetDDNSProviderFromString(singleton.Conf.DDNS.Provider)
|
|
|
|
|
}
|
2024-02-24 08:10:27 -05:00
|
|
|
|
if err == nil && serverDomain != "" {
|
|
|
|
|
ipv4, ipv6, _ := utils.SplitIPAddr(host.IP)
|
|
|
|
|
maxRetries := int(singleton.Conf.DDNS.MaxRetries)
|
2024-02-25 22:49:28 -05:00
|
|
|
|
config := &ddns.DomainConfig{
|
2024-03-29 23:00:55 -04:00
|
|
|
|
EnableIPv4: singleton.ServerList[clientID].EnableIPv4,
|
|
|
|
|
EnableIpv6: singleton.ServerList[clientID].EnableIpv6,
|
2024-02-24 08:10:27 -05:00
|
|
|
|
FullDomain: serverDomain,
|
|
|
|
|
Ipv4Addr: ipv4,
|
|
|
|
|
Ipv6Addr: ipv6,
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 12:08:16 -04:00
|
|
|
|
go singleton.RetryableUpdateDomain(provider, config, maxRetries)
|
2024-02-24 08:10:27 -05:00
|
|
|
|
} else {
|
|
|
|
|
// 虽然会在启动时panic, 可以断言不会走这个分支, 但是考虑到动态加载配置或者其它情况, 这里输出一下方便检查奇奇怪怪的BUG
|
2024-08-23 23:11:06 -04:00
|
|
|
|
log.Printf("NEZHA>> 未找到对应的DDNS配置(%s), 或者是provider填写不正确, 请前往config.yml检查你的设置", singleton.ServerList[clientID].DDNSProfile)
|
2024-02-24 08:10:27 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 发送IP变动通知
|
2024-08-10 22:35:19 -04:00
|
|
|
|
if singleton.ServerList[clientID].Host != nil && singleton.Conf.EnableIPChangeNotification &&
|
2022-01-08 22:54:14 -05:00
|
|
|
|
((singleton.Conf.Cover == model.ConfigCoverAll && !singleton.Conf.IgnoredIPNotificationServerIDs[clientID]) ||
|
|
|
|
|
(singleton.Conf.Cover == model.ConfigCoverIgnoreAll && singleton.Conf.IgnoredIPNotificationServerIDs[clientID])) &&
|
|
|
|
|
singleton.ServerList[clientID].Host.IP != "" &&
|
2021-01-13 09:30:28 -05:00
|
|
|
|
host.IP != "" &&
|
2022-01-08 22:54:14 -05:00
|
|
|
|
singleton.ServerList[clientID].Host.IP != host.IP {
|
2022-10-31 09:54:37 -04:00
|
|
|
|
|
2023-06-18 17:02:20 -04:00
|
|
|
|
singleton.SendNotification(singleton.Conf.IPChangeNotificationTag,
|
|
|
|
|
fmt.Sprintf(
|
|
|
|
|
"[%s] %s, %s => %s",
|
|
|
|
|
singleton.Localizer.MustLocalize(&i18n.LocalizeConfig{
|
|
|
|
|
MessageID: "IPChanged",
|
|
|
|
|
}),
|
|
|
|
|
singleton.ServerList[clientID].Name, singleton.IPDesensitize(singleton.ServerList[clientID].Host.IP),
|
|
|
|
|
singleton.IPDesensitize(host.IP),
|
|
|
|
|
),
|
|
|
|
|
nil)
|
2021-01-13 09:30:28 -05:00
|
|
|
|
}
|
2021-07-16 06:09:50 -04:00
|
|
|
|
|
2024-08-10 22:35:19 -04: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 06:09:50 -04:00
|
|
|
|
}
|
|
|
|
|
|
2024-07-28 02:39:14 -04:00
|
|
|
|
// 不要冲掉国家码
|
|
|
|
|
if singleton.ServerList[clientID].Host != nil {
|
|
|
|
|
host.CountryCode = singleton.ServerList[clientID].Host.CountryCode
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-08 22:54:14 -05:00
|
|
|
|
singleton.ServerList[clientID].Host = &host
|
2019-12-07 05:14:40 -05:00
|
|
|
|
return &pb.Receipt{Proced: true}, nil
|
|
|
|
|
}
|
2024-07-14 00:47:36 -04: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
|
|
|
|
|
}
|
|
|
|
|
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")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 01:59:58 -04:00
|
|
|
|
|
|
|
|
|
func (s *NezhaHandler) LookupGeoIP(c context.Context, r *pb.GeoIP) (*pb.GeoIP, error) {
|
|
|
|
|
var clientID uint64
|
|
|
|
|
var err error
|
|
|
|
|
if clientID, err = s.Auth.Check(c); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据内置数据库查询 IP 地理位置
|
|
|
|
|
record := &geoip.IPInfo{}
|
|
|
|
|
ip := r.GetIp()
|
|
|
|
|
netIP := net.ParseIP(ip)
|
|
|
|
|
location, err := geoip.Lookup(netIP, record)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将地区码写入到 Host
|
|
|
|
|
singleton.ServerLock.RLock()
|
|
|
|
|
defer singleton.ServerLock.RUnlock()
|
2024-07-28 03:11:50 -04:00
|
|
|
|
if singleton.ServerList[clientID].Host == nil {
|
|
|
|
|
return nil, fmt.Errorf("host not found")
|
2024-07-28 02:39:14 -04:00
|
|
|
|
}
|
2024-07-28 03:11:50 -04:00
|
|
|
|
singleton.ServerList[clientID].Host.CountryCode = location
|
2024-07-28 01:59:58 -04:00
|
|
|
|
|
|
|
|
|
return &pb.GeoIP{Ip: ip, CountryCode: location}, nil
|
|
|
|
|
}
|