gpt4 book ai didi

linux - UDP 数据包转发从 sendmsg 获取 EINVAL

转载 作者:太空狗 更新时间:2023-10-29 11:49:20 25 4
gpt4 key购买 nike

我正在编写自己的 DNS 黑洞来阻止我家庭网络上的广告和恶意软件。我意识到这种类型的程序已经存在,但我想学习这个过程并编写自己的程序。从 Linux 文档来看,似乎可以告诉 sendmsg() 使用不同的返回地址,以便可以转发 UDP 数据包,接收服务器会将响应发送给原始请求者而不是我的服务器。从稀疏文档中,我设置了绑定(bind)到端口 53 (DNS) 的套接字。我正在接收 DNS 请求,解释它们并在网站被列入黑名单时做出响应。对于“好”域名,我得到了 2 个不同的结果。在 MacOS 上,我的转发请求已发送,但响应返回到我的程序而不是原始请求者。在 Linux(Armbian 4.13 内核)上,我从 sendmsg() 调用中获得 EINVAL,但没有发送任何内容。任何人都可以阐明我做错了什么吗? (为简洁起见,已删除所有错误检查)

其他信息...此方案中的一个问题是原始 DNS 请求绑定(bind)到 53 以外的套接字端口(很明显)。我如何告诉 sendmsg() 将响应返回到原始请求的正确端口号?

我的“代理”版本有效。我存储了原始请求的交易 ID 和请求端口号,并且能够成功返回响应

最新消息 - 我能够成功完成我想要的,但没有使用 sendmsg()。通过使用 RAW 套接字并“欺骗”IP header 中的返回地址,我能够将数据包发送回不同的返回地址和端口号(使用 sendto)。 nslookup 认为这是一个问题,但浏览器可以接受。我想我们可以称此为“结案”

struct sockaddr_in addr, addrfrom, fwaddr;
socklen_t addrLen = sizeof(struct sockaddr_in);
int rc, listen_sock;

listen_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
rc = 1;
setsockopt(listen_sock, IPPROTO_IP, IP_PKTINFO, &rc, sizeof(rc));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(53); // port address of DNS
addr.sin_addr.s_addr = INADDR_ANY;
bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));
memset(&fwaddr, 0, sizeof(fwaddr));
fwaddr.sin_family = AF_INET;
fwaddr.sin_port = htons(53);
fwaddr.sin_addr.s_addr = 0x08080808; // Google DNS server

<... Receive DNS request from client...>

if (bForward)
{
struct msghdr msg;
struct iovec iov[1];
struct {
struct cmsghdr cm; /* this ensures alignment */
struct in_pktinfo ipi;
} cmsg;
memset(&cmsg, 0, sizeof(cmsg));
memset(&msg, 0, sizeof(msg));
iov[0].iov_base = request_bufffer;
iov[0].iov_len = request_len;
msg.msg_flags = 0;
msg.msg_name = &fwaddr; // dest address of packet
msg.msg_namelen = addrLen;
msg.msg_iov = &iov[0];
msg.msg_iovlen = 1;

msg.msg_control = &cmsg;
msg.msg_controllen = sizeof(cmsg); //sizeof(struct in_pktinfo);
cmsg.cm.cmsg_len = sizeof(cmsg);
cmsg.cm.cmsg_level = IPPROTO_IP;
cmsg.cm.cmsg_type = IP_PKTINFO;

cmsg.ipi.ipi_ifindex = 0;
cmsg.ipi.ipi_spec_dst = addrfrom.sin_addr; // original source address
rc= sendmsg(listen_sock, &msg, 0);
} // bForward

最佳答案

问题

我发现这段代码中有 2 个潜在问题。

  1. 您没有使用 CMSG macros分配和操作您的辅助数据(即您的 cmsg 结构)。
  2. 即使这是固定的,您也试图欺骗发送数据包的源 IP 地址 - 即您试图声明它来自不属于此框的地址。我从未尝试这样做,但如果您发现内核安全性阻止您这样做,我也不会感到惊讶。

那怎么办?

首先,我会使用类似 this SO answer 的代码来修复您的消息结构.我怀疑您应该仔细查看 in_pktinfo 的偏移量和您传递给 cmsg_len 的长度,因为这些可能会导致 EINVAL 错误。一种可能的策略可能是将代码缩减到它只是正常发送数据包以查看哪些数据结构被拒绝的程度。

如果这样做不行,我想您可以考虑重写您的发送逻辑以使用原始套接字来绕过您的内核可能正在执行的任何 IP 验证。例如,this code看起来应该大致就是您所需要的。

如果这还不够,我怀疑您需要在内核安全系统中进行路由。

关于linux - UDP 数据包转发从 sendmsg 获取 EINVAL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46984389/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com