Generic sockets with Socat

Generic sockets with Socat
使用 Socat 的通用套接字

Introduction 介绍

Beginning with version 1.7.0 socat provides means to freely control important aspects of socket handling. This allows to experiment with socket types and protocols that are not explicitely implemented in socat.
从版本 1.7.0 开始,socat 提供了自由控制套接字处理的重要方面的手段。这允许尝试在 socat 中未明确实现的套接字类型和协议。

The related socat features fall into three major categories:
相关的 socat 功能分为三大类:

In practice this gives you two possibilities:
实际上,这给您两种可能性:

If you want to cope with sockets staying within the usual domains ( = protocol families = address families) which are IPv4, IPv6, UNIX/local, and raw interface for socat 1.7.0, it is sufficient to learn about a couple of address options that allow to change default parameters, and to apply generic socket options.
如果您想处理套接字保持在通常的域内(=协议族=地址族),这些域包括 IPv4、IPv6、UNIX/local 和 socat 1.7.0 的原始接口,只需了解一些地址选项即可,这些选项允许更改默认参数,并应用通用套接字选项。

For other address families socat provides generic socket addresses.
对于其他地址族,socat 提供通用套接字地址。

Generic socket options 通用套接字选项

Example 1: DCCP communication
示例 1:DCCP 通信

A relatively new communication protocol has been introduced in the Internet community for which no socat address type has been implemented up to version 1.7.0 (see IETF's Datagram Congestion Control Protocol and Linux foundation Net:DCCP for more info). Taken that the operating system implements DCCP, it is possible to use this protocol with socat while just employing standard socket addresses and some options.
一个相对较新的通信协议已经引入互联网社区,截至版本 1.7.0,尚未实现任何 socat 地址类型(请参阅 IETF 的 Datagram Congestion Control Protocol 和 Linux 基金会的 Net:DCCP 了解更多信息)。假设操作系统实现了 DCCP,可以在 socat 中使用此协议,只需使用标准套接字地址和一些选项。

A simple server that accepts a DCCP connection, passes the arriving data to a subprocess for converting upper case to lower case characters, and then returns it to the client:
一个简单的服务器接受 DCCP 连接,将到达的数据传递给子进程以将大写字符转换为小写字符,然后将其返回给客户端:

socat TCP4-LISTEN:4096,reuseaddr,type=6,prototype=33 exec:'tr A-Z a-z',pty,raw,echo=0

A simple client that sends some upper case characters to the server via DCCP and prints what the server returns:
一个简单的客户端,通过 DCCP 将一些大写字符发送到服务器,并打印服务器返回的内容:

echo ABCD |socat - TCP4-CONNECT:localhost:4096,type=6,prototype=33

We choose the TCP4 addresses as base because it best matches the DCCP requirements:
我们选择 TCP4 地址作为基础,因为它最符合 DCCP 的要求:

  1. DCCP is (here) based on IPv4
    DCCP 是基于 IPv4 的
  2. DCCP is stream oriented and uses connect() and listen(); accept() calls
    DCCP 是面向流的,并使用 connect()listen(); accept() 调用
  3. DCCP protocol uses ports
    DCCP 协议使用端口

Option type=6 changes TCP's SOCK_STREAM parameter to SOCK_DCCP, and prototype=33 replaces the default IPPROTO_TCP with IPPROTO_DCCP.
选项 type=6 将 TCP 的 SOCK_STREAM 参数更改为 SOCK_DCCP ,而 prototype=33 将默认的 IPPROTO_TCP 替换为 IPPROTO_DCCP

DCCP has an important parameter, the service code. It provides another multiplexing layer beyond the protocol ports. The Linux implementation of DCCP allows to set this parameter with code like setsocktopt(fd, SOL_DCCP, DCCP_SOCKOPT_SERVICE, {1}, sizeof(int)). The equivalent generic socat option is: setsockopt-int=269:2:1 for service code 1. If the service codes on server and client do not match the connect() operation fails with error:
DCCP 有一个重要的参数,即服务代码。它提供了协议端口之外的另一个多路复用层。DCCP 的 Linux 实现允许使用类似 setsocktopt(fd, SOL_DCCP, DCCP_SOCKOPT_SERVICE, {1}, sizeof(int)) 的代码设置此参数。等效的通用 socat 选项是: setsockopt-int=269:2:1 用于服务代码 1。如果服务器和客户端上的服务代码不匹配,则 connect() 操作将因错误而失败:

... E connect(3, AF=2 127.0.0.1:4096, 16): Invalid request code

Please note that this examples works with IPv6 as well, you just need to replace the TCP4 words with TCP6, and the IPv4 socket address with an appropriate IPv6 socket address, e.g. [::1]!
请注意,这些示例也适用于 IPv6,您只需将 TCP4 更改为 TCP6,并将 IPv4 套接字地址更改为适当的 IPv6 套接字地址,例如 [::1]

Generic socket addresses
通用套接字地址

socat's generic socket addresses are a more comprehensive mechanism that allows to deal with protocol families whose socket addresses are not supported by socat - no semantical parsing, no structured assignment to the struct components are available. Instead, the socket address records for binding and connecting/sending are specified in unstructured hexadecimal form. The following example demonstrates this by performing simple data transfer over raw AppleTalk protocol.
socat 的通用套接字地址是一种更全面的机制,允许处理协议族的套接字地址不受 socat 支持的情况 - 没有语义解析,也没有将结构化分配给结构组件。相反,用于绑定和连接/发送的套接字地址记录以非结构化十六进制形式指定。以下示例通过在原始 AppleTalk 协议上执行简单数据传输来演示这一点。

Note: I do not have any knowledge about AppleTalk. I just managed to configure my Linux host to tolerate the creation of a receiving and a sending socket. Don't blame me nor ask me for support if it does not work for you.
注意:我对 AppleTalk 一无所知。我只是设法配置我的 Linux 主机以容忍创建一个接收套接字和一个发送套接字。如果对您不起作用,请不要责怪我,也不要向我寻求支持。

Enabling AppleTalk protocol
启用 AppleTalk 协议

Install the netatalk package. Check that /etc/netatalk/atalkd.conf has an entry like eth0 -phase 2 -net 0-65534 -addr 65280.243. The last part is an arbitrary (?) host address, some of the following values must fit it. Make sure the atalkd daemon is running. Run the AppleTalk ping command:
安装 netatalk 包。检查 /etc/netatalk/atalkd.conf 是否有类似 eth0 -phase 2 -net 0-65534 -addr 65280.243 的条目。最后一部分是一个任意的主机地址,一些以下值必须匹配它。确保 atalkd 守护程序正在运行。运行 AppleTalk ping 命令:

aecho 65280.243

If you get an error like:
如果您遇到类似错误:

Device or resource busy

then try to restart atalkd:
然后尝试重新启动 atalkd

/etc/init.d/atalkd restart
/etc/init.d/atalkd 重新启动

When aecho works like ping you are ready for the next step.
aecho 的工作方式类似于 ping 时,您已准备好进行下一步。

Example 2: AppleTalk datagram communication
例子 2:AppleTalk 数据报通信

We start a socat process with a receiver and echo service:
我们启动一个带有接收器和回显服务的 socat 进程:

socat SOCKET-RECVFROM:5:2:0:x40x00x0000x00x00x0000000000000000 PIPE

Then, in another shell on the same host, we start a client socket process that sends data to the server and gets the answer:
然后,在同一台主机上的另一个 shell 中,我们启动一个客户端套接字进程,向服务器发送数据并获取答复:

echo ABCD |socat - SOCKET-DATAGRAM:5:2:0:x40x00xff00xf3x00x0000000000000000

The client process should print the data.
客户端进程应打印数据。

How did this work? The generic socat address has just used the system call parameters that were provided on command line, without knowing anything about AppleTalk sockets and protocol. The values 5, 2, and 0 are directly used for the socket() call: they specify the domain (PF_APPLETALK=5), socket type (SOCK_DGRAM=2), and no protocol (0) - values for Linux. The long hex strings define the socket addresses. They can only be constructed with knowledge of the underlying structure. In /usr/include/linux/atalk.h we find the following declarations:
这是如何工作的?通用的 socat 地址只是使用了在命令行上提供的系统调用参数,而不知道任何关于 AppleTalk sockets 和协议的信息。值 5、2 和 0 直接用于 socket() 调用:它们指定了域( PF_APPLETALK=5 )、套接字类型( SOCK_DGRAM=2 )和无协议(0)- 适用于 Linux 的值。长十六进制字符串定义了套接字地址。只有了解底层结构才能构建它们。在 /usr/include/linux/atalk.h 中,我们找到以下声明:

struct atalk_addr {
        __be16  s_net;
        __u8    s_node;
};

struct sockaddr_at {
        sa_family_t       sat_family;
        __u8              sat_port;
        struct atalk_addr sat_addr;
        char              sat_zero[8];

After rolling out atalk_addr and considering implicit padding by the C programming language we get the following byte map:
在推出 atalk_addr 并考虑 C 编程语言的隐式填充后,我们得到以下字节映射:

componentoffsetlengthvaluemeaning
sat_family02x0005address family 地址族
sat_port21x40port
-31x00padding
sat_addr.s_net42xff00network address 网络地址
sat_addr.s_node61xf3node address 节点地址
-71x00padding
sat_zero88x0000000000000000padding

Note that hexadecimal ff00 is the same as decimal 65280, and hexadecimal xf3 is the same as decimal 243 - these are the numbers specified in atalkd.conf.
请注意,十六进制 ff00 等同于十进制 65280,十六进制 xf3 等同于十进制 243 - 这些是在 atalkd.conf 中指定的数字。

The address family component must be omitted from the socket address because it is added by socat implicitely. The resulting hexadecimal representation of the target socket address is therefore:
由于 socat 隐式添加了地址族组件,因此必须从套接字地址中省略地址族组件。因此,目标套接字地址的十六进制表示如下:

x40x00xff00xf3x00x0000000000000000

The receiver just has to specify the port, so its bind address data is:
接收方只需指定端口,因此其绑定地址数据为:

x40x00x0000x00x00x0000000000000000

Parameters for well known socket types
众所周知的套接字类型的参数

Finding the correct parameters and socket addresses is not always trivial. Therefore this section provides tables with the parameters of common socket types. Some of these types are directly implemented by socat (and other programs). Establishing interoperability between a directly implemented socket and a generic socket might be your first step before entering unknown ground.
找到正确的参数和套接字地址并不总是简单的。因此,本节提供了常见套接字类型参数的表格。其中一些类型是由 socat(和其他程序)直接实现的。在进入未知领域之前,建立直接实现套接字和通用套接字之间的互操作性可能是您的第一步。

Socket parameters 套接字参数

Table: parameter names for "well known" sockets:
表: "众所周知" 套接字的参数名称:

namedomainsocktypeprotocol levelremark
UDP4PF_INETSOCK_DGRAMIPPROTO_UDP SOL_UDP
UDP6PF_INET6SOCK_DGRAMIPPROTO_UDP SOL_UDP
raw IPv4 原始 IPv4PF_INETSOCK_RAWIPPROTO_RAW SOL_IP
raw IPv6 原始 IPv6PF_INET6SOCK_RAWIPPROTO_RAW SOL_IPV6
UNIXPF_LOCALSOCK_DGRAM0 SOL_SOCKET
PACKETPF_PACKETSOCK_RAW768SOL_PACKETtcpdump (include layer 2 header)
tcpdump(包括第二层标头)
PACKETPF_PACKETSOCK_DGRAM768SOL_PACKETno level 2 header 没有第二层标头
SCTP4PF_INETSOCK_SEQPACKETIPPROTO_SCTP SOL_SCTP

Table: parameter values:
表:参数值:

nameLinuxFreeBSDNetBSDOpenBSDSolarisAIXCygwinMac OS XHP-UX
PF_LOCAL111111111
PF_INET222222222
PF_APPLETALK51616161616161616
PF_INET6102824242624-3022
PF_PACKET17--------
SOCK_STREAM111121111
SOCK_DGRAM222212222
SOCK_RAW333343333
SOCK_SEQPACKET555565555
SOCK_DCCP(6)--------
SOCK_PACKET10--------
IPPROTO_IP000000000
IPPROTO_TCP666666666
IPPROTO_UDP171717171717171717
IPPROTO_DCCP33--------
IPPROTO_SCTP132132--132132---
IPPROTO_RAW255255255255255----
SOL_SOCKET16553565535655356553565535655356553565535
SOL_IP000000000
SOL_TCP666666666
SOL_UDP17-----17--
SOL_IPV6414141414141-4141
SOL_PACKET263--------
SOL_DCCP269--------

Socket address specifications
套接字地址规范

These hexadecimal data define socket addresses for local and remote sockets, and for bind and range options. The basis is the struct sockaddr_* for the respective address family that should be declared in the C include files. Please keep in mind that their first two bytes (sa_family and - on BSD - sa_len) are implicitely prepended by socat.
这些十六进制数据定义了本地和远程套接字的套接字地址,以及绑定和范围选项。基础是应在 C 包含文件中声明的相应地址族的 struct sockaddr_* 。请记住,它们的前两个字节( sa_family 和 - 在 BSD 上 - sa_len )会被 socat 隐式地添加在前面。

Linux on 32bit Intel:
Linux 在 32 位 Intel 上:

namesocket address type (without leading address family)
套接字地址类型(不包括前导地址族)
binary specification 二进制规范
IPv42 bytes port, 4 bytes IPv4 addr, 8 bytes 0
2 字节端口,4 字节 IPv4 地址,8 字节 0
x0016 x7f000001 x0000000000000000
IPv62 bytes port, 4 bytes flowinfo, 16 bytes IPv6 addr, 4 bytes scope-id
2 字节端口,4 字节流信息,16 字节 IPv6 地址,4 字节范围 ID
x0016 x00000000 x0102030405060708090a0b0c0d0e0f x00000000
UNIXvariable length path name, 0 terminated
可变长度路径名,以 0 结尾
x2f746d702f736f636b00
PACKET2 bytes protocol (0x0003), interface index as int in host byte order, 8 bytes 0
2 字节协议(0x0003),接口索引作为主机字节顺序中的整数,8 字节 0
x0003 x02000000 x0000000000000000

For AppleTalk see above example.
对于 AppleTalk,请参见上面的示例。

Solaris on 32bit Intel:
32 位英特尔上的 Solaris

namesocket address type (without leading address family)
套接字地址类型(不包括前导地址族)
binary specification 二进制规范
IPv62 bytes port, 4 bytes flowinfo, 16 bytes IPv6 addr, 4 bytes scope-id, 4 bytes src-id
2 字节端口,4 字节流信息,16 字节 IPv6 地址,4 字节范围 ID,4 字节源 ID
x0016 x00000000 x0102030405060708090a0b0c0d0e0f x00000000 x00000000

Forever - play on...
永远 - 继续玩下去...

Eager to experiment with exotic socket types? Run nmap's protocol scan and see what is available on your system:
渴望尝试异国情调的套接字类型?运行 nmap 的协议扫描,看看您的系统上有什么可用的:

nmap -sO localhost

Copyright: Gerhard Rieger 2008
版权所有:Gerhard Rieger 2008

License: GNU Free Documentation License (FDL)
许可证:GNU 自由文档许可证(FDL)