Files
nekoray/go/grpc.go
2022-08-08 11:11:25 +08:00

224 lines
5.0 KiB
Go

package main
import (
"bufio"
"context"
"encoding/json"
"flag"
"fmt"
"io"
"libcore"
"log"
"nekoray_core/gen"
"net"
"net/http"
"os"
"runtime"
"strconv"
"strings"
"time"
_ "unsafe"
grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
v2rayNet "github.com/v2fly/v2ray-core/v5/common/net"
"google.golang.org/grpc"
)
type server struct {
gen.LibcoreServiceServer
}
var last time.Time
var nekoray_debug bool
func (s *server) KeepAlive(ctx context.Context, in *gen.EmptyReq) (*gen.EmptyResp, error) {
last = time.Now()
return &gen.EmptyResp{}, nil
}
func NekorayCore() {
_token := flag.String("token", "", "")
_port := flag.Int("port", 19810, "")
_debug := flag.Bool("debug", false, "")
flag.CommandLine.Parse(os.Args[2:])
nekoray_debug = *_debug
go func() {
t := time.NewTicker(time.Second * 10)
for {
<-t.C
if last.Add(time.Second * 10).Before(time.Now()) {
fmt.Println("Exit due to inactive")
os.Exit(0)
}
}
}()
// Libcore
setupCore()
// GRPC
lis, err := net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(*_port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
token := *_token
if token == "" {
os.Stderr.WriteString("Please set a token: ")
s := bufio.NewScanner(os.Stdin)
if s.Scan() {
token = strings.TrimSpace(s.Text())
}
}
if token == "" {
fmt.Println("You must set a token")
os.Exit(0)
}
os.Stderr.WriteString("token is set\n")
auther := Authenticator{
Token: token,
}
s := grpc.NewServer(
grpc.StreamInterceptor(grpc_auth.StreamServerInterceptor(auther.Authenticate)),
grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(auther.Authenticate)),
)
gen.RegisterLibcoreServiceServer(s, &server{})
log.Printf("neokray grpc server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
// PROXY
func getProxyHttpClient(_instance *libcore.V2RayInstance) *http.Client {
dailContext := func(ctx context.Context, network, addr string) (net.Conn, error) {
dest, err := v2rayNet.ParseDestination(fmt.Sprintf("%s:%s", network, addr))
if err != nil {
return nil, err
}
return _instance.DialContext(ctx, dest)
}
transport := &http.Transport{
TLSHandshakeTimeout: time.Second * 3,
ResponseHeaderTimeout: time.Second * 3,
}
if _instance != nil {
transport.DialContext = dailContext
}
client := &http.Client{
Transport: transport,
}
return client
}
// UPDATE
var update_download_url, update_download_as string
func (s *server) Update(ctx context.Context, in *gen.UpdateReq) (*gen.UpdateResp, error) {
ret := &gen.UpdateResp{}
client := getProxyHttpClient(instance)
if in.Action == gen.UpdateAction_Check { // Check update
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.github.com/repos/MatsuriDayo/nekoray/releases", nil)
resp, err := client.Do(req)
if err != nil {
ret.Error = err.Error()
return ret, nil
}
defer resp.Body.Close()
v := []struct {
HtmlUrl string `json:"html_url"`
Assets []struct {
Name string `json:"name"`
BrowserDownloadUrl string `json:"browser_download_url"`
} `json:"assets"`
Prerelease bool `json:"prerelease"`
Body string `json:"body"`
}{}
err = json.NewDecoder(resp.Body).Decode(&v)
if err != nil {
ret.Error = err.Error()
return ret, nil
}
nowVer := strings.TrimLeft(version_standalone, "nekoray-")
var search string
if runtime.GOOS == "windows" && runtime.GOARCH == "amd64" {
search = "windows64"
update_download_as = "nekoray.zip"
} else if runtime.GOOS == "linux" && runtime.GOARCH == "amd64" {
search = "linux64"
update_download_as = "nekoray.tar.gz"
} else {
ret.Error = "Not official support platform"
return ret, nil
}
for _, release := range v {
if len(release.Assets) > 0 {
for _, asset := range release.Assets {
if strings.Contains(asset.Name, nowVer) {
return ret, nil // No update
}
if strings.Contains(asset.Name, search) {
if release.Prerelease {
continue
}
update_download_url = asset.BrowserDownloadUrl
ret.AssetsName = asset.Name
ret.DownloadUrl = asset.BrowserDownloadUrl
ret.ReleaseUrl = release.HtmlUrl
ret.ReleaseNote = release.Body
return ret, nil // update
}
}
}
}
} else { // Download update
if update_download_url == "" || update_download_as == "" {
ret.Error = "?"
return ret, nil
}
req, _ := http.NewRequestWithContext(ctx, "GET", update_download_url, nil)
resp, err := client.Do(req)
if err != nil {
ret.Error = err.Error()
return ret, nil
}
defer resp.Body.Close()
f, err := os.OpenFile("../"+update_download_as, os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
ret.Error = err.Error()
return ret, nil
}
defer f.Close()
_, err = io.Copy(f, resp.Body)
if err != nil {
ret.Error = err.Error()
return ret, nil
}
f.Sync()
}
return ret, nil
}