Support for partial blocking? block 30% of requests going to a domain

Hello,

Does PiHole support conditional/rated blocking?

I'm using PiHole mainly to block social media applications, I don't want all the requests to be blocked as this annoys my family members. I used to write a python script that just disables PiHole at random times of day but it's still annoying for them (as they don't know what's going on).

Is there some way to block a custom domain list for lets say 30% of the time? so the applications will still work but in a sluggish way rater than completely stopping? so it appears that something is going wrong with the apps and not our internet.

My motivation is to save their time and make more time available for human-human interaction and to have quality time, not everyone staring at their phone in isolation.

If that doesn't qualify as a feature, I'll appreciate pointing me towards where I should make changes in the code, thank you so much.

This is not a feature we have implemented nor one we would actively work on (I think). Even when I surely can follow your motivation, it still seems like a strange thing to do. Also, it isn't at all clear if you can achieve your goal with like this as the client devices (may it be laptops, phones, etc.) will likely pick up the (longish) time-to-live (TTL) of the domain and just use their internal memory for said time.

I just queried twitter.com and the reply came back with a TTL of twenty minutes. Within this interval, your family member's devices will cache the result internally and not ask your Pi-hole. Everything will work at this point and there isn't much we could do to stop this.

Yes, you could overwrite the TTL to be some smaller value (config option max-ttl=...) but this will not really help much, either, as the underlying feature ("block sometimes only") is not available.

The changes will likely be doable, however, there are two issues here:

  1. Your changes will likely cause incompatibilities with future updates, you will have to fix possible merge conflicts (not too much work)
  2. There is no category you could use for this. In order to keep this doable, you'd have to break one of the existing categories (like blacklist regex) to always behave in this wobbly mode.

If you still want to do this, here is a proposal how it would be possible:

  1. Compile FTL from source (see here)
  2. Add max-ttl=5 (or whatever value you deem appropriate (seconds)) in /etc/dnsmasq.d/99-wobbly.conf
  3. Change the code here:

to something like

			{
				if (rand() % 100 > 70)
				{
					// Really block it
					query_blocked(query, domain, client, QUERY_REGEX);
					return true;
				}
				else
				{
					// Permit it this time
					return false;
				}
			}

Again, I'm not sure if you can get what you want by this, however, it is an idea.

2 Likes

Thank you for your detailed reply, TTL never came across my mind when I was thinking about it, but that's interesting. In a sense that I'll have to balance between the max-ttl and the blocking rate so blocking remains effective.

I'll give it a try. thanks again, I really appreciate your help.

May I suggest using powerdns recursor as pihole's upstream resolver? It's a recursive resolver similar to unbound but it includes a lua scripting engine that allows you to manipulate the dns requests and responses. To download and install, go to https://repo.powerdns.com/ and follow the instructions for your distribution. This way, you don't have to modify any code and risk breaking future updates.

Here's a sample script that may do what you're looking for (I have not tested it).


host = "(twitter%.com)%.?$"

math.randomseed(os.time())

function preresolve(dq)
  modified = false
  if string.match(dq.qname:toString(), host) and math.random(1, 10) <= 3 then
    dq.variable = true  -- disable packet cache in any case
    dq.rcode = pdns.NXDOMAIN 
    modified = true
  end
  return modified
end

function postresolve(dq)
  modified = false
  if string.match(dq.qname:toString(), host) then
    local records = dq:getRecords()
    for k,v in pairs(records) do
        v.ttl = 5
        modified = true
    end
    if modified then
      dq:setRecords(records)
    end
  end
  return modified
end