pymumu / smartdns

A local DNS server to obtain the fastest website IP for the best Internet experience, support DoT, DoH. 一个本地DNS服务器,获取最快的网站IP,获得最佳上网体验,支持DoH,DoT。
https://pymumu.github.io/smartdns/
GNU General Public License v3.0
7.93k stars 1.05k forks source link

Random crash when using MAC addresses as client identifiers #1695

Closed Someone-Practice closed 3 months ago

Someone-Practice commented 3 months ago

Issue

When using MAC addresses as client identifiers in client-rules, smartdns will crash occasionally.

Environment

To replicate

  1. Add a client rule with the MAC address of a device as its identifier
  2. Connect that device
  3. Do random dns requests
  4. Disconnect that device
  5. Return to step 2, smartdns will crash eventually

Core dump and executable

I stopped seeing crash traces in log after I turn on core dumping, but I do recall the log entry referred to a function called find_neighbors or something similar.

Core dump

Executable used

pymumu commented 3 months ago

https://github.com/pymumu/smartdns/blob/04d68e7797d32c02b76b51aa7556cc9e73b7892d/src/util.c#L911

Modify as follows

int rtalen = len;
Someone-Practice commented 3 months ago

https://github.com/pymumu/smartdns/blob/04d68e7797d32c02b76b51aa7556cc9e73b7892d/src/util.c#L911

Modify as follows

int rtalen = len;

Unfortunately, MAC rules ceased to function after changing that integer to signed. Tested on latest master and Release45.

Executable and makefile

pymumu commented 3 months ago
int netlink_get_neighbors(int family,
                          int (*callback)(const uint8_t *net_addr, int net_addr_len, const uint8_t mac[6], void *arg),
                          void *arg)
{
    if (netlink_neighbor_fd <= 0) {
        netlink_neighbor_fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, NETLINK_ROUTE);
        if (netlink_neighbor_fd < 0) {
            return -1;
        }
    }

    struct nlmsghdr *nlh;
    struct ndmsg *ndm;
    char buf[1024 * 16];
    struct iovec iov = {buf, sizeof(buf)};
    struct sockaddr_nl sa;
    struct msghdr msg;
    int len;
    int ret = 0;

    memset(&msg, 0, sizeof(msg));
    msg.msg_name = &sa;
    msg.msg_namelen = sizeof(sa);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    nlh = (struct nlmsghdr *)buf;
    nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
    nlh->nlmsg_type = RTM_GETNEIGH;
    nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
    nlh->nlmsg_seq = time(NULL);
    nlh->nlmsg_pid = getpid();

    ndm = NLMSG_DATA(nlh);
    ndm->ndm_family = family;

    if (send(netlink_neighbor_fd, buf, NLMSG_SPACE(sizeof(struct ndmsg)), 0) < 0) {
        return -1;
    }

    while ((len = recvmsg(netlink_neighbor_fd, &msg, 0)) > 0) {
        if (ret != 0) {
            continue;
        }

        int nlh_len = len;
        for (nlh = (struct nlmsghdr *)buf; NLMSG_OK(nlh, nlh_len); nlh = NLMSG_NEXT(nlh, nlh_len)) {
            ndm = NLMSG_DATA(nlh);
            struct rtattr *rta = RTM_RTA(ndm);
            const uint8_t *mac = NULL;
            const uint8_t *net_addr = NULL;
            int net_addr_len = 0;
            int rta_len = RTM_PAYLOAD(nlh);

            for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
                if (rta->rta_type == NDA_DST) {
                    if (ndm->ndm_family == AF_INET) {
                        struct in_addr *addr = RTA_DATA(rta);
                        if (IN_MULTICAST(ntohl(addr->s_addr))) {
                            continue;
                        }

                        if (ntohl(addr->s_addr) == 0) {
                            continue;
                        }

                        net_addr = (uint8_t *)&addr->s_addr;
                        net_addr_len = IPV4_ADDR_LEN;
                    } else if (ndm->ndm_family == AF_INET6) {
                        struct in6_addr *addr = RTA_DATA(rta);
                        if (IN6_IS_ADDR_MC_NODELOCAL(addr)) {
                            continue;
                        }
                        if (IN6_IS_ADDR_MC_LINKLOCAL(addr)) {
                            continue;
                        }
                        if (IN6_IS_ADDR_MC_SITELOCAL(addr)) {
                            continue;
                        }

                        if (IN6_IS_ADDR_UNSPECIFIED(addr)) {
                            continue;
                        }

                        net_addr = addr->s6_addr;
                        net_addr_len = IPV6_ADDR_LEN;
                    }
                } else if (rta->rta_type == NDA_LLADDR) {
                    mac = RTA_DATA(rta);
                }
            }

            if (net_addr != NULL && mac != NULL) {
                ret = callback(net_addr, net_addr_len, mac, arg);
                if (ret != 0) {
                    break;
                }
            }
        }
    }

    return ret;
}
Someone-Practice commented 3 months ago
int netlink_get_neighbors(int family,
                        int (*callback)(const uint8_t *net_addr, int net_addr_len, const uint8_t mac[6], void *arg),
                        void *arg)
/* TRUNCATED */

MAC rules are functional now. Ran it for a day and I haven't observed a crash yet. The test will continue til tomorrow.

Someone-Practice commented 3 months ago

Still no crashes. I think it's safe to assume the issue has been resolved now.