Skip to content

Commit 5902dc8

Browse files
authored
Merge b8026f9 into 320baae
2 parents 320baae + b8026f9 commit 5902dc8

9 files changed

Lines changed: 364 additions & 3 deletions

File tree

.github/agents/pr-agent.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ Agent 禁止的事项(严格):
176176
## 风格与质量要求
177177
- C 代码:使用 `make format` 格式化(底层是 `clang-format`,style 定义在 `variables.mk``STYLE` 变量中);
178178
- Go 代码:使用 `gofmt`/`goimports` 格式化,遵守 `.golangci.yml` 中的 lint 规则;
179+
- **golangci-lint 验证(必需)**:所有提交的 Go 代码必须通过 `golangci-lint` 检查。Agent 在提交 PR 前,必须运行 `golangci-lint run ./...`(使用仓库根目录的 `.golangci.yml` 配置),并确保无新增 lint 错误。CI 中使用 golangci-lint v2.1(配置版本 `version: 2`),本地安装命令:`go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6`
179180
- 小步提交、单一目的 PR、清晰的 PR 描述;
180181
- 所有 PR 中若有未解决的集成测试限制,必须在 PR 描述中注明并提供复现步骤或需要的维护者权限/环境。
181182

internal/probe/gotls/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,12 @@ func (c *Config) validateCaptureMode() error {
274274
if c.PcapFile == "" {
275275
return fmt.Errorf("pcap mode requires PcapFile to be set")
276276
}
277+
278+
// Auto-detect an active network interface when none is specified.
279+
// On Android emulators wlan0 may exist but have no addresses;
280+
// networking may go through eth0 or another interface instead.
281+
c.setDefaultIfname()
282+
277283
if c.Ifname == "" {
278284
return fmt.Errorf("pcap mode requires Ifname to be set")
279285
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2022 CFC4N <cfc4n.cs@gmail.com>. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package gotls
16+
17+
import "net"
18+
19+
// ifaceHasAddr returns true when the named interface exists, is up, and has at
20+
// least one address.
21+
func ifaceHasAddr(name string) bool {
22+
iface, err := net.InterfaceByName(name)
23+
if err != nil {
24+
return false
25+
}
26+
if iface.Flags&net.FlagUp == 0 {
27+
return false
28+
}
29+
addrs, err := iface.Addrs()
30+
if err != nil {
31+
return false
32+
}
33+
return len(addrs) > 0
34+
}
35+
36+
// firstUpNonLoopbackInterface walks all network interfaces and returns the
37+
// name of the first non-loopback interface that is up and has an address.
38+
// Note: this is not necessarily the default-route interface; it may return
39+
// virtual bridges (e.g. docker0) before the real egress interface.
40+
func firstUpNonLoopbackInterface() string {
41+
ifaces, err := net.Interfaces()
42+
if err != nil {
43+
return ""
44+
}
45+
for _, iface := range ifaces {
46+
if iface.Flags&net.FlagLoopback != 0 {
47+
continue
48+
}
49+
if iface.Flags&net.FlagUp == 0 {
50+
continue
51+
}
52+
addrs, err := iface.Addrs()
53+
if err != nil || len(addrs) == 0 {
54+
continue
55+
}
56+
return iface.Name
57+
}
58+
return ""
59+
}
60+
61+
// setDefaultIfname detects an active network interface when Ifname is empty.
62+
// On real devices wlan0 is common, but in emulators networking may use eth0
63+
// or another interface. This function tries to find an interface that is up
64+
// and has at least one IP address.
65+
func (c *Config) setDefaultIfname() {
66+
if c.Ifname != "" {
67+
return
68+
}
69+
70+
// Try the common default first.
71+
if ifaceHasAddr("wlan0") {
72+
c.Ifname = "wlan0"
73+
return
74+
}
75+
76+
// Fallback: iterate all interfaces looking for one that is up and has an
77+
// address, skipping loopback.
78+
if name := firstUpNonLoopbackInterface(); name != "" {
79+
c.Ifname = name
80+
return
81+
}
82+
83+
// Last resort: keep the historical default so the error message is clear.
84+
c.Ifname = "wlan0"
85+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2022 CFC4N <cfc4n.cs@gmail.com>. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package gotls
16+
17+
import (
18+
"net"
19+
"testing"
20+
)
21+
22+
func TestIfaceHasAddr_Loopback(t *testing.T) {
23+
// The loopback interface (lo) should always be present and have an address
24+
// on Linux. On non-Linux or unusual environments, skip.
25+
iface, err := net.InterfaceByName("lo")
26+
if err != nil {
27+
t.Skip("loopback interface 'lo' not available on this host")
28+
}
29+
if iface.Flags&net.FlagUp == 0 {
30+
t.Skip("loopback interface 'lo' is not UP")
31+
}
32+
if !ifaceHasAddr("lo") {
33+
t.Error("ifaceHasAddr returned false for loopback interface 'lo' which is UP")
34+
}
35+
}
36+
37+
func TestIfaceHasAddr_NonExistent(t *testing.T) {
38+
// A completely made-up interface name should return false.
39+
if ifaceHasAddr("nonexistent_iface_xyz") {
40+
t.Error("ifaceHasAddr returned true for a non-existent interface")
41+
}
42+
}
43+
44+
func TestFirstUpNonLoopbackInterface(t *testing.T) {
45+
name := firstUpNonLoopbackInterface()
46+
if name == "" {
47+
t.Skip("no active non-loopback interface found on this host")
48+
}
49+
50+
// The returned interface must actually be up and have addresses.
51+
iface, err := net.InterfaceByName(name)
52+
if err != nil {
53+
t.Fatalf("firstUpNonLoopbackInterface returned %q but InterfaceByName failed: %v", name, err)
54+
}
55+
if iface.Flags&net.FlagUp == 0 {
56+
t.Errorf("firstUpNonLoopbackInterface returned %q which is not UP", name)
57+
}
58+
if iface.Flags&net.FlagLoopback != 0 {
59+
t.Errorf("firstUpNonLoopbackInterface returned loopback interface %q", name)
60+
}
61+
addrs, err := iface.Addrs()
62+
if err != nil {
63+
t.Fatalf("cannot get addresses for %q: %v", name, err)
64+
}
65+
if len(addrs) == 0 {
66+
t.Errorf("firstUpNonLoopbackInterface returned %q which has no addresses", name)
67+
}
68+
}
69+
70+
func TestSetDefaultIfname(t *testing.T) {
71+
// When Ifname is already set, setDefaultIfname should not change it.
72+
c := NewConfig()
73+
c.Ifname = "eth99"
74+
c.setDefaultIfname()
75+
if c.Ifname != "eth99" {
76+
t.Errorf("setDefaultIfname changed pre-set Ifname from 'eth99' to %q", c.Ifname)
77+
}
78+
79+
// When Ifname is empty, setDefaultIfname should set it to something non-empty.
80+
c2 := NewConfig()
81+
c2.Ifname = ""
82+
c2.setDefaultIfname()
83+
if c2.Ifname == "" {
84+
t.Error("setDefaultIfname left Ifname empty")
85+
}
86+
}

internal/probe/openssl/config_ecandroid.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,30 @@ func detectAndroidVersion() (string, error) {
104104
return "", fmt.Errorf("android version not found in build.prop")
105105
}
106106

107-
// Android-specific default interface
107+
// Android-specific default interface.
108+
// On real devices wlan0 is common, but in emulators networking may use eth0
109+
// or another interface. This function tries to find an interface that is up
110+
// and has at least one IP address.
108111
func (c *Config) setDefaultIfname() {
109-
if c.Ifname == "" {
112+
if c.Ifname != "" {
113+
return
114+
}
115+
116+
// Try the common Android default first.
117+
if ifaceHasAddr(DefaultIfname) {
110118
c.Ifname = DefaultIfname
119+
return
111120
}
121+
122+
// Fallback: iterate all interfaces looking for one that is up and has an
123+
// address, skipping loopback.
124+
if name := firstUpNonLoopbackInterface(); name != "" {
125+
c.Ifname = name
126+
return
127+
}
128+
129+
// Last resort: keep the historical default so the error message is clear.
130+
c.Ifname = DefaultIfname
112131
}
113132

114133
// validateCgroupPath is a no-op on Android since cgroup filtering is not supported.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2022 CFC4N <cfc4n.cs@gmail.com>. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package openssl
16+
17+
import "net"
18+
19+
// ifaceHasAddr returns true when the named interface exists, is up, and has at
20+
// least one address.
21+
func ifaceHasAddr(name string) bool {
22+
iface, err := net.InterfaceByName(name)
23+
if err != nil {
24+
return false
25+
}
26+
if iface.Flags&net.FlagUp == 0 {
27+
return false
28+
}
29+
addrs, err := iface.Addrs()
30+
if err != nil {
31+
return false
32+
}
33+
return len(addrs) > 0
34+
}
35+
36+
// firstUpNonLoopbackInterface walks all network interfaces and returns the
37+
// name of the first non-loopback interface that is up and has an address.
38+
// Note: this is not necessarily the default-route interface; it may return
39+
// virtual bridges (e.g. docker0) before the real egress interface.
40+
func firstUpNonLoopbackInterface() string {
41+
ifaces, err := net.Interfaces()
42+
if err != nil {
43+
return ""
44+
}
45+
for _, iface := range ifaces {
46+
if iface.Flags&net.FlagLoopback != 0 {
47+
continue
48+
}
49+
if iface.Flags&net.FlagUp == 0 {
50+
continue
51+
}
52+
addrs, err := iface.Addrs()
53+
if err != nil || len(addrs) == 0 {
54+
continue
55+
}
56+
return iface.Name
57+
}
58+
return ""
59+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2022 CFC4N <cfc4n.cs@gmail.com>. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package openssl
16+
17+
import (
18+
"net"
19+
"testing"
20+
)
21+
22+
func TestIfaceHasAddr_Loopback(t *testing.T) {
23+
// The loopback interface (lo) should always be present and have an address
24+
// on Linux. On non-Linux or unusual environments, skip.
25+
iface, err := net.InterfaceByName("lo")
26+
if err != nil {
27+
t.Skip("loopback interface 'lo' not available on this host")
28+
}
29+
if iface.Flags&net.FlagUp == 0 {
30+
t.Skip("loopback interface 'lo' is not UP")
31+
}
32+
if !ifaceHasAddr("lo") {
33+
t.Error("ifaceHasAddr returned false for loopback interface 'lo' which is UP")
34+
}
35+
}
36+
37+
func TestIfaceHasAddr_NonExistent(t *testing.T) {
38+
// A completely made-up interface name should return false.
39+
if ifaceHasAddr("nonexistent_iface_xyz") {
40+
t.Error("ifaceHasAddr returned true for a non-existent interface")
41+
}
42+
}
43+
44+
func TestFirstUpNonLoopbackInterface(t *testing.T) {
45+
name := firstUpNonLoopbackInterface()
46+
if name == "" {
47+
t.Skip("no active non-loopback interface found on this host")
48+
}
49+
50+
// The returned interface must actually be up and have addresses.
51+
iface, err := net.InterfaceByName(name)
52+
if err != nil {
53+
t.Fatalf("firstUpNonLoopbackInterface returned %q but InterfaceByName failed: %v", name, err)
54+
}
55+
if iface.Flags&net.FlagUp == 0 {
56+
t.Errorf("firstUpNonLoopbackInterface returned %q which is not UP", name)
57+
}
58+
if iface.Flags&net.FlagLoopback != 0 {
59+
t.Errorf("firstUpNonLoopbackInterface returned loopback interface %q", name)
60+
}
61+
addrs, err := iface.Addrs()
62+
if err != nil {
63+
t.Fatalf("cannot get addresses for %q: %v", name, err)
64+
}
65+
if len(addrs) == 0 {
66+
t.Errorf("firstUpNonLoopbackInterface returned %q which has no addresses", name)
67+
}
68+
}

internal/probe/openssl/config_linux.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,19 @@ func (c *Config) setDefaultIfname() {
103103
return
104104
}
105105

106+
// Try the historical default first.
107+
if ifaceHasAddr("wlan0") {
108+
c.Ifname = "wlan0"
109+
return
110+
}
111+
112+
// Fallback: iterate all interfaces looking for one that is up and has an
113+
// address, skipping loopback.
114+
if name := firstUpNonLoopbackInterface(); name != "" {
115+
c.Ifname = name
116+
return
117+
}
118+
106119
c.Ifname = "wlan0"
107120
}
108121

0 commit comments

Comments
 (0)