@sunjiehao/zyhy-network 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,23 +1,22 @@
1
1
  # ZYHY-Network
2
2
 
3
- 当前版本:`V0.2.1`
3
+ 当前版本:`V0.2.3`
4
4
 
5
- 办公时不用再在「网线」和「公司 Wi-Fi」之间来回切。
5
+ Mac 同时使用公司网线和 `ZYHY-Private` Wi-Fi:普通网络走网线,OA、邮箱等内网地址自动走 Wi-Fi。
6
6
 
7
- - 看网页、刷视频、走代理:用网线
8
- - 打开 OA、邮箱、iHR:自动走公司 Wi-Fi `ZYHY-Private`
7
+ ## 功能
9
8
 
10
- 两个可以同时开着。
9
+ - 仅在网线已连接且 Wi-Fi 为 `ZYHY-Private` 时启用分流
10
+ - 监听 macOS 网络与系统代理变化并自动更新,不再每 5 秒轮询
11
+ - 默认每 300 秒低频校验一次,可修改间隔或关闭
12
+ - 支持 `zyhy-network refresh` 立即刷新 DNS、路由和分流配置
13
+ - GitHub 与 npm 安装提供相同的 `zyhy-network` 命令
11
14
 
12
- ## 你需要
15
+ ## 安装
13
16
 
14
- 1. 一台 Mac
15
- 2. 插上公司网线
16
- 3. Wi-Fi 连上 **ZYHY-Private**(不要连别的公司 Wi-Fi)
17
+ GitHub:
17
18
 
18
- ## 怎么装
19
-
20
- 打开「终端」,把下面三行粘贴进去,回车,输入开机密码:
19
+ 请按顺序逐行执行下面三条命令。安装完成后不需要再手动运行其他脚本。
21
20
 
22
21
  ```bash
23
22
  git clone https://github.com/lampardrodgers/ZYHY-Network.git
@@ -25,50 +24,34 @@ cd ZYHY-Network
25
24
  sudo ./install.sh
26
25
  ```
27
26
 
28
- 装完后:
29
-
30
- 1. 用 **新标签页** 打开 OA、邮箱(不要刷新刚才失败的那一页)
31
- 2. 如果用的是 Chrome:设置 → 隐私和安全 → 安全 → 关掉「使用安全 DNS」
32
-
33
- 能打开就行,不用再配域名。
34
-
35
- ## 用的时候注意
36
-
37
- - 网线要插着,Wi-Fi 必须是 `ZYHY-Private`,分流才会开
38
- - 拔掉网线、或换成别的 Wi-Fi:全部按系统原来的方式上网,内网入口可能会打不开
39
- - 插回网线并连回 `ZYHY-Private`:等几秒会自动恢复
40
-
41
- 后台会监听 macOS 的网络状态变化,只在有线网、公司 Wi-Fi、网关或 DNS
42
- 等实际发生变化时更新分流配置。另有每 5 分钟一次的低频校验,用来补偿
43
- 偶尔漏掉的系统事件和内网域名解析变化;配置内容没变时不会重写网络设置。
44
-
45
- GitHub 安装和 npm 安装提供完全相同的命令。需要立即重新解析内网 DNS
46
- 并校验全部分流配置时:
47
-
48
- ```bash
49
- zyhy-network refresh
50
- ```
51
-
52
- ## npm 命令行版本
27
+ `sudo ./install.sh` 会安装并启动后台服务。之后,网线、Wi-Fi、IP、网关或
28
+ DNS 发生变化时,程序会自动重新判断是否启用内网分流;网络状态没有变化时
29
+ 不会重复修改系统网络配置。
53
30
 
54
- 也可以从 npm 安装:
31
+ npm
55
32
 
56
33
  ```bash
57
34
  npm install -g @sunjiehao/zyhy-network
58
35
  zyhy-network install
59
36
  ```
60
37
 
61
- 无论使用哪种安装方式,需要修改系统配置时都会自动请求管理员权限。常用命令:
38
+ 需要修改系统配置时,命令会自动请求管理员权限。
39
+
40
+ ## 常用命令
62
41
 
63
42
  ```bash
64
- zyhy-network status
65
- zyhy-network refresh
66
- zyhy-network add '*.hq.cmcc'
67
- zyhy-network uninstall
43
+ zyhy-network status # 查看状态
44
+ zyhy-network refresh # 强制刷新
45
+ zyhy-network interval # 查看定时校验设置
46
+ zyhy-network interval 600 # 改为每 600 秒校验
47
+ zyhy-network interval 0 # 关闭定时校验
48
+ zyhy-network add '*.hq.cmcc' # 添加域名后缀
49
+ zyhy-network add 172.21.0.0/16 # 添加网段
50
+ zyhy-network uninstall # 卸载,保留配置
68
51
  ```
69
52
 
70
- ## 不想用了
53
+ ## 注意
71
54
 
72
- ```bash
73
- zyhy-network uninstall
74
- ```
55
+ - 网线和 `ZYHY-Private` Wi-Fi 需要同时连接;网络变化后会自动恢复分流
56
+ - 系统代理工具重启或切换后,会自动恢复内网域名的代理绕过项
57
+ - Chrome 若无法打开内网,请关闭“设置 → 隐私和安全 → 安全 → 使用安全 DNS”
package/bin/zyhy-network CHANGED
@@ -16,6 +16,11 @@ case "$command" in
16
16
  exec /usr/bin/sudo "$TARGET" "$@"
17
17
  fi
18
18
  ;;
19
+ interval)
20
+ if [[ $# -gt 1 && $(id -u) -ne 0 ]]; then
21
+ exec /usr/bin/sudo "$TARGET" "$@"
22
+ fi
23
+ ;;
19
24
  esac
20
25
 
21
26
  exec "$TARGET" "$@"
@@ -29,11 +29,12 @@ const rootCommands = new Set([
29
29
  'remove',
30
30
  'uninstall'
31
31
  ]);
32
+ const intervalNeedsRoot = command === 'interval' && args.length > 1;
32
33
 
33
34
  let executable = '/bin/bash';
34
35
  let commandArgs = [script, ...args];
35
36
 
36
- if (rootCommands.has(command) && process.getuid?.() !== 0) {
37
+ if ((rootCommands.has(command) || intervalNeedsRoot) && process.getuid?.() !== 0) {
37
38
  executable = '/usr/bin/sudo';
38
39
  commandArgs = ['/bin/bash', script, ...args];
39
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sunjiehao/zyhy-network",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "macOS Ethernet and ZYHY-Private Wi-Fi split routing CLI",
5
5
  "bin": {
6
6
  "zyhy-network": "bin/zyhy-network.js"
@@ -17,7 +17,7 @@
17
17
  set -euo pipefail
18
18
 
19
19
  LABEL="com.zyhy.network.split"
20
- VERSION="0.2.1"
20
+ VERSION="0.2.3"
21
21
  PLIST="/Library/LaunchDaemons/${LABEL}.plist"
22
22
  RECONCILE_LABEL="${LABEL}.reconcile"
23
23
  RECONCILE_PLIST="/Library/LaunchDaemons/${RECONCILE_LABEL}.plist"
@@ -87,6 +87,7 @@ save_config() {
87
87
  # add hostname: sudo zyhy-private-split add foo.hq.cmcc
88
88
  # add suffix: sudo zyhy-private-split add '*.hq.cmcc'
89
89
  # add CIDR: sudo zyhy-private-split add 172.21.0.0/16
90
+ # timed check: sudo zyhy-private-split interval 300 (0 disables)
90
91
 
91
92
  WIFI_DOMAIN="${WIFI_DOMAIN}"
92
93
  DOMAINS="${DOMAINS}"
@@ -316,7 +317,8 @@ http_proxy_keys() {
316
317
  echo "State:/Network/Global/Proxies"
317
318
  while IFS= read -r key; do
318
319
  [[ -n "$key" ]] || continue
319
- if echo "show ${key}" | scutil | grep -q 'HTTPEnable : 1'; then
320
+ if echo "show ${key}" | scutil \
321
+ | grep -Eq '(HTTPEnable|HTTPSEnable|SOCKSEnable|ProxyAutoConfigEnable) : 1'; then
320
322
  printf '%s\n' "$key"
321
323
  fi
322
324
  done < <(echo "list State:/Network/Service/.*/Proxies" | scutil | awk -F'= ' '/subKey/{print $2}')
@@ -335,12 +337,11 @@ read_proxy_exceptions() {
335
337
  '
336
338
  }
337
339
 
338
- write_proxy_exceptions() {
340
+ write_proxy_exceptions_for_key() {
341
+ local key=$1
342
+ shift
339
343
  local items=$*
340
- local key
341
- [[ -n "$items" ]] || return 0
342
- while IFS= read -r key; do
343
- [[ -n "$key" ]] || continue
344
+ if [[ -n "$items" ]]; then
344
345
  scutil >/dev/null <<EOF
345
346
  open
346
347
  d.init
@@ -349,7 +350,16 @@ d.add ExceptionsList * ${items}
349
350
  set ${key}
350
351
  quit
351
352
  EOF
352
- done < <(http_proxy_keys | awk 'NF && !seen[$0]++')
353
+ else
354
+ scutil >/dev/null <<EOF
355
+ open
356
+ d.init
357
+ get ${key}
358
+ d.remove ExceptionsList
359
+ set ${key}
360
+ quit
361
+ EOF
362
+ fi
353
363
  }
354
364
 
355
365
  list_has() {
@@ -363,51 +373,69 @@ list_has() {
363
373
  }
364
374
 
365
375
  ensure_proxy_exceptions() {
366
- local -a cur=() want=() merged=()
367
- local x
368
- while IFS= read -r x; do
369
- [[ -n "$x" ]] && cur+=("$x")
370
- done < <(read_proxy_exceptions "")
376
+ local -a want=()
377
+ local key x changed=0
371
378
  while IFS= read -r x; do
372
379
  [[ -n "$x" ]] && want+=("$x")
373
380
  done < <(wanted_proxy_exceptions | awk 'NF && !seen[$0]++')
374
- if [[ ${#cur[@]} -gt 0 ]]; then
375
- merged=("${cur[@]}")
376
- fi
377
- for x in "${want[@]}"; do
378
- if [[ ${#cur[@]} -gt 0 ]] && list_has "$x" "${cur[@]}"; then
381
+
382
+ while IFS= read -r key; do
383
+ [[ -n "$key" ]] || continue
384
+ local -a cur=() merged=()
385
+ while IFS= read -r x; do
386
+ [[ -n "$x" ]] && cur+=("$x")
387
+ done < <(read_proxy_exceptions "$key")
388
+ if [[ ${#cur[@]} -gt 0 ]]; then
389
+ merged=("${cur[@]}")
390
+ fi
391
+ for x in "${want[@]}"; do
392
+ if [[ ${#cur[@]} -gt 0 ]] && list_has "$x" "${cur[@]}"; then
393
+ continue
394
+ fi
395
+ merged+=("$x")
396
+ done
397
+ if [[ ${#merged[@]} -eq ${#cur[@]} ]]; then
379
398
  continue
380
399
  fi
381
- merged+=("$x")
382
- done
383
- if [[ ${#merged[@]} -eq ${#cur[@]} ]]; then
384
- return 0
400
+ write_proxy_exceptions_for_key "$key" "${merged[@]}"
401
+ changed=1
402
+ done < <(http_proxy_keys | awk 'NF && !seen[$0]++')
403
+
404
+ if (( changed )); then
405
+ log "proxy bypass +${want[*]}"
385
406
  fi
386
- write_proxy_exceptions "${merged[*]}"
387
- log "proxy bypass +${want[*]}"
388
407
  }
389
408
 
390
409
  remove_proxy_exceptions() {
391
- local -a cur=() keep=() want=()
392
- local x
393
- while IFS= read -r x; do
394
- [[ -n "$x" ]] && cur+=("$x")
395
- done < <(read_proxy_exceptions "")
396
- [[ ${#cur[@]} -gt 0 ]] || return 0
410
+ local -a want=()
411
+ local key x changed=0
397
412
  while IFS= read -r x; do
398
413
  [[ -n "$x" ]] && want+=("$x")
399
414
  done < <(wanted_proxy_exceptions)
400
- for x in "${cur[@]}"; do
401
- if [[ ${#want[@]} -gt 0 ]] && list_has "$x" "${want[@]}"; then
415
+
416
+ while IFS= read -r key; do
417
+ [[ -n "$key" ]] || continue
418
+ local -a cur=() keep=()
419
+ while IFS= read -r x; do
420
+ [[ -n "$x" ]] && cur+=("$x")
421
+ done < <(read_proxy_exceptions "$key")
422
+ [[ ${#cur[@]} -gt 0 ]] || continue
423
+ for x in "${cur[@]}"; do
424
+ if [[ ${#want[@]} -gt 0 ]] && list_has "$x" "${want[@]}"; then
425
+ continue
426
+ fi
427
+ keep+=("$x")
428
+ done
429
+ if [[ ${#keep[@]} -eq ${#cur[@]} ]]; then
402
430
  continue
403
431
  fi
404
- keep+=("$x")
405
- done
406
- if [[ ${#keep[@]} -eq ${#cur[@]} ]]; then
407
- return 0
432
+ write_proxy_exceptions_for_key "$key" "${keep[@]}"
433
+ changed=1
434
+ done < <(http_proxy_keys | awk 'NF && !seen[$0]++')
435
+
436
+ if (( changed )); then
437
+ log "proxy bypass restored"
408
438
  fi
409
- write_proxy_exceptions "${keep[*]}"
410
- log "proxy bypass restored"
411
439
  }
412
440
 
413
441
  drop_state_entry() {
@@ -505,6 +533,10 @@ cmd_apply_locked() {
505
533
  return 0
506
534
  fi
507
535
 
536
+ # Proxy apps can replace ExceptionsList without changing IP, gateway, or DNS.
537
+ # Repair it before the network-input fast path so proxy-only events are enough.
538
+ ensure_proxy_exceptions
539
+
508
540
  local input_tmp
509
541
  input_tmp=$(mktemp)
510
542
  write_network_inputs "$input_tmp" "$eth" "$wifi" "$wifi_ip" "$wifi_gw" "$wifi_dns"
@@ -554,7 +586,6 @@ cmd_apply_locked() {
554
586
 
555
587
  write_resolvers "$wifi_dns"
556
588
  write_hosts_block "$hosts_body"
557
- ensure_proxy_exceptions
558
589
 
559
590
  local tmp state_changed=0
560
591
  tmp=$(mktemp)
@@ -617,6 +648,41 @@ cmd_refresh() {
617
648
  echo "refreshed: routes, DNS, hosts, resolvers and proxy exceptions checked"
618
649
  }
619
650
 
651
+ cmd_interval() {
652
+ load_config
653
+ local seconds=${1-}
654
+ if [[ -z "$seconds" ]]; then
655
+ if (( RECONCILE_INTERVAL == 0 )); then
656
+ echo "timed check: disabled (0 seconds)"
657
+ else
658
+ echo "timed check: every ${RECONCILE_INTERVAL} seconds"
659
+ fi
660
+ return
661
+ fi
662
+
663
+ require_root
664
+ [[ "$seconds" =~ ^[0-9]+$ ]] || {
665
+ echo "usage: $0 interval <seconds>; 0 disables timed checks" >&2
666
+ exit 2
667
+ }
668
+ RECONCILE_INTERVAL=$seconds
669
+ save_config
670
+
671
+ launchctl bootout "system/${RECONCILE_LABEL}" >/dev/null 2>&1 || true
672
+ if [[ -x "$INSTALL_BIN" && -f "$PLIST" ]]; then
673
+ write_install_plists "$INSTALL_BIN"
674
+ if (( RECONCILE_INTERVAL > 0 )); then
675
+ bootstrap_with_retry "$RECONCILE_PLIST"
676
+ fi
677
+ fi
678
+
679
+ if (( RECONCILE_INTERVAL == 0 )); then
680
+ echo "timed check disabled; network-change watching remains enabled"
681
+ else
682
+ echo "timed check interval: ${RECONCILE_INTERVAL} seconds"
683
+ fi
684
+ }
685
+
620
686
  cmd_status() {
621
687
  load_config
622
688
  local wifi eth pair
@@ -657,7 +723,11 @@ cmd_status() {
657
723
  $0==e{on=0}
658
724
  ' /etc/hosts || true
659
725
  echo "watcher : $(launchctl print "system/${LABEL}" >/dev/null 2>&1 && echo loaded || echo not-loaded)"
660
- echo "reconcile : $(launchctl print "system/${RECONCILE_LABEL}" >/dev/null 2>&1 && echo "loaded (${RECONCILE_INTERVAL}s)" || echo not-loaded)"
726
+ if (( RECONCILE_INTERVAL == 0 )); then
727
+ echo "reconcile : disabled (0s)"
728
+ else
729
+ echo "reconcile : $(launchctl print "system/${RECONCILE_LABEL}" >/dev/null 2>&1 && echo "loaded (${RECONCILE_INTERVAL}s)" || echo "not-loaded (${RECONCILE_INTERVAL}s)")"
730
+ fi
661
731
  echo "sys proxy : $(scutil --proxy | awk '/HTTPEnable/{e=$3} /HTTPProxy/{p=$3} /HTTPPort/{n=$3} END{if(e==1) print p":"n; else print "off"}')"
662
732
  echo "proxy bypass:"
663
733
  read_proxy_exceptions | sed 's/^/ /' || true
@@ -756,8 +826,8 @@ start_network_event_stream() {
756
826
  exec 3>"$WATCH_COMMAND_FIFO"
757
827
  exec 4<"$WATCH_EVENT_FIFO"
758
828
  cat >&3 <<'EOF'
759
- n.add ^State:/Network/(Global/(IPv4|DNS)|Interface/.*/(IPv4|Link|AirPort)|Service/.*/(DHCP|DNS|IPv4))$ pattern
760
- n.add ^Setup:/Network/Service/.*/(DNS|IPv4)$ pattern
829
+ n.add ^State:/Network/(Global/(IPv4|DNS|Proxies)|Interface/.*/(IPv4|Link|AirPort)|Service/.*/(DHCP|DNS|IPv4|Proxies))$ pattern
830
+ n.add ^Setup:/Network/(Global/Proxies|Service/.*/(DNS|IPv4|Proxies))$ pattern
761
831
  n.watch
762
832
  EOF
763
833
  }
@@ -817,6 +887,12 @@ write_install_plists() {
817
887
  </plist>
818
888
  EOF
819
889
 
890
+ chmod 644 "$PLIST"
891
+ if (( RECONCILE_INTERVAL == 0 )); then
892
+ rm -f "$RECONCILE_PLIST"
893
+ return
894
+ fi
895
+
820
896
  cat >"$RECONCILE_PLIST" <<EOF
821
897
  <?xml version="1.0" encoding="UTF-8"?>
822
898
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -839,7 +915,7 @@ EOF
839
915
  </dict>
840
916
  </plist>
841
917
  EOF
842
- chmod 644 "$PLIST" "$RECONCILE_PLIST"
918
+ chmod 644 "$RECONCILE_PLIST"
843
919
  }
844
920
 
845
921
  bootstrap_with_retry() {
@@ -880,7 +956,7 @@ cmd_install() {
880
956
  write_install_plists "$INSTALL_BIN"
881
957
  cmd_apply --force
882
958
  bootstrap_with_retry "$PLIST"
883
- if ! bootstrap_with_retry "$RECONCILE_PLIST"; then
959
+ if (( RECONCILE_INTERVAL > 0 )) && ! bootstrap_with_retry "$RECONCILE_PLIST"; then
884
960
  launchctl bootout "system/${LABEL}" >/dev/null 2>&1 || true
885
961
  return 1
886
962
  fi
@@ -957,6 +1033,7 @@ usage: $0 <command>
957
1033
  add <item> add host, '*.suffix', or CIDR (needs sudo)
958
1034
  remove <item> remove one of the above (needs sudo)
959
1035
  refresh force DNS resolution and reconcile now (needs sudo)
1036
+ interval [seconds] show/set timed-check interval; 0 disables (set needs sudo)
960
1037
  apply [--force] apply now; --force also reconciles DNS (needs sudo)
961
1038
  clear tear down routes (needs sudo)
962
1039
  install copy to /usr/local/sbin and load daemon (needs sudo)
@@ -972,6 +1049,7 @@ load_config
972
1049
  case "$cmd" in
973
1050
  apply) cmd_apply "${1-}" ;;
974
1051
  refresh) cmd_refresh ;;
1052
+ interval) cmd_interval "${1-}" ;;
975
1053
  watch) cmd_watch ;;
976
1054
  clear) cmd_clear ;;
977
1055
  status) cmd_status ;;
@@ -5,6 +5,8 @@ DOMAINS="oa.hq.cmcc mail.cmhi.chinamobile.com"
5
5
  SUFFIXES="hq.cmcc cmhi.chinamobile.com"
6
6
  CIDRS="172.21.0.0/16 172.16.0.0/16"
7
7
 
8
- # 网络变化后等待接口稳定的秒数;另每 300 秒做一次只读为主的兜底校验。
8
+ # 网络变化后等待接口稳定的秒数。
9
9
  EVENT_SETTLE_SECONDS="2"
10
+
11
+ # 定时兜底校验间隔(秒);设为 0 可关闭,网络变化事件监听不受影响。
10
12
  RECONCILE_INTERVAL="300"