/* * nss-dontstalkme: Return localhost for tracking host IPs * * Copyright (C) 2014 Guido Günther * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Author: Guido Günther * * Heavily inspired by nss-myhostname.c which is * Copyright 2008-2011 Lennart Poettering */ #include #include #include #include #include #include #include #include /* We use 127.0.2.1 as returned address */ #define LOCALADDRESS_IPV4 (htonl(0x7F000201)) #define LOCALADDRESS_IPV6 &in6addr_loopback #define LOOPBACK_INTERFACE "lo" #define ALIGN(a) (((a+sizeof(void*)-1)/sizeof(void*))*sizeof(void*)) #define _public_ __attribute__ ((visibility("default"))) #define ARRAY_CARDINALITY(Array) (sizeof(Array) / sizeof(*(Array))) /* The hosts we blacklist To blacklist all hosts from domain foo.bar use .foo.bar */ const char* stalkers[] = { /* https://support.google.com/analytics/answer/1009688?hl=en-GB */ ".google-analytics.com", ".doubleclick.net", "partner.googleadservices.com", "p.twitter.com", }; enum nss_status _nss_dontstalkme_gethostbyname4_r(const char *name, struct gaih_addrtuple **pat, char *buffer, size_t buflen, int *errnop, int *h_errnop, int32_t *ttlp) _public_; enum nss_status _nss_dontstalkme_gethostbyname3_r(const char *name, int af, struct hostent *host, char *buffer, size_t buflen, int *errnop, int *h_errnop, int32_t *ttlp, char **canonp) _public_; enum nss_status _nss_dontstalkme_gethostbyname2_r(const char *name, int af, struct hostent *host, char *buffer, size_t buflen, int *errnop, int *h_errnop) _public_; enum nss_status _nss_dontstalkme_gethostbyname_r(const char *name, struct hostent *host, char *buffer, size_t buflen, int *errnop, int *h_errnop) _public_; /* * check if host matches the given pattern. * * If pattern starts with a dot all names ending in pattern will * match. Otherwise name has to match pattern exactly. */ static int match_pattern (const char *name, const char *pattern) { int name_len = strlen(name); int pattern_len = strlen(pattern); if (pattern_len && pattern[0] == '.') { if (name_len < pattern_len) return 0; return strcmp (name + name_len - pattern_len, pattern) == 0; } else { if (!strcasecmp(name, pattern)) return 1; } return 0; } static int is_stalker (const char* name) { unsigned int i = 0; for (i = 0; i < ARRAY_CARDINALITY(stalkers); i++) { if (match_pattern(name, stalkers[i])) return 1; } return 0; } enum nss_status _nss_dontstalkme_gethostbyname4_r(const char *name, struct gaih_addrtuple **pat, char *buffer, size_t buflen, int *errnop, int *h_errnop, int32_t *ttlp) { unsigned lo_ifi; size_t l, idx, ms; char *r_name; struct gaih_addrtuple *r_tuple, *r_tuple_prev = NULL; lo_ifi = if_nametoindex(LOOPBACK_INTERFACE); if (! is_stalker(name)) { *errnop = ENOENT; *h_errnop = HOST_NOT_FOUND; return NSS_STATUS_NOTFOUND; } l = strlen(name); ms = ALIGN(l+1)+ALIGN(sizeof(struct gaih_addrtuple))*2; if (buflen < ms) { *errnop = ENOMEM; *h_errnop = NO_RECOVERY; return NSS_STATUS_TRYAGAIN; } /* First, fill in hostname */ r_name = buffer; l = strlen(name); memcpy(r_name, name, l+1); idx = ALIGN(l+1); /* Second, fill in IPv6 tuple */ r_tuple = (struct gaih_addrtuple*) (buffer + idx); r_tuple->next = r_tuple_prev; r_tuple->name = r_name; r_tuple->family = AF_INET6; memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16); r_tuple->scopeid = (uint32_t) lo_ifi; idx += ALIGN(sizeof(struct gaih_addrtuple)); r_tuple_prev = r_tuple; /* Third, fill in IPv4 tuple */ r_tuple = (struct gaih_addrtuple*) (buffer + idx); r_tuple->next = r_tuple_prev; r_tuple->name = r_name; r_tuple->family = AF_INET; *(uint32_t*) r_tuple->addr = LOCALADDRESS_IPV4; r_tuple->scopeid = (uint32_t) lo_ifi; idx += ALIGN(sizeof(struct gaih_addrtuple)); r_tuple_prev = r_tuple; /* Verify the size matches */ assert(idx == ms); *pat = r_tuple_prev; if (ttlp) *ttlp = 0; return NSS_STATUS_SUCCESS; } static inline size_t proto_address_size(int proto) { assert(proto == AF_INET || proto == AF_INET6); return proto == AF_INET6 ? 16 : 4; } static enum nss_status fill_in_hostent(const char *hn, int af, struct hostent *result, char *buffer, size_t buflen, int *errnop, int *h_errnop, int32_t *ttlp, char **canonp) { size_t l, idx, ms; char *r_addr, *r_name, *r_aliases, *r_addr_list; size_t alen; alen = proto_address_size(af); l = strlen(hn); ms = ALIGN(l+1)+ sizeof(char*)+ ALIGN(alen)+ 2*sizeof(char*); if (buflen < ms) { *errnop = ENOMEM; *h_errnop = NO_RECOVERY; return NSS_STATUS_TRYAGAIN; } /* First, fill in hostname */ r_name = buffer; memcpy(r_name, hn, l+1); idx = ALIGN(l+1); /* Second, create (empty) aliases array */ r_aliases = buffer + idx; *(char**) r_aliases = NULL; idx += sizeof(char*); /* Third, add addresses */ r_addr = buffer + idx; if (af == AF_INET) *(uint32_t*) r_addr = LOCALADDRESS_IPV4; else memcpy(r_addr, LOCALADDRESS_IPV6, 16); idx += ALIGN(alen); /* Fourth, add address pointer array */ r_addr_list = buffer + idx; ((char**) r_addr_list)[0] = r_addr; ((char**) r_addr_list)[1] = NULL; idx += 2*sizeof(char*); /* Verify the size matches */ assert(idx == ms); result->h_name = r_name; result->h_aliases = (char**) r_aliases; result->h_addrtype = af; result->h_length = alen; result->h_addr_list = (char**) r_addr_list; if (ttlp) *ttlp = 0; if (canonp) *canonp = r_name; return NSS_STATUS_SUCCESS; } enum nss_status _nss_dontstalkme_gethostbyname3_r(const char *name, int af, struct hostent *host, char *buffer, size_t buflen, int *errnop, int *h_errnop, int32_t *ttlp, char **canonp) { if (af == AF_UNSPEC) af = AF_INET; if (af != AF_INET && af != AF_INET6) { *errnop = EAFNOSUPPORT; *h_errnop = NO_DATA; return NSS_STATUS_UNAVAIL; } if (! is_stalker(name)) { *errnop = ENOENT; *h_errnop = HOST_NOT_FOUND; return NSS_STATUS_NOTFOUND; } return fill_in_hostent(name, af, host, buffer, buflen, errnop, h_errnop, ttlp, canonp); } enum nss_status _nss_dontstalkme_gethostbyname2_r(const char *name, int af, struct hostent *host, char *buffer, size_t buflen, int *errnop, int *h_errnop) { return _nss_dontstalkme_gethostbyname3_r(name, af, host, buffer, buflen, errnop, h_errnop, NULL, NULL); } enum nss_status _nss_dontstalkme_gethostbyname_r(const char *name, struct hostent *host, char *buffer, size_t buflen, int *errnop, int *h_errnop) { return _nss_dontstalkme_gethostbyname3_r(name, AF_UNSPEC, host, buffer, buflen, errnop, h_errnop, NULL, NULL); }