What's new

pixelserv pixelserv - A Better One-pixel Webserver for Adblock

  • SNBForums Code of Conduct

    SNBForums is a community for everyone, no matter what their level of experience.

    Please be tolerant and patient of others, especially newcomers. We are all here to share and learn!

    The rules are simple: Be patient, be nice, be helpful or be gone!

kvic

Part of the Furniture
pixelserv-tls

pixelserv-tls is a tiny bespoke HTTP/1.1 webserver with HTTPS and SNI support. It acts on behalf of hundreds of thousands of advert/tracker servers and responds to all requests with nothing to speed up web browsing.
Why you may want to run it
If you want to develop for pixelserv-tls, look at these interesting projects:
Hosts files for Dnsmasq or Unbound

pixelserv-tls is part of a complete DNS-based adblock strategy. You'll need a script to prepare hosts files for Dnsmasq or Unbound to redirect ad requests to <your pixelserv ip>.

AB-Solution is a user-friendly terminal app. That can prepare host files for Dnsmasq (and does much more). It integrates seamlessly with pixelserv-tls.

ublockr
is a minimalistic script that can also prepare hosts files for Dnsmasq. Works with pixelserv-tls out of box.

If you want to understand DNS-based adblock and want a minimal and custom script to prepare hosts files, perhaps ublocker is a good starter.

Selected HOWTO from Wiki
Install libopenssl v1.0.2n-1c or downgrade from a newer version
 
Last edited:
NB pixelserv only a null target, the dns poisoning scripts are more complicated - watch out with autoupdating ones, users tend to notice if you crash lan dns/dhcp services!

I use this 13kB one on my mips N66 with Merlin web interface set to non standard https port. Tomato httpd was patched to only listen on its LAN IP address, which frees up ports 80,443 for other things such as pixelserv, Asuswrt and dd-wrt both has same issue of web gui listening on the lan interface (all IPs).

pixelserv compiled to run on router WRT54G

The c pixelserv is mainly library calls using same functions as many other router tools dnsmasq, miniupnpd etc, so is small when dynamically linked with compatible router toolchain, and compressed with upx.

Current stats, using manually small updated blocklist from pgl.yoyo

Code:
/mnt/usb4gb/pixelserv version: V35.HZ11FIX2 compiled: Nov 29 2014 13:32:49 options: 192.168.66.254 -p 80 -p 81 -p 8080 -p 8081 -p 443 -o 2
1077841 uts, 52311 req, 1316 avg, 47900 rmx, 2169 tav, 4031 tmx, 0 err, 7235 tmo, 454 cls, 0 nou, 0 pth, 2023 nfe, 1865 ufe, 139 gif, 5 bad, 5889 txt, 0 jpg, 8 png, 9 swf, 3 ico, 33461 ssl, 1 sta, 0 stt, 0 204, 1096 rdr, 123 pst, 0 hed

A lot of ads moving to https so not many null pixels anymore, these just get closed quickly by this version of pixelserv (ssl requests), you do get error messages from mobile if not using adblockplus.

That's

Code:
 12 days, 11 hours, 26 minutes, 48 seconds
since last rebooted

using

pixelserv compiled to run on router WRT54G
 
Last edited:
You’re right. Asuswrt/Asuswrt-Merlin WebUI binds to all NICs on port 80 for http. A better choice will be to only bind to br0 for http, and store http port in a nvram variable. It’s a little change but provide power users with tremendous flexibilty.

Asus, you hear power users’ call? Or need Merlin to kick your butt? :)

To workaround this limitation, I used to run a watchdog script that run WebUI on a http port of my choice, monitor the process and keep it running. The solution worked well. But not of my liking because such workaround shouldn’t be needed in the first place. Second Asuswrt has its own watchdog process that kicks in and restart httpd in the event of its unexpected suicide. So on rare occasions both watchdogs race to resurrect the dead process. That’s a bit ugly. Though my watchdog script prevails every single time.

At the moment, I’m experimenting with port forward. Pixelserv runs on port 8080. WebUI runs on port 80 - untouched. So far it looks promising.

I had setup weekly update for all host lists. Automatically bounce dnsmasq after update. It worked well. I can’t recall any human intervention in the past year. I didn’t spent time looking at the actual content of those host lists until recently. I can’t help myself sweating. I guess the lucky choice I made a year ago was to use host rather than domain level blocking e.g.

Code:
// Examples of host level blocking
10.8.10.8 amer.hops.glbdns.microsoft.com
10.8.10.8 ba.afl.rakuten.co.jp

// Examples of domain level blocking
address=/10.im.cz/10.8.10.8
address=/100cpc.com/10.8.10.8

So blocked hosts should be limited rather than all hosts below that sub-domains.

That’s both good and bad. The bad thing is redundant entries in dnsmasq and worse that redundant data does not improve effectivenss of adblock. For example, certain domains you’re sure that anything of that domain could be simply blocked. Any hosts below that domain hence need not require individual entries in dnsmasq. End result is reduced redundant data and increased effectivenss (though memory footprint of dnsmasq not necessarily reduced).

People might be lured to use domain-level blocking for all host lists they pulled from the net. I believe they have been seeing lots of inconvenience. We simply can’t do that without data massage.
 
I use domain blocking but the minimal list specially created for dnsmasq,

Blocking ads using dnsmasq with an additional hosts file

which still works well in UK at least, and is very efficient. I agree attempts to parse host links to common domain level can lead to a lot of work with whitelisting.

Re port 80 we've discussed this before here and elsewhere

Any way to stop httpd from binding to 0.0.0.0:80

It's got implications for core functionality of Asuswrt, need to consider changing lan IP address and remote access config etc so will not be addressed in merlin builds. Just disable http and use https on non standard port to free up 80,443.

It was a one line code change to Tomato, if you want to try yourself, maybe a fork would be interested?

Thoughts on httpd
 
Last edited:
To Support User Configurable HTTP Port

I think it's also trivial to support that for WebUI in Asuswrt. I looked at it before and dug out the first change from my memory lane. In src/router/rc/services.c:
Code:
void
start_httpd(void)
{
    // commented out by kvic
    // char *httpd_argv[] = {"httpd", NULL};
    char *httpd_argv[] = {"httpd", “-p”, nvram_safe_get("http_lanport”), NULL}; // kvic
    pid_t pid;
#ifdef RTCONFIG_HTTPS
    char *https_argv[] = {"httpd", "-s", "-p", nvram_safe_get("https_lanport"), NULL};
    pid_t pid_https;
#endif

I name the new nvram variable, http_lanport modelled after the existing https_lanport (which defaults to 8443). The second change is to declare the new nvram and initialize to 80 just like https_lanport.

Asus engineers would have better done it in the first place. Maybe for some commercial reason they didn't. If people want this feature, it’s time to harass Asus here.

Use iptables to Redirect Ad Requests

I started experimenting the following two NAT rules that redirect ad requests on 80 & 443 to 8080 where my pixelserv sits. WebUI runs as factory default on port 80.
Code:
iptables -t nat -A PREROUTING --dest 10.8.10.8 -p tcp --dport 80 -j DNAT --to-dest 192.168.1.1:8080
iptables -t nat -A PREROUTING --dest 10.8.10.8 -p tcp --dport 443 -j DNAT --to-dest 192.168.1.1:8080

I find this works well so far. If people would like to try on your setup, remember to put these two the first rules of your PREROUTING filter of the nat table in firewall-start. And exercise discretion according to your own setup. It's possible the rules in certain scenario will enter an infinite loop. Results in long wait for server timeout. I bumped into that and pulled myself out..
 
Last edited:
You’re right. They probably inherited the same GPL code from Linksys WRT54G. Bind to all interfaces is fine as long as users have a way to change http port. Hence, my suggestion in the previous post to add http_lanport. A trivial change that's huge by increasing the platform's flexibility IMHO.

Some thoughts on improving hosts file for adblock

The other night I was analysing my combined hosts files. Below are some stats:
Code:
$ /jffs/bin/analyseHostsFile.sh /tmp/hosts.tmp.sorted
2 lvl:    7445
3 lvl:    16498
4 lvl:    4622
5 lvl:    277
6 lvl:    19
7 lvl:    11
8 lvl:    0
9 lvl:    1
10 lvl:   0
11 lvl:   0
12 lvl:   0
About a quarter of my 29K hosts are secondary-level domain (“2 lvl” e.g. doubleclick.com). That’s interesting because they’re potential candidates for domain blocking. Also if a domain is blocked, any hosts under that domain need not be added to dnsmasq. For example, www2.doubleclick.com” and www3.doubleclick.com can be removed. The potential to harvest and eliminate such redundant data in “3 lvl”, “4 lvl” and etc is attractive. And such elimination can be repeated for tertiary-level domains in “3 lvl” for “4 lvl”, “5 lvl” and etc. From my stats above such repeated eliminations provide diminishing return. But a few years down the road, it's worry-free along the way.

Adblock Plus is a credible source of adservers. They shall be included. I looked at easy lists before. Now I'm thinking to devise a better way to extract adservers and include them in the block list.

I believe the hosts maintainers such as adblock plus, Winhelp2002.mvps.org and plg.yoyo.org are doing a terrific job. However, I don’t know if they check and classify a host vs domain and if dead hosts are removed. I understand the latter is a dilemma. A dead host could resurrect itself in a week or month..who knows. But as a user of hosts files I have more flexibility. Assume we update hosts files weekly. It doesn’t hurt to do some checking.

I’m playing around with automating some of the above.
 
I want to open my Dnsmasq to selected devices from internet. A kind of homebrew OpenDNS like service for my friend circle.

Instead of wide open access, preferably with some level of control.

I know VPN is one option but not preferred. Source IP address based filtering is another option. But assume the select devices with dynamic IP once in the wild. This approach is not effective and too much human maintenance from time to time.

What other possibilities are available?
 
Super-charged Dnsmasq

Automated the approach outlined in #7. Expanded the number of host lists (e.g. including malicious sites) and resulted in a total number of hosts before process to 40K. After process, trims down to 29K (more than a quarter less..not bad). Over 99% of these are blocked at domain level. With the remaining less than 1% blocked at host level. No special rationale. Just keep the facility for whatever need in future.
Code:
Number of hosts/domains at each level:
    15641 /tmp/domain.tmp.2lvl
    11592 /tmp/domain.tmp.3lvl
     1678 /tmp/domain.tmp.4lvl
      120 /tmp/domain.tmp.5lvl
       41 /tmp/domain.tmp.6lvl
       10 /tmp/domain.tmp.7lvl
        1 /tmp/domain.tmp.8lvl
        4 /tmp/domain.tmp.9lvl
        8 /tmp/domain.tmp.tmp
    29095 total
The number of secondary-level domains grow more than double. The explanation behind it is possibly because most malicious sites are provided at domain level rather than hosts. The extra benefit of this increased base is higher level domain/hosts are more likely to be eliminated as redundant. This is proven by the decreased number of domains/hosts in 3rd-level and onwards.

My Dnsmasq cache size is set to 10k. The idea behind is assume 20 individuals at worst scenario using my network. With each allocated a cache size of 500 and they don't have any lookup in common. Another change made to Dnsmasq is increase min-cache-ttl to 300s after reading this fellow's discovery here.

With all above Dnsmasq consumes about 5MiB ram. Average response time for a non-cached lookup is less than 90ms. Cached lookup is less than 10ms. I love this grown-up kid!

Retired Privoxy

I didn't plan to until one day I found my PC browser's Adblock Plus was not enabled. I must have forgotten. But I surfed for a whole week without realising it! If PC can live without adblock plugin. Mobile devices shall be even less an issue without it. Turned off Privoxy and proved right. It's only three month old in my setup. Short lived but sorry..

A super-charged Dnsmasq+a tiny pixelserv (only occupy 200Ki ram!) is my favourite adblock facility at the moment. I think I won't need another overhaul until IPv6 becomes common place.
 
Revive the thread...it's a continuation about adding https to pixelserv touched upon in a few posts here.

Pixelserv with HTTPS

Based on Pixelserv V35.HZ12, I added OpenSSL to properly handle https requests. They will be deciphered and responded properly. And respective stats updated accordingly.

I have built binaries for mips & arm. They run on Asuswrt-Merlin. You'll need to generate your own certificates. Save in a file and provide to pixelserv. http/https ports can be optionally specified from command line.

Tested with Safari on OS X and iOS. Both work well without a glitch. A quick test on Chrome for OS X is not so. Haven't tested on Windows and Linux.

Anyone has interest?

EDIT:

Four binaries can be downloaded here.

How to generate a certificate for use in Pixelserv?

Follow the steps here to generate a new certificate. You get a few files. Depends on how you name it..there will be e.g. two file named "server.crt" and "server.key" (which is private key). Concatenate them into a single file, say, pixelserv.pem by

$ cat server.crt server.key > /jffs/etc/pixelserv.pem

How to launch Pixelserv with a different certificate?

By default Pixelserv will read the certificate located at /jffs/etc/pixelserv.pem. You can override this with command line option "-z" e.g.

$ pixelserv -z /opt/etc/mycertishere.pem

How to enable different http/https ports?

By default Pixelserv will listen on 80 & 443 for http and https respectively. You can override this with command line option "-p" (for http port) and "-k" (for https port) e.g.

$ pixelserv -p 80 -k 443 -p 8080 -k 5443 -k 9443
 
Last edited:
Wow, very interested - you double the size of the code?
Here you are:)

I found the dynamically linked binary increased not much. The static one increases by a factor of >10 due to linking in OpenSSL. I include static binaries because runtime RAM usage is less (about half of the dynamic).

Does this mean certificate errors on clients unless trusted?

You're right. Browsers will relentlessly warn you with a "broken lock" in red on the address bar. Here is one way to overcome in OS X for self-signed cert (i.e. you generate the certs yourself).

If you happen to have purchased a certificate from a Trusted Root/Intermediate Certificate authorities, you can use with pixelserv-tls. Then you won't need any workaround.
 
I'm a novice in the area of PKI. This is a little project to help myself (and hopefully others) to understand better. Looking further into the issue of Google Chrome that I saw, it reveals lots of interesting stuff.

The red padlock and the warning message I'm seeing in Chrome:
2me7tb8.png

Upon my research, even a certificate is trusted by the system, an application will go through a few other sanity checks. One of them is comparing fully qualified domain name of the server that the application requests against that it gets from server's certificate (i.e. that of the server responds to its request).

E.g. A server (pixelserv) uses a certificate issued to "AsusRocks.com". A client (Chrome) requests "doubleclick.net". Through dnsmasq trick, the request goes to pixelserv and security handshake goes through well. When Chrome does the last piece of checking and finds that it connects to "doubleclick.net" but the server's certificate is issued to "AsusRocks.com". The two domain names mismatch. It stops and warns the users.

For self signed certs that users have explicitly granted trust in OS, seems to me it's up to applications how strict it wants to enforce. Safari/MobileSafari will give a green padlock without a warning in this case. Chrome is persistent until a user explicitly grants "proceed" on server-by-server basis. Firefox, I heard, is more on the side of Safari (though I haven't confirmed myself).

Now the problem of Chrome's behaviour is troubling pixelserv (with https) being useful to Chrome users.

What could be a reasonably quick solution?
 
I gave this some thought. Can't think of a quick one button action that will work for Chrome. After all PKI is..PKI. We perhaps have to play by its rules for serving dummy ads. So that a solution would be generally usable.

The implication is that for each domain and sub-domain we're blocking, we need a cert. Fortunately enough there is a facility in PKI called wildcard cert. This helps alleviate the workload a bit. So that we don't have to create one cert for, say, ad1.doubleclick.net and a second one for ad2.doubleclick.net. Instead, we create a wildcard cert for *.doubleclick.net. A wildcard cert is just as good as that. If we have ad.g.doubleclick.net. This cert will be broken for the last domain. That's why wildcard certs only alleviate the situation a bit.

In my case, as mentioned in post #5, I have about 30,000 unique ad domains. That haven't included potentially limitless variation of sub-sub-sub..domains. If we adopt a brute force approach, to generate one cert for each of those 30K domain, one cert about 2KiB, we generate 50MiB junk on disk for storing those certs. Frankly no single user or a family will hit 10% of those 30K ad domains (the percentage will be even less for https domains..though growing). Also likely a burden of daily maintenance. And more likely than not we'll bump into an oops moment of a new ad domain which is not included yet. So I think this is not good to begin with.

Next we have a choice only to generate certs for ad domains that we actually use. This gives to two possibilities. We actually can at real-time in Pixelserv generate a cert, sign it, save it and then load up for use. This is an interesting option. As it's saved to disk, subsequent connect for this ad domain will have a cert ready right away. But router's CPU might be slow in generating every new cert. Likely the first few connect attempts will fail even though that doesn't hurt much. The downside for this approach is that we have to re-invent a few wheels in C. Those are already available e.g. as EasyRSA 3.0 or openssl command line tools. Also the real-time processing perhaps will go out of control when multiple users (even though still a small number) bring in connection attempts to new ad domains. Expect Pixelserv to blow up on the router. Of coz there are facilities (which on their own might be interesting experiments) we can keep it under control but with added complexity. Anyway...what's so urgent for blocking ads? Our bottom line is a rejected connection for an ad domain without a cert. This leads me to the idea of some sort of offline/batch processing approach.

This first begins with adding access log to pixelserv. For a long time, I would want to monitor what apps are pulling from and sending to ad servers, especially for those using https! In the case of Microsoft or the like...you know what I meant. I know anything secrets the data is perhaps mangled or further encrypted during transit. But have a URL and some names are better than nothing. I think this would be fun. Even though most likely the logs are archived and checked when mishaps come. This feature can be "opt-in" with command line switch as some people might not like.

An easy by product is to re-use access log for recording ad domains that do not have a certificate. With a bit thoughtful structuring of the logging format. We give ourselves an easier life later to extract needed info for automation of cert generation offline..

For the logging of missing a cert possible, we will be making use of Server Name Indication. Apparently we the pixelserv are not the first guys who overload one web server for serving multiple domains..just that we'll be serving tonnes of such ad domains..*grin*

The above implies that we won't be presenting one cert to Pixelserv but a directory of certs (the dir starts empty, grows up and hopefully top off gradually). This also implies that each user/installation will have to generate a Root CA. This CA cert will be used to sign all certs generated for ad domains. For individual browsers, each OS has only to install the Root CA once. Then all certs for those ad domains will be trusted without further action by users (and green grin padlocks). This approach also allows the Root CA and its private key be stored on a separate machine other than the router.. Extract domain from log, generate certs, signing etc. The whole batch could potentially be automated with scripts (let's leave it for another day).

The description might be long. Hopefully codes are much shorter. This is my playbook at the moment. Thoughts?
 
@kvic: excellent discussion. It seems that you have deep understanding and an eye for sharpening this tool. Have you developed a script that can reproduce your success on RT-AC68U? I'm not sure if the RT-AC56U is for the purposes of this exercise the same as an RT-AC68U: they use the same ARM binaries so this leads me to believe that a script should work for either..
 
@kvic: excellent discussion. It seems that you have deep understanding and an eye for sharpening this tool. Have you developed a script that can reproduce your success on RT-AC68U? I'm not sure if the RT-AC56U is for the purposes of this exercise the same as an RT-AC68U: they use the same ARM binaries so this leads me to believe that a script should work for either..

You're right. Same ARM binary for AC56/68U. So you can download the pixelserv binary from post #1 under "Installation of Pixelserv" section. Then add the launch script to Entware by following post #1 "init.d Script for Entware" section. This gives you a nice installation for AC68+Entware. Pixelserv will auto start on boot.

If you don't have Entware, then the launch script and location of the pixelserv binary have to be adapted accordingly.

The latest part of this thread is about enhancing Pixelserv to process HTTPS requests. At the moment still under development but won't be far away..
 
@kvic: Sounds promising. If you would like, I could start a wiki entry for you to fill out to document your experience for the community. Let me know if this is something you would like. It sounds like you have done a lot of good work and that the evolution of documenting it from wiki -> full script automation is the path to amplifying your work.

Just as minor tangent these are some of my goals for my RT-AC68U routers and pixelserv is a target.

I have changed out RT-N16s (Optware) in favor RT-AC68U routers (Entware). The procedure I followed to install Entware is https://www.dd-wrt.com/wiki/index.php/Asus_T-Mobile_Cellspot#Entware_Installation

OTRW Optware has excellent apps:

1) pixelserv
2) stophammer
3) asiablock

These apps are nicely described here:
http://www.dd-wrt.com/wiki/index.ph...Service_Explanations.2FConfiguration_Examples

As I understand 2) and 3) are scripts that that use iptables to identify IP addresses to be dropped. Pixelserv is a form of DNS poisoning that redirects targets to a single-pixel webserver. One hopes that stophammer and asiablock are scripts that can be modded for the RT-AC68U and do not contain compiled code.
 
There was a later OTRW2 as well? The original pixelserv was a perl script called from xinetd, and there was a tiny C version of pixelserv which just served the 1x1 gif without the overhead of perl. The C pixelserv discussed in this thread is a standalone daemon more like a micro webserver with more intelligence about what it serves up. The dns poisoning diverting websites towards the pixelserv IP is generally referred to as adblock scripts, looks like OTRW bundled it into the pixelserv script category, but adblocking doesn't need pixelserv.
 
I tried to get pixelserv running as specified in the 1st post but it doesnt seem to run, I presume it should auto run on reboot?

Also
admin@RT-AC68U:/tmp/home/root# /opt/etc/init.d/S81pixelserv start
Starting pixelserv... failed.

What could be the issue?
 

Similar threads

Latest threads

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!
Top