MSYS/Cygwin使用什么机制来模拟Unix域套接字?

15

我正在尝试编写一款使用C#语言的软件,与使用MSYS构建的另一款软件通过(MSYS模拟的)Unix域套接字进行通信。我了解到“套接字服务器”(我不确定正确术语是什么)会创建一个临时文件,其中包含以下内容:

!<socket >59108 282F93E1-9E2D051A-46B57EFC-64A1852F

59108对应一个TCP端口,"socket server"正在监听回环接口上的该端口。使用数据包捕获工具,我已经确定"socket client"连接到了这个端口,并且信息通过回环接口交换。

我在我的软件中复制了这种行为,"socket client"连接到我的监听端口,但没有传输信息。我相信还有另一步操作,最可能涉及"socket"文件中的GUID,但我无法确定是什么。我需要做什么才能触发客户端与服务器的通信?

似乎MSYS使用了Cygwin的机制,其中涉及命名事件,该事件(可能)由"server"创建并(显然)被"server"发出信号,但是我天真的尝试实现似乎没有起作用。

我找到了Conrad Scott写的电子邮件,其中描述了"握手"过程的各种缺陷,并提出了一种据称可以解决它们的补丁。在这封电子邮件中,Conrad描述了所使用的过程,他指出实际上有两个事件,一个由"server"管理,另一个由"client"管理。我使用API Monitor查找CreateEvent()的调用,虽然有几个,但我找不到一个看起来像"smoking gun"(关键证据)的。也没有有趣的CreateSemaphore()调用,因此看起来Conrad的补丁从未被应用过(或者至少在MSYS分支Cygwin之后应用了一些时间)。

3个回答

9

看起来divB和Mark的答案都是正确的,但他们都省略了一些细节,所以这里希望能更加完整。

这里有两种不同的实现方式。我没有对哪个实现使用了哪种实现进行详尽的调查,但截至本文撰写时,当前版本的cygwin使用divB描述的实现,而MsysGit使用Mark描述的实现。

服务器初始化:

  1. Create a socket (AddressFamily = IPv4, Type = Stream, Protocol = TCP). (.NET/MFC)
  2. Bind it to loopback (127.0.0.1). (.NET/MFC)
  3. Tell the socket to listen. (.NET/MFC)
  4. Generate a random 16-byte GUID.
  5. Create a file with the following contents based on the TCP port and the GUID. Using the original example where 59108 is the TCP port and 282F93E1-9E2D051A-46B57EFC-64A1852F is the GUID.

    In the cygwin implementation, the socket file contents are:

    !<socket >59108 s 282F93E1-9E2D051A-46B57EFC-64A1852F
    

    And in the msysgit implementation, the socket file contents are:

    !<socket >59108 282F93E1-9E2D051A-46B57EFC-64A1852F
    

    The difference being the extra "s" between the port and the GUID.

  6. Set the System attribute on this file.

  7. In msysgit implementation only, Create a named wait handle with the name cygwin.local_socket.secret.58598.282F93E1-9E2D051A-46B57EFC-64A1852F (InitalState = False, Reset = AutoReset). (.NET/MFC)

    58598 is derived by using a HostToNetworkOrder function on the port (as 16-bit unsigned integer). i.e. 59108 == 0xE6E4 and 58598 == 0xE4E6.

处理连接:

  1. 接受传入的套接字。 (.NET/MFC).
  2. 在cygwin实现中,进行握手,包括以下步骤:
    1. 读取16个字节。如果它们与GUID不匹配,则失败。
    2. 发送相同的16个字节。
    3. 读取12个字节作为3个32位整数。它们是调用进程的pid、uid和gid。
    4. 发送12个字节(3个32位整数)。使用服务器的pid和接收到的uid和gid。
  3. 在msysgit实现中,通过以下方式与客户端同步:
    1. 获取传入套接字的端口号。在本例中,我们假设它是63524
    2. 打开客户端的现有等待句柄。(.NET/MFC)。您需要像为服务器所做的那样将端口转换为网络字节顺序。因此,在本例中,名称为cygwin.local_socket.secret.9464.282F93E1-9E2D051A-46B57EFC-64A1852F
    3. 向服务器发送信号并等待客户端(ToSignal=server,WaitOn=client,Timeout=10000毫秒,ExitContext/Alertable=False)。(.NET/MFC)。不太确定ExitContext/Alertable参数,但False似乎可行。
  4. 将套接字移交给(希望已经存在的)用于处理您正在进行的任何操作的代码(在我们三个人的情况下,似乎是ssh代理)。

1
根据 https://cygwin.com/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=winsup/cygwin/fhandler_socket.cc;hb=a9f4b71e8e63c363cd461aec3e0910dfd492e777#l345 ,进程凭证的顺序应该是“pid,uid,gid”,而不是在步骤2.3和2.4中指定的“pid,gid,uid”。 - abourget
1
对于感兴趣的人,我在这里用Go编写了一个成功的实现:https://github.com/abourget/secrets-bridge - abourget

6
至少对于cygwin,我现在可以回答你的问题了:我刚刚使用MFC实现了一个兼容cygwin的套接字服务器。 我是通过查看cygwin源代码来完成的。好像甚至没有事件。 所以你提到的补丁似乎没有被实施。
所发生的一切都是:
1.)创建套接字文件,GUID(“共享密钥”)只是随机数。 2.)该文件必须具有“系统”属性。 如果它在NTFS上,则cygwin代码会执行一些奇怪的权限操作,我还没有研究过。 3.)在本地主机上创建了一个网络套接字,并在套接字文件中指示端口。
因此,当客户端通过TCP / IP连接到套接字时:
4.) 它首先将4个随机数发送到服务器; 服务器检查它们是否有效 5.) 服务器将它们发送回来 6.) 客户端发送3个32位数字:pid、uid和gid 7.) 服务器返回自己版本的这些数字。
我不明白这个握手的目的是什么,因为从安全角度来看,它是完全无价值的。

顺便提一下,这也适用于MSYS,我的套接字服务器(一个SSH代理)适用于cygwin和MSYS的ssh/rsync客户端。 - divB

5
我已经为Git附带的OpenSSH (ssh-agent.exe)版本编写了一个正确运行的解决方案:
服务器端的设置包括以下步骤: 1. 创建一个“秘密字符串”,由四组以短横线(“-”)分隔的八位十六进制数字组成。 2. 监听本地端口。 3. 使用模式`EventResetMode.AutoReset`创建名为cygwin.local_socket.secret.[secret string].[以反转字节顺序表示的监听端口号]的`EventWaitHandle`。 4. 写出“套接字”文件,其中包含字符串![以非反转字节顺序表示的端口号][秘密字符串]。
当有连接时,必须执行以下步骤: 1. 使用事件名称cygwin.local_socket.secret.[以反转字节顺序表示的远程端口号].[秘密字符串]使用`EventWaitHandle.OpenExisting()`打开客户端的事件处理程序。 2. 发送服务器的事件句柄并等待客户端的等待句柄被信号化,使用`EventWaitHandle.SignalAndWait()`。
我同意它看起来像邮件列表上讨论的补丁从未应用。我设计的顺序更接近那个列表上讨论的顺序,并且也匹配我从Cygwin中挖掘出来的代码。
我不理解我发现的功能与divB发现的功能之间的差异,但我确认它可以与我使用的软件(Git的OpenSSH)一起运行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接