Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
681 changes: 448 additions & 233 deletions docs/hiddifyrpc.md

Large diffs are not rendered by default.

368 changes: 256 additions & 112 deletions v2/ezytel/ezytel.pb.go

Large diffs are not rendered by default.

49 changes: 35 additions & 14 deletions v2/ezytel/ezytel_service.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 7 additions & 7 deletions v2/ezytel/ezytel_service_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 64 additions & 0 deletions v2/hcore/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ package hcore
import (
"context"
"fmt"
"net"
"runtime"
"strings"
"time"

"github.com/hiddify/hiddify-core/v2/config"
"github.com/hiddify/hiddify-core/v2/db"
hcommon "github.com/hiddify/hiddify-core/v2/hcommon"
"github.com/sagernet/sing-box/adapter"
"github.com/wlynxg/anet"

// "github.com/sagernet/sing-box/common/conntrack"
"github.com/sagernet/sing-box/protocol/group"

Expand Down Expand Up @@ -383,3 +387,63 @@ func (h *HiddifyInstance) UrlTest(in *UrlTestRequest) (*hcommon.Response, error)
Message: "",
}, nil
}

func (s *CoreService) GetLANIP(ctx context.Context, req *hcommon.Empty) (*LANIPResponse, error) {
// Use anet instead of net to bypass Android 11+ SELinux restrictions
// that block direct Netlink socket binding (permission denied error).
ifaces, err := anet.Interfaces()
if err != nil {
Log(LogLevel_ERROR, LogType_CORE, "GetLANIP failed: ", err)
return &LANIPResponse{Ip: "127.0.0.1"}, nil
}

for _, iface := range ifaces {
// Filter out interfaces that are down, loopback, or point-to-point (VPN/Tunnels)
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 || iface.Flags&net.FlagPointToPoint != 0 {
continue
}

// Exclude interfaces with names typical for VPNs, TAP, or TUN devices
name := strings.ToLower(iface.Name)
if strings.Contains(name, "tun") || strings.Contains(name, "tap") ||
strings.Contains(name, "wintun") || strings.Contains(name, "utun") ||
strings.Contains(name, "vpn") || strings.Contains(name, "ppp") {
continue
}

// Retrieve addresses using anet to ensure compatibility on Android 11+
addrs, err := anet.InterfaceAddrsByInterface(&iface)
if err != nil {
continue
}

for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}

if ip == nil || ip.IsLoopback() {
continue
}

// Ensure it is a valid IPv4 address
ip4 := ip.To4()
if ip4 == nil {
continue
}

// Verify the IP belongs to a standard private IP range (RFC 1918)
if ip4[0] == 10 ||
(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) ||
(ip4[0] == 192 && ip4[1] == 168) {
return &LANIPResponse{Ip: ip4.String()}, nil
}
}
}
// Fallback to loopback if no physical LAN interface is found
return &LANIPResponse{Ip: "127.0.0.1"}, nil
}
Loading
Loading