如何通过netcat将二进制数据回显给侦听C程序?

时间:2017-03-05 18:19:08

标签: c sockets

我正在尝试使用UDP编写的程序设置一个简单的通信方案。程序将以非阻塞方式设置UDP套接字,然后使用select()从套接字读取非阻塞。以下是该计划的内容:

#include <stdio.h>   /* Standard input/output definitions */
#include <stdlib.h>  /* UNIX standard function definitions */
#include <unistd.h> 
#include <sys/types.h> 
#include <sys/time.h> 
#include <sys/un.h> 
#include <sys/socket.h> 
#include <fcntl.h>   /* File control definitions */
#include <signal.h>
#include <time.h>
#include <stdint.h>  /* Standard integer types */
#include <string.h>  /* String function definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>

int setupUdpSocketN(int *fd, uint16_t port_no)
{
struct sockaddr_in my_addr;

    // Create UDP socket:
    if ( (*fd = socket(AF_INET,SOCK_DGRAM,0)) < 0 ) 
        return -1; 
    fcntl(*fd, F_SETFL, O_NONBLOCK);

    // Bind socket:
    memset((char *)&my_addr, 0, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    my_addr.sin_port = htons(port_no);

    if ( bind(*fd, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1 ) {
        close(*fd);
        return -2;
    }

return EXIT_SUCCESS;
}

int main(void) {
    int fd, maxfd, nready;
    size_t recvlen;
    uint8_t buf[256];
    fd_set rset;
    setupUdpSocketN(&fd, 60000);
    maxfd = fd + 1;

    while(1) {
        FD_ZERO(&rset);
        FD_SET(fd, &rset);

        if( (nready = select(maxfd, &rset, NULL, NULL, NULL)) < 0 ) {
            printf("Error in select: %s\n", strerror(errno));
        }
        if( FD_ISSET(fd, &rset) ) {
            recvlen = recv(fd, buf, 256, 0);
            //recvlen = recvfrom(fd, buf, 256, 0, NULL, NULL);
            for(int i = 0; i < recvlen; i++) {
                printf("%02x ", buf[i]);
            }
            printf("\n");
        }
    }
    return 0;
}

然后我尝试使用echo,xxd和netcat发送此程序二进制数据:

echo -ne "\xfe\x64\x32\x1a\xb0" | xxd -rp | nc -u localhost 60000

然而,这个程序在运行时只是无休止地阻塞。我对套接字编程很新,不确定如何继续。

1 个答案:

答案 0 :(得分:1)

我认为主要问题不在于代码,而在于如何调用netcat。当我运行服务器并使用nc -u localhost 60000时,我可以重现您的问题:服务器无限期地阻塞而没有接收数据。但是,当我强制netcat使用IPv4时,我可以将数据成功发送到服务器:

nc -4 -u localhost 60000

或者,将代码更改为使用IPv6并使用不带-4标志的netcat也可以:

struct sockaddr_in6 my_addr;
...
if ( (*fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0 ) 
  return -1;
...
my_addr.sin6_family = AF_INET6;
my_addr.sin6_addr = in6addr_any;
my_addr.sin6_port = htons(port_no);

向您展示如何调试问题:我首先使用netcat启动UDP服务器,然后使用第二个netcat实例将数据发送到该服务器。这按预期工作。然后,我使用strace检查了netcat服务器使用的系统调用。 strace输出显示使用IPv6的netcat:

...
socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
bind(3, {sa_family=AF_INET6, sin6_port=htons(60000), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = 0
...

除此之外,还有两个其他问题:

  • recv函数返回接收到的字节数,如果发生错误,则返回-1。因此,返回值的类型为ssize_t,而不是size_t。由于您的代码声明了size_t recvlen,因此当recv返回-1时,将发生整数下溢,并且recv将是类型size_t的最大可能值(例如{{ 1}}。这样会导致缓冲区溢出,因为ULLONG_MAX = 18446744073709551615小得多。

  • 您的buf / echo调用仅对我产生零。我不确定这是否打算。 xxd的{​​{1}}标志启用“反向”模式,其中-r将尝试解析十六进制转储(例如,由xxd产生而没有xxd标志)然后再现原始字节序列,该序列是原始十六进制转储的输入。但是在您的情况下,xxd输出原始二进制数据,而不是编码的十六进制转储,因此-r无法以反向模式解析此数据。