#include #include #include #include #include #include #include #include /* for droppriv */ #include #include int n_pending_requests = 0; struct event_base *base = NULL; struct user_data { char *name; /* the name we're resolving */ int idx; /* its position on the command line */ }; void callback(int errcode, struct evutil_addrinfo *addr, void *ptr) { struct user_data *data = ptr; const char *name = data->name; if (errcode) { printf("%d. %s -> %s\n", data->idx, name, evutil_gai_strerror(errcode)); } else { struct evutil_addrinfo *ai; printf("%d. %s", data->idx, name); if (addr->ai_canonname) printf(" [%s]", addr->ai_canonname); puts(""); for (ai = addr; ai; ai = ai->ai_next) { char buf[128]; const char *s = NULL; if (ai->ai_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr; s = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, 128); } else if (ai->ai_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr; s = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 128); } if (s) printf(" -> %s\n", s); } evutil_freeaddrinfo(addr); } free(data->name); free(data); if (--n_pending_requests == 0) event_base_loopexit(base, NULL); } #define fatal(msg) { printf(msg "\n"); exit(1); } void droppriv() { struct passwd *pw; /* bad example, never use user 'nobody' in real code, create your own */ pw = getpwnam("nobody"); if (!pw) fatal("unknown user"); if (chroot(pw->pw_dir) != 0) fatal("unable to chroot"); if (chdir("/") != 0) fatal("unable to chdir"); if (setgroups(1, &pw->pw_gid) == -1) fatal("setgroups() failed"); if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) fatal("setresgid failed"); if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) fatal("setresuid() failed"); endpwent(); } /* Take a list of domain names from the command line and resolve them in * parallel. */ int main(int argc, char **argv) { int i; struct evdns_base *dnsbase; if (argc == 1) { puts("No addresses given."); return 0; } if (geteuid() != 0) errx(1, "need root privileges"); droppriv(); printf("droppriv + chroot DONE\n"); base = event_base_new(); if (!base) return 1; dnsbase = evdns_base_new(base, 1); if (!dnsbase) return 2; for (i = 1; i < argc; ++i) { struct evutil_addrinfo hints; struct evdns_getaddrinfo_request *req; struct user_data *user_data; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_flags = EVUTIL_AI_CANONNAME; /* Unless we specify a socktype, we'll get at least two entries for * each address: one for TCP and one for UDP. That's not what we * want. */ hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; if (!(user_data = malloc(sizeof(user_data)))) { perror("malloc"); exit(1); } if (!(user_data->name = strdup(argv[i]))) { perror("strdup"); exit(1); } user_data->idx = i; ++n_pending_requests; req = evdns_getaddrinfo( dnsbase, argv[i], NULL /* no service name given */, &hints, callback, user_data); if (req == NULL) { printf(" [request for %s returned immediately]\n", argv[i]); /* No need to free user_data or decrement n_pending_requests; that * happened in the callback. */ } } if (n_pending_requests) event_base_dispatch(base); evdns_base_free(dnsbase, 0); event_base_free(base); return 0; }