有没有办法使用套接字API设置TCP选项?

时间:2009-07-16 17:33:02

标签: tcp sockets

我对套接字编程比较新,所以这听起来像是一个非常蹩脚的问题。 我必须使用经过身份验证的TCP(MD5作为TCP选项开始)作为某些应用程序的传输。我想知道是否可以使用套接字API完成此操作,或者我可以使用其他形式的现有TCP API来执行相同操作。如果我能得到一些帮助,我将不胜感激。

4 个答案:

答案 0 :(得分:2)

以下是Linux的用法:http://criticalindirection.com/2015/05/12/tcp_md5sig/

sockopt struct:

struct tcp_md5sig {
    struct  __kernel_sockaddr_storage tcpm_addr;
    __u16   __tcpm_pad1;
    __u16   tcpm_keylen;
    __u32   __tcpm_pad2;
    __u8    tcpm_key[TCP_MD5SIG_MAXKEYLEN];
};

即使内核和libc中存在该功能,Linux手册页也没有引用。

从链接:

  

套接字选项TCP_MD5SIG保存预共享MD5密钥的映射   针对相应的对等端点。必须绑定   客户端到服务器已知的特定IP和端口。该   必须在服务器的侦听套接字上调用setsockopt()   客户端的连接套接字,在调用connect()之前   客户端。

它还有完整的客户端 - 服务器示例。以下是一个片段:

//Sockets Layer Call: bind()
memset((char *) &cl_addr, 0, sizeof(cl_addr));
cl_addr.sin6_family = AF_INET6;
if(inet_pton(AF_INET6, c6ip, &cl_addr.sin6_addr) <= 0)
    error("ERROR on inet_pton");
cl_addr.sin6_port = htons(atoi(cport));

if (bind(sockfd, (struct sockaddr *) &cl_addr, sizeof(cl_addr)) < 0)
    error("ERROR on binding");


memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin6_family = AF_INET6;
if(inet_pton(AF_INET6, s6ip, &serv_addr.sin6_addr) <= 0)
    error("ERROR on inet_pton");
serv_addr.sin6_port = htons(atoi(sport));


memcpy(&md5.tcpm_addr, &serv_addr, sizeof(serv_addr));
strcpy(md5.tcpm_key, key);
md5.tcpm_keylen = strlen(key);

if ((r = setsockopt(sockfd, IPPROTO_TCP, TCP_MD5SIG, &md5, sizeof(md5))) < 0)
    error("listen setsockopt TCP_MD5SIG");

答案 1 :(得分:1)

我对你的问题感到有点困惑。您当然可以使用setsockopt函数在套接字上设置套接字选项,但是通过其他问题的声音,这不是您的意思。我从来没有听说过任何名为 Authenticated TCP 的传输协议,google也没有任何用处。这是标准吗?有没有RFC?

如果您只是想要一个安全的,经过身份验证的TCP传输层,那么您应该查看安全套接字层,或者简称 SSL ,或者替换它,传输层安全性,或{{ 3}}简而言之。对于你正在使用的任何语言(你还没有指定),几乎肯定会有一个实现。

另外,MD5对于身份验证是什么意思? MD5是一种散列算法,但它的抗冲突性不足以用于需要安全签名的通信。

编辑啊哈!你在谈论TCP选项,我现在明白了。我还没有看到任何套接字API中内置的特定TCP选项的任何实现,所以你可能在这里运气不好。这取决于您使用的实现,但鉴于这是一个相当模糊的TCP选项,旨在增强边界网关协议,而不是您通常在路由软件之外使用的东西,这可能是特别罕见的。如果它受支持,你可以设置如下:

BOOL optVal = TRUE;
int optLen = sizeof(BOOL);

if (setsockopt(
      socket,
      IPPROTO_TCP,
      TCP_WHATEVER,
      optVal,
      optLen) != SOCKET_ERROR) {
    printf("Success\n");
}

答案 2 :(得分:0)

如果您正在寻找RFC 2385描述的TCP-MD5选项,一些系统(如FreeBSD)支持布尔TCP_MD5SIG选项来启用它。它在套接字上启用如下:

int opt = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_MD5SIG, &opt, sizeof(opt));

有关详细信息,请参阅tcp(4)

Linux有一个使用struct参数的不同版本;这里的其他答案有一些额外的细节。

答案 3 :(得分:0)

我刚刚在Ruby中实现了一个TCP-MD5连接类,并且认为这段代码片段可能会为其他任何人节省成本(至少在Linux上进行设置的人都没有记录)。这只是通过查看标题和实验;希望Ruby对于用其他语言写作的人来说并不太可怕。

class TCPMD5Socket < Socket
  IPPROTO_TCP =  6 # linux/in.h
  TCP_MD5SIG  = 14 # linux/tcp.h
  TCP_MD5SIG_MAXKEYLEN = 80 # linux/tcp.h

  # Works exactly the same as TCPSocket.open except you can supply a password
  # to pass to the kernel for MD5 authhentication.
  #
  def initialize(host, port, password)
    raise ArgumentError.new("Password is too long") if 
      password.length > TCP_MD5SIG_MAXKEYLEN

    family = Socket.const_get(IPAddr.new(host).ipv4? ? "AF_INET" : "AF_INET6")
    super(family, Socket::SOCK_STREAM, 0)      
    # struct tcp_md5sig {
    #   struct __kernel_sockaddr_storage tcpm_addr; /* address associated */
    #   __u16   __tcpm_pad1;                /* zero */
    #   __u16   tcpm_keylen;                /* key length */
    #   __u32   __tcpm_pad2;                /* zero */
    #   __u8    tcpm_key[TCP_MD5SIG_MAXKEYLEN];     /* key (binary) */
    # };
    tcp_md5sig_buffer = [
      Socket.pack_sockaddr_in(port, host), 0, password.length, 0, password
    ].pack("a128SSLa#{TCP_MD5SIG_MAXKEYLEN}")    
    setsockopt(IPPROTO_TCP, TCP_MD5SIG, tcp_md5sig_buffer)
    connect(Socket.pack_sockaddr_in(port, host))
  end
end

除了Linux 2.6.20之外,这不会起作用,但至少如果你使用的是FreeBSD,它会在man page中描述。