mirror of
https://github.com/MatsuriDayo/nekoray.git
synced 2025-12-17 20:44:38 +03:00
update
This commit is contained in:
@@ -52,12 +52,10 @@ namespace NekoRay::fmt {
|
|||||||
outbound["server"] = serverAddress;
|
outbound["server"] = serverAddress;
|
||||||
outbound["server_port"] = serverPort;
|
outbound["server_port"] = serverPort;
|
||||||
|
|
||||||
QJsonArray users;
|
if (!username.isEmpty() && !password.isEmpty()) {
|
||||||
QJsonObject user;
|
outbound["username"] = username;
|
||||||
user["username"] = username;
|
outbound["password"] = password;
|
||||||
user["password"] = password;
|
}
|
||||||
users.push_back(user);
|
|
||||||
if (!username.isEmpty() && !password.isEmpty()) outbound["users"] = users;
|
|
||||||
|
|
||||||
stream->BuildStreamSettingsSingBox(&outbound);
|
stream->BuildStreamSettingsSingBox(&outbound);
|
||||||
result.outbound = outbound;
|
result.outbound = outbound;
|
||||||
|
|||||||
@@ -2,15 +2,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
|
||||||
"neko/pkg/neko_common"
|
"neko/pkg/neko_common"
|
||||||
|
"neko/pkg/neko_log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@@ -27,8 +25,10 @@ var instance_cancel context.CancelFunc
|
|||||||
// Use sing-box instead of libcore & v2ray
|
// Use sing-box instead of libcore & v2ray
|
||||||
|
|
||||||
func setupCore() {
|
func setupCore() {
|
||||||
|
neko_log.SetupLog(50*1024, "./neko.log")
|
||||||
|
//
|
||||||
log.SetFlags(log.LstdFlags)
|
log.SetFlags(log.LstdFlags)
|
||||||
log.SetOutput(os.Stdout)
|
log.SetOutput(neko_log.LogWriter)
|
||||||
//
|
//
|
||||||
neko_common.GetProxyHttpClient = func() *http.Client {
|
neko_common.GetProxyHttpClient = func() *http.Client {
|
||||||
return getProxyHttpClient(instance)
|
return getProxyHttpClient(instance)
|
||||||
@@ -67,45 +67,19 @@ func getProxyHttpClient(box *box.Box) *http.Client {
|
|||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move
|
type logWriter struct {
|
||||||
func UrlTestSingBox(box *box.Box, link string, timeout int32) (int32, error) {
|
files []io.Writer
|
||||||
client := getProxyHttpClient(box)
|
}
|
||||||
if client == nil {
|
|
||||||
return 0, fmt.Errorf("no client")
|
func (w *logWriter) Write(p []byte) (n int, err error) {
|
||||||
}
|
for _, file := range w.files {
|
||||||
|
if file == nil {
|
||||||
// Test handshake time
|
continue
|
||||||
var time_start time.Time
|
}
|
||||||
var times = 1
|
n, err = file.Write(p)
|
||||||
var rtt_times = 1
|
if err != nil {
|
||||||
|
return
|
||||||
// Test RTT "true delay"
|
}
|
||||||
if link2 := strings.TrimLeft(link, "true"); link != link2 {
|
}
|
||||||
link = link2
|
return
|
||||||
times = 3
|
|
||||||
rtt_times = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", link, nil)
|
|
||||||
req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", rand.Int()%84, rand.Int()%2))
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < times; i++ {
|
|
||||||
if i == 1 || times == 1 {
|
|
||||||
time_start = time.Now()
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Url test failed:", err)
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
resp.Body.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
return int32(time.Since(time_start).Milliseconds() / int64(rtt_times)), nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,16 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"neko/gen"
|
"neko/gen"
|
||||||
"neko/pkg/grpc_server"
|
"neko/pkg/grpc_server"
|
||||||
"neko/pkg/neko_common"
|
"neko/pkg/neko_common"
|
||||||
|
"neko/pkg/neko_log"
|
||||||
|
"neko/pkg/speedtest"
|
||||||
"nekobox_core/box_main"
|
"nekobox_core/box_main"
|
||||||
"net"
|
"reflect"
|
||||||
"time"
|
"unsafe"
|
||||||
|
|
||||||
box "github.com/sagernet/sing-box"
|
box "github.com/sagernet/sing-box"
|
||||||
)
|
)
|
||||||
@@ -39,6 +42,17 @@ func (s *server) Start(ctx context.Context, in *gen.LoadConfigReq) (out *gen.Err
|
|||||||
}
|
}
|
||||||
|
|
||||||
instance, instance_cancel, err = box_main.Create([]byte(in.CoreConfig), true)
|
instance, instance_cancel, err = box_main.Create([]byte(in.CoreConfig), true)
|
||||||
|
|
||||||
|
// Logger
|
||||||
|
if instance != nil {
|
||||||
|
logFactory_ := reflect.Indirect(reflect.ValueOf(instance)).FieldByName("logFactory")
|
||||||
|
logFactory_ = reflect.NewAt(logFactory_.Type(), unsafe.Pointer(logFactory_.UnsafeAddr())).Elem() // get unexported logFactory
|
||||||
|
logFactory_ = logFactory_.Elem().Elem() // get struct
|
||||||
|
writer_ := logFactory_.FieldByName("writer")
|
||||||
|
writer_ = reflect.NewAt(writer_.Type(), unsafe.Pointer(writer_.UnsafeAddr())).Elem() // get unexported io.Writer
|
||||||
|
writer_.Set(reflect.ValueOf(neko_log.LogWriter))
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,27 +107,11 @@ func (s *server) Test(ctx context.Context, in *gen.TestReq) (out *gen.TestResp,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Latency
|
// Latency
|
||||||
out.Ms, err = UrlTestSingBox(i, in.Url, in.Timeout)
|
out.Ms, err = speedtest.UrlTest(getProxyHttpClient(i), in.Url, in.Timeout)
|
||||||
} else if in.Mode == gen.TestMode_TcpPing {
|
} else if in.Mode == gen.TestMode_TcpPing {
|
||||||
host, port, err := net.SplitHostPort(in.Address)
|
out.Ms, err = speedtest.TcpPing(in.Address, in.Timeout)
|
||||||
if err != nil {
|
|
||||||
out.Error = err.Error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ip, err := net.ResolveIPAddr("ip", host)
|
|
||||||
if err != nil {
|
|
||||||
out.Error = err.Error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//
|
|
||||||
startTime := time.Now()
|
|
||||||
_, err = net.DialTimeout("tcp", net.JoinHostPort(ip.String(), port), time.Duration(in.Timeout)*time.Millisecond)
|
|
||||||
endTime := time.Now()
|
|
||||||
if err == nil {
|
|
||||||
out.Ms = int32(endTime.Sub(startTime).Milliseconds())
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// TODO copy
|
err = fmt.Errorf("not available")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
var instance *libcore.V2RayInstance
|
var instance *libcore.V2RayInstance
|
||||||
|
|
||||||
func setupCore() {
|
func setupCore() {
|
||||||
|
// TODO del
|
||||||
device.IsNekoray = true
|
device.IsNekoray = true
|
||||||
libcore.SetConfig("", false, true)
|
libcore.SetConfig("", false, true)
|
||||||
libcore.InitCore("", "", "", nil, ".", "moe.nekoray.pc:bg", true, 50)
|
libcore.InitCore("", "", "", nil, ".", "moe.nekoray.pc:bg", true, 50)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"neko/gen"
|
"neko/gen"
|
||||||
"neko/pkg/grpc_server"
|
"neko/pkg/grpc_server"
|
||||||
"neko/pkg/neko_common"
|
"neko/pkg/neko_common"
|
||||||
|
"neko/pkg/speedtest"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -121,31 +122,15 @@ func (s *server) Test(ctx context.Context, in *gen.TestReq) (out *gen.TestResp,
|
|||||||
|
|
||||||
// Latency
|
// Latency
|
||||||
var t int32
|
var t int32
|
||||||
t, err = libcore.UrlTestV2ray(i, in.Inbound, in.Url, in.Timeout)
|
t, err = speedtest.UrlTest(getProxyHttpClient(i), in.Address, in.Timeout)
|
||||||
out.Ms = t // sn: ms==0 是错误
|
out.Ms = t // sn: ms==0 是错误
|
||||||
} else if in.Mode == gen.TestMode_TcpPing {
|
} else if in.Mode == gen.TestMode_TcpPing {
|
||||||
host, port, err := net.SplitHostPort(in.Address)
|
out.Ms, err = speedtest.TcpPing(in.Address, in.Timeout)
|
||||||
if err != nil {
|
|
||||||
out.Error = err.Error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ip, err := net.ResolveIPAddr("ip", host)
|
|
||||||
if err != nil {
|
|
||||||
out.Error = err.Error()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
//
|
|
||||||
startTime := time.Now()
|
|
||||||
_, err = net.DialTimeout("tcp", net.JoinHostPort(ip.String(), port), time.Duration(in.Timeout)*time.Millisecond)
|
|
||||||
endTime := time.Now()
|
|
||||||
if err == nil {
|
|
||||||
out.Ms = int32(endTime.Sub(startTime).Milliseconds())
|
|
||||||
}
|
|
||||||
} else if in.Mode == gen.TestMode_FullTest {
|
} else if in.Mode == gen.TestMode_FullTest {
|
||||||
if in.Config == nil {
|
if in.Config == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// TODO del
|
||||||
// Test instance
|
// Test instance
|
||||||
i := libcore.NewV2rayInstance()
|
i := libcore.NewV2rayInstance()
|
||||||
i.ForTest = true
|
i.ForTest = true
|
||||||
@@ -164,7 +149,7 @@ func (s *server) Test(ctx context.Context, in *gen.TestReq) (out *gen.TestResp,
|
|||||||
// Latency
|
// Latency
|
||||||
var latency string
|
var latency string
|
||||||
if in.FullLatency {
|
if in.FullLatency {
|
||||||
t, _ := libcore.UrlTestV2ray(i, in.Inbound, in.Url, in.Timeout)
|
t, _ := speedtest.UrlTest(getProxyHttpClient(i), in.Address, in.Timeout)
|
||||||
out.Ms = t
|
out.Ms = t
|
||||||
if t > 0 {
|
if t > 0 {
|
||||||
latency = fmt.Sprint(t, "ms")
|
latency = fmt.Sprint(t, "ms")
|
||||||
|
|||||||
51
go/pkg/neko_log/log.go
Normal file
51
go/pkg/neko_log/log.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package neko_log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var f_neko_log *os.File
|
||||||
|
|
||||||
|
var LogWriter *logWriter
|
||||||
|
|
||||||
|
func SetupLog(maxSize int, path string) {
|
||||||
|
if f_neko_log != nil && LogWriter != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// mod from libcore, simplify because only 1 proccess.
|
||||||
|
oldBytes, err := os.ReadFile(path)
|
||||||
|
if err == nil && len(oldBytes) > maxSize {
|
||||||
|
if os.Truncate(path, 0) == nil {
|
||||||
|
oldBytes = oldBytes[len(oldBytes)-maxSize:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f_neko_log, err = os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0644)
|
||||||
|
if err == nil {
|
||||||
|
f_neko_log.Write(oldBytes)
|
||||||
|
} else {
|
||||||
|
fmt.Println("error open log", err)
|
||||||
|
}
|
||||||
|
//
|
||||||
|
LogWriter = &logWriter{
|
||||||
|
files: []io.Writer{os.Stdout, f_neko_log},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type logWriter struct {
|
||||||
|
files []io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *logWriter) Write(p []byte) (n int, err error) {
|
||||||
|
for _, file := range w.files {
|
||||||
|
if file == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n, err = file.Write(p)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
72
go/pkg/speedtest/speedtest.go
Normal file
72
go/pkg/speedtest/speedtest.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package speedtest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UrlTest(client *http.Client, link string, timeout int32) (int32, error) {
|
||||||
|
if client == nil {
|
||||||
|
return 0, fmt.Errorf("no client")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test handshake time
|
||||||
|
var time_start time.Time
|
||||||
|
var times = 1
|
||||||
|
var rtt_times = 1
|
||||||
|
|
||||||
|
// Test RTT "true delay"
|
||||||
|
if link2 := strings.TrimLeft(link, "true"); link != link2 {
|
||||||
|
link = link2
|
||||||
|
times = 3
|
||||||
|
rtt_times = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", link, nil)
|
||||||
|
req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", rand.Int()%84, rand.Int()%2))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < times; i++ {
|
||||||
|
if i == 1 || times == 1 {
|
||||||
|
time_start = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Url test failed:", err)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return int32(time.Since(time_start).Milliseconds() / int64(rtt_times)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TcpPing(address string, timeout int32) (ms int32, err error) {
|
||||||
|
host, port, err := net.SplitHostPort(address)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ip, err := net.ResolveIPAddr("ip", host)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//
|
||||||
|
startTime := time.Now()
|
||||||
|
c, err := net.DialTimeout("tcp", net.JoinHostPort(ip.String(), port), time.Duration(timeout)*time.Millisecond)
|
||||||
|
endTime := time.Now()
|
||||||
|
if err == nil {
|
||||||
|
ms = int32(endTime.Sub(startTime).Milliseconds())
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -85,7 +85,7 @@ namespace NekoRay {
|
|||||||
|
|
||||||
// Socks & HTTP Inbound
|
// Socks & HTTP Inbound
|
||||||
QString inbound_address = "127.0.0.1";
|
QString inbound_address = "127.0.0.1";
|
||||||
int inbound_socks_port = 2080;
|
int inbound_socks_port = 2080; // or Mixed
|
||||||
int inbound_http_port = -2081;
|
int inbound_http_port = -2081;
|
||||||
QString custom_inbound = "{\"inbounds\": []}";
|
QString custom_inbound = "{\"inbounds\": []}";
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
// Clean
|
// Clean
|
||||||
QDir::setCurrent(QApplication::applicationDirPath());
|
QDir::setCurrent(QApplication::applicationDirPath());
|
||||||
|
if (QFile::exists("updater.old")) {
|
||||||
QFile::remove("updater.old");
|
QFile::remove("updater.old");
|
||||||
|
QFile::remove("sing-box.exe"); // v1.11
|
||||||
|
}
|
||||||
#ifndef Q_OS_WIN
|
#ifndef Q_OS_WIN
|
||||||
if (!QFile::exists("updater")) {
|
if (!QFile::exists("updater")) {
|
||||||
QFile::link("launcher", "updater");
|
QFile::link("launcher", "updater");
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ namespace NekoRay::rpc {
|
|||||||
|
|
||||||
QString Client::Start(bool *rpcOK, const libcore::LoadConfigReq &request) {
|
QString Client::Start(bool *rpcOK, const libcore::LoadConfigReq &request) {
|
||||||
libcore::ErrorResp reply;
|
libcore::ErrorResp reply;
|
||||||
auto status = grpc_channel->Call("Start", request, &reply);
|
auto status = grpc_channel->Call("Start", request, &reply, 3000);
|
||||||
|
|
||||||
if (status == QNetworkReply::NoError) {
|
if (status == QNetworkReply::NoError) {
|
||||||
*rpcOK = true;
|
*rpcOK = true;
|
||||||
|
|||||||
@@ -585,9 +585,13 @@ void MainWindow::neko_set_spmode(int mode, bool save) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
SetSystemProxy("127.0.0.1",
|
auto socks_port = NekoRay::dataStore->inbound_socks_port;
|
||||||
NekoRay::dataStore->inbound_http_port,
|
auto http_port = NekoRay::dataStore->inbound_http_port;
|
||||||
NekoRay::dataStore->inbound_socks_port);
|
if (IS_NEKO_BOX) {
|
||||||
|
http_port = socks_port;
|
||||||
|
socks_port = -1;
|
||||||
|
}
|
||||||
|
SetSystemProxy("127.0.0.1", http_port, socks_port);
|
||||||
} else if (mode == NekoRay::SystemProxyMode::VPN) {
|
} else if (mode == NekoRay::SystemProxyMode::VPN) {
|
||||||
if (!StartVPNProcess()) {
|
if (!StartVPNProcess()) {
|
||||||
refresh_status();
|
refresh_status();
|
||||||
@@ -974,7 +978,10 @@ void MainWindow::on_menu_profile_debug_info_triggered() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_menu_copy_links_triggered() {
|
void MainWindow::on_menu_copy_links_triggered() {
|
||||||
if (ui->masterLogBrowser->hasFocus()) return;
|
if (ui->masterLogBrowser->hasFocus()) {
|
||||||
|
ui->masterLogBrowser->copy();
|
||||||
|
return;
|
||||||
|
};
|
||||||
auto ents = get_now_selected();
|
auto ents = get_now_selected();
|
||||||
QStringList links;
|
QStringList links;
|
||||||
for (const auto &ent: ents) {
|
for (const auto &ent: ents) {
|
||||||
@@ -1227,6 +1234,9 @@ void MainWindow::keyPressEvent(QKeyEvent *event) {
|
|||||||
case Qt::Key_Escape:
|
case Qt::Key_Escape:
|
||||||
// take over by shortcut_esc
|
// take over by shortcut_esc
|
||||||
break;
|
break;
|
||||||
|
case Qt::Key_Enter:
|
||||||
|
neko_start();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
QMainWindow::keyPressEvent(event);
|
QMainWindow::keyPressEvent(event);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ void MainWindow::setup_grpc() {
|
|||||||
#ifndef NKR_NO_GRPC
|
#ifndef NKR_NO_GRPC
|
||||||
// Setup Connection
|
// Setup Connection
|
||||||
defaultClient = new Client([=](const QString &errStr) {
|
defaultClient = new Client([=](const QString &errStr) {
|
||||||
showLog("gRPC Error: " + errStr);
|
showLog("[Error] gRPC: " + errStr);
|
||||||
}, "127.0.0.1:" + Int2String(NekoRay::dataStore->core_port), NekoRay::dataStore->core_token);
|
}, "127.0.0.1:" + Int2String(NekoRay::dataStore->core_port), NekoRay::dataStore->core_token);
|
||||||
auto t = new QTimer();
|
auto t = new QTimer();
|
||||||
connect(t, &QTimer::timeout, this, [=]() {
|
connect(t, &QTimer::timeout, this, [=]() {
|
||||||
|
|||||||
Reference in New Issue
Block a user