我正在写我自己的DNS黑洞来阻止我的家庭networking上的广告和恶意软件。 我意识到这种types的程序已经存在了,但是我想学习这个过程并写下我自己的程序。 从Linux文档看来,可以告诉sendmsg()使用不同的返回地址,以便可以转发UDP数据包,并且接收服务器将响应发送给原始请求者而不是我的服务器。 从稀疏文档,我设置我的套接字绑定到端口53(DNS)。 我收到DNS请求,解释他们,并在网站被列入黑名单时作出回应。 对于“好”的域名,我得到了2个不同的结果。 在MacOS上,我的转发请求被发送,但响应返回到我的程序,而不是原来的请求者。 在Linux(Armbian 4.13内核)上,我从sendmsg()调用中获取EINVAL,并且不发送任何内容。 任何人都可以阐明我做错了什么? (为简洁起见,所有错误检查已被删除)
附加信息…在这种scheme中的一个皱纹是原始的DNS请求被绑定到53以外的套接字端口(显然)。 如何告诉sendmsg()将响应返回到原始请求的正确端口号?
我的“代理”版本的这个作品。 我存储了原始请求的事务ID和请求端口号,并能够成功返回响应
最新消息 – 我能够成功地完成我想要的,但不能使用sendmsg()。 通过使用RAW套接字和“欺骗”IP报头中的返回地址,我能够将数据包发送回不同的返回地址和端口号(使用sendto)。 nslookup认为这是一个问题,但浏览器可以。 我想我们可以称之为'closures'
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个潜在的问题。
首先我会修复你的meassage结构,使用这样的代码回答 。 我怀疑你应该仔细看看in_pktinfo
的偏移量和你传递给cmsg_len
的长度,因为这些会导致EINVAL错误。 一个可能的策略可能是将代码切回到正常发送数据包的位置,以查看哪些数据结构被拒绝。
如果不这样做,我想你可以考虑重写你的发送逻辑来使用原始套接字来绕过你的内核可能正在做的任何IP验证。 例如, 这段代码看起来应该大致是你所需要的。
如果还不够,我怀疑你需要在内核安全系统中寻找路由。