|
|
@ -12,6 +12,7 @@ import (
|
|
|
|
"io"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"log"
|
|
|
|
"mime"
|
|
|
|
"mime"
|
|
|
|
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"os"
|
|
|
@ -33,6 +34,7 @@ import (
|
|
|
|
"tailscale.com/ipn"
|
|
|
|
"tailscale.com/ipn"
|
|
|
|
"tailscale.com/ipn/ipnlocal"
|
|
|
|
"tailscale.com/ipn/ipnlocal"
|
|
|
|
"tailscale.com/net/dns"
|
|
|
|
"tailscale.com/net/dns"
|
|
|
|
|
|
|
|
"tailscale.com/net/interfaces"
|
|
|
|
"tailscale.com/net/netns"
|
|
|
|
"tailscale.com/net/netns"
|
|
|
|
"tailscale.com/paths"
|
|
|
|
"tailscale.com/paths"
|
|
|
|
"tailscale.com/tailcfg"
|
|
|
|
"tailscale.com/tailcfg"
|
|
|
@ -218,6 +220,7 @@ func main() {
|
|
|
|
fatalErr(err)
|
|
|
|
fatalErr(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
a.store = newStateStore(a.jvm, a.appCtx)
|
|
|
|
a.store = newStateStore(a.jvm, a.appCtx)
|
|
|
|
|
|
|
|
interfaces.RegisterInterfaceGetter(a.getInterfaces)
|
|
|
|
go func() {
|
|
|
|
go func() {
|
|
|
|
if err := a.runBackend(); err != nil {
|
|
|
|
if err := a.runBackend(); err != nil {
|
|
|
|
fatalErr(err)
|
|
|
|
fatalErr(err)
|
|
|
@ -1251,6 +1254,86 @@ func (a *App) contextForView(view jni.Object) jni.Object {
|
|
|
|
return ctx
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Report interfaces in the device in net.Interface format.
|
|
|
|
|
|
|
|
func (a *App) getInterfaces() ([]interfaces.Interface, error) {
|
|
|
|
|
|
|
|
var ifaceString string
|
|
|
|
|
|
|
|
err := jni.Do(a.jvm, func(env *jni.Env) error {
|
|
|
|
|
|
|
|
cls := jni.GetObjectClass(env, a.appCtx)
|
|
|
|
|
|
|
|
m := jni.GetMethodID(env, cls, "getInterfacesAsString", "()Ljava/lang/String;")
|
|
|
|
|
|
|
|
n, err := jni.CallObjectMethod(env, a.appCtx, m)
|
|
|
|
|
|
|
|
ifaceString = jni.GoString(env, jni.String(n))
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
var ifaces []interfaces.Interface
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return ifaces, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for _, iface := range strings.Split(ifaceString, "\n") {
|
|
|
|
|
|
|
|
// Example of the strings we're processing:
|
|
|
|
|
|
|
|
// wlan0 30 1500 true true false false true | fe80::2f60:2c82:4163:8389%wlan0/64 10.1.10.131/24
|
|
|
|
|
|
|
|
// r_rmnet_data0 21 1500 true false false false false | fe80::9318:6093:d1ad:ba7f%r_rmnet_data0/64
|
|
|
|
|
|
|
|
// mnet_data2 12 1500 true false false false false | fe80::3c8c:44dc:46a9:9907%rmnet_data2/64
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if strings.TrimSpace(iface) == "" {
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fields := strings.Split(iface, "|")
|
|
|
|
|
|
|
|
if len(fields) != 2 {
|
|
|
|
|
|
|
|
log.Printf("getInterfaces: unable to split %q", iface)
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var name string
|
|
|
|
|
|
|
|
var index, mtu int
|
|
|
|
|
|
|
|
var up, broadcast, loopback, pointToPoint, multicast bool
|
|
|
|
|
|
|
|
_, err := fmt.Sscanf(fields[0], "%s %d %d %t %t %t %t %t",
|
|
|
|
|
|
|
|
&name, &index, &mtu, &up, &broadcast, &loopback, &pointToPoint, &multicast)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
log.Printf("getInterfaces: unable to parse %q: %v", iface, err)
|
|
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
newIf := interfaces.Interface{
|
|
|
|
|
|
|
|
Interface: &net.Interface{
|
|
|
|
|
|
|
|
Name: name,
|
|
|
|
|
|
|
|
Index: index,
|
|
|
|
|
|
|
|
MTU: mtu,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
AltAddrs: []net.Addr{}, // non-nil to avoid Go using netlink
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if up {
|
|
|
|
|
|
|
|
newIf.Flags |= net.FlagUp
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if broadcast {
|
|
|
|
|
|
|
|
newIf.Flags |= net.FlagBroadcast
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if loopback {
|
|
|
|
|
|
|
|
newIf.Flags |= net.FlagLoopback
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if pointToPoint {
|
|
|
|
|
|
|
|
newIf.Flags |= net.FlagPointToPoint
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if multicast {
|
|
|
|
|
|
|
|
newIf.Flags |= net.FlagMulticast
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
addrs := strings.Trim(fields[1], " \n")
|
|
|
|
|
|
|
|
for _, addr := range strings.Split(addrs, " ") {
|
|
|
|
|
|
|
|
ip, err := netaddr.ParseIPPrefix(addr)
|
|
|
|
|
|
|
|
if err == nil {
|
|
|
|
|
|
|
|
newIf.AltAddrs = append(newIf.AltAddrs, ip.IPNet())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ifaces = append(ifaces, newIf)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ifaces, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func fatalErr(err error) {
|
|
|
|
func fatalErr(err error) {
|
|
|
|
// TODO: expose in UI.
|
|
|
|
// TODO: expose in UI.
|
|
|
|
log.Printf("fatal error: %v", err)
|
|
|
|
log.Printf("fatal error: %v", err)
|
|
|
|