惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
云风的 BLOG
云风的 BLOG
Microsoft Security Blog
Microsoft Security Blog
博客园 - 司徒正美
Y
Y Combinator Blog
Stack Overflow Blog
Stack Overflow Blog
雷峰网
雷峰网
小众软件
小众软件
GbyAI
GbyAI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
aimingoo的专栏
aimingoo的专栏
MyScale Blog
MyScale Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
A
About on SuperTechFans
宝玉的分享
宝玉的分享
WordPress大学
WordPress大学
B
Blog RSS Feed
G
Google Developers Blog
量子位
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 三生石上(FineUI控件)

依云's Blog

Wayfire支持不缩放Xwayland啦 - 依云's Blog 使用wayvnc远程访问无头Wayfire会话 - 依云's Blog pacfiles: 高速的 pacman -F 替代品 用 Android 手机当电脑的话筒 - 依云's Blog 使用 ffmpeg 对音频文件进行响度归一化 - 依云's Blog 使用 nftables 屏蔽大量 IP - 依云's Blog YubiKey 初体验 - 依云's Blog fcitx5 码表同步方案 - 依云's Blog 我正在使用的火狐扩展(2024年版) - 依云's Blog 使用 PipeWire 实现自动应用均衡器 如果你发现你的 OOM Killer 在乱杀进程 使用 atuin 管理 shell 命令历史 btrfs 元数据满了怎么办 btrfs 翻车记 在 nspawn 里运行 docker Linux 上的字体配置与故障排除 新的 PaddleOCR 部署方案 使用 EasyEffects 调整 Bose 音箱的体验 让离线软件真正离线 我所讨厌的网页行为 tmux 状态栏优化 Google Chrome 中的字体设置 从 getmail6 到 offlineimap 微信消息通知的困扰 Qt 的字体渲染问题 Wayfire 迁移进展(四):不那么 high 的 DPI Wayfire 迁移进展(二):Xwayland HiDPI 以及 waybar Wayfire 迁移进展 不同情况下的图形效果 Wayland 初体验
通过 UDP 打洞连接 NAT 后边的 mosh
依云 · 2013-10-13 · via 依云's Blog

又是一篇关于 UDP 打洞的文章。之前写过关于在完全圆锥型(full cone)NAT的文章中如何使用 socat 命令打洞。根据那篇文章里的知识,连接到一个 full cone NAT 后边的 mosh 不成问题。不过,我现在的网络是受限圆锥型(restricted cone)NAT 了呢!

也就是复杂了一些。双方要向中间服务器和对方都发送数据包才可以。另外就是,客户端(mosh-client)这边得使用在打洞期间使用的端口号才行。

打洞流程根据维基百科,双方通过中间服务器(还是我的 udpaddr 啦)交换地址,双方均向得到的地址发送一数据包,然后开始正常通讯。比较麻烦,于是有了这个脚本

#!/usr/bin/env python3

import socket
import re
import sys
import subprocess

udp_server = ('xmpp.vim-cn.com', 2727)
addr_re = re.compile(r"\('(?P<ip>[^']+)', (?P<port>\d+)(?:, (?P<cport>\d+))?")

def main(server):
  sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  sock.settimeout(2)
  print('Send message...')
  sock.sendto(b'req from holepunch.py\n', udp_server)
  msg, addr = sock.recvfrom(1024)
  print('Got answer from %s: %s' % (addr, msg))
  m = addr_re.search(msg.decode())
  if not m:
    print("Error: can't parse answer.")
    sys.exit(1)
  m_ip = m.group('ip')
  m_port = int(m.group('port'))
  port = sock.getsockname()[1]
  print('Got my IP and Port: (%r, %s, %s).' % (m_ip, m_port, port))

  msg = input('> Peer address: ')
  m = addr_re.search(msg)
  if not m:
    print("Error: can't parse input.")
    sys.exit(2)
  p_ip = m.group('ip')
  p_port = int(m.group('port'))
  c_port = int(m.group('cport'))

  print('send initial packet and wait for answer...')
  sock.sendto(b'HELO\n', (p_ip, p_port))
  try:
    msg = sock.recvfrom(1024)
    print('Received:', msg)
  except socket.timeout:
    print("Timed out (it's normal).")

  if server:
    sock.close()
    print('Starting mosh server...')
    msg = subprocess.check_output(['mosh-server', 'new', '-p', str(port)])
    secret = msg.split()[3].decode()
    print('Connect with:\nMOSH_KEY=%s MOSH_CPORT=%s mosh-client %s %s' % (secret, c_port, m_ip, m_port))
  else:
    print('done.')

if __name__ == '__main__':
  server = len(sys.argv) == 2 and sys.argv[1] == '-s'
  main(server)

如果 mosh 服务器端位于受限 NAT 后,还需要给 mosh-client 打个(我随手写的很 dirty 的)补丁以便指定客户端使用的 UDP 端口号:

diff --git a/src/network/network.cc b/src/network/network.cc
index 2f4e0bf..718f6c5 100644
--- a/src/network/network.cc
+++ b/src/network/network.cc
@@ -176,6 +176,11 @@ Connection::Socket::Socket()
     perror( "setsockopt( IP_RECVTOS )" );
   }
 #endif
+
+  if ( getenv("MOSH_CPORT") ) {
+    int port = atoi(getenv("MOSH_CPORT"));
+    try_bind( _fd, INADDR_ANY, port, port);
+  }
 }

 void Connection::setup( void )

然后,连接流程如下:

  1. mosh 服务端运行holepunch.py -s命令,客户端运行holepunch.py
  2. 双方看到自己的地址信息(依次是IP, 外网端口, 本地端口)后,复制并发送给对方;
  3. 双方输入对方的地址。为了节省时间(mosh-server 只会等一分钟,NAT 上的端口映射也是有时效的),只要输入的一行内包含上述地址信息的文本即可,前后可以有不小心复制过来的多余字符;
  4. 服务端将得到一行包含 mosh 的密钥的命令。将此命令发送对客户端;客户端运行此命令连接。如果 mosh-client 的名字不是标准名字,需要自行修改;
  5. 一切顺利的话就连接上啦!

mosh 服务端输出示例:

>>> holepunch.py -s
Send message...
Got answer from ('202.133.113.62', 2727): b"Your address is ('180.109.80.47', 3169)\n"
Got my IP and Port: ('180.109.80.47', 3169, 8127).
> Peer address:  Port: ('222.95.148.73', 5223, 55473). 
send initial packet and wait for answer...
Timed out (it's normal).
Starting mosh server...

mosh-server (mosh 1.2.4)
Copyright 2012 Keith Winstein <mosh-devel@mit.edu>
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

[mosh-server detached, pid = 5202]
Connect with:
MOSH_KEY=1apguNqtfN/K4JSvllnJxA MOSH_CPORT=55473 mosh-client 222.95.148.73 5223

mosh 客户端输出示例:

>>> holepunch.py
Send message...
Got answer from ('202.133.113.62', 2727): b"Your address is ('222.95.148.73', 5223)\n"
Got my IP and Port: ('222.95.148.73', 5223, 55473).
> Peer address: rt: ('180.109.80.47', 3169, 8127)
send initial packet and wait for answer...
Timed out (it's normal).
done.

2013年10月20日更新:想要通过打出来的 UDP 洞进行 TCP 通信吗?参见文章通过 OpenVPN 让 TCP 使用 UDP 洞