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: 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.

The Pixelserv guys before me did lot of hard work in the past few years. I was amazed by its small RAM usage and felt silly using lighttpd for the same purpose. Hence, I pulled pieces together from the net and started this thread.

By all means go ahead and start a wiki for the community. I have a wiki in mind (though not sure if I actually will do it) when all pieces are ready:
  1. ad domain based blocking/redirection through dnsmasq
    - already done as of post #9 which I can share later
  2. an updated pixelserv that processes HTTPS requests and log access
    - being worked on
  3. auto generation of certs for new HTTPS ad domains
    - have skeleton scripts which work on my PC
  4. a How-To on better logging; replace firmware's syslog with syslog-ng+logrotate from Entware
    - already working on my router but I'm procrastinating on a how-to..
  5. firmware can be configured to yield port 80 and not bind to all interfaces
    - John has it in his fork...
4&5 are not pre-requisites but good to have..

One thing I'm trying to justify is the on-going need of pixelserv's existence. I think with HTTPS capability and access logging it's here to stay. For example, one use of access log could be to easily spot what domains to whitelist for a broken website. Also e.g. serving tonnes of https domains isn't something can be setup with lighttpd.

So if you have the passion to start a wiki, by all means go ahead. Thanks.
 
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?

That's right. With S81pixelserv, rc.unslung from Entware will automatically start pixelserv on reboot provided that the script itself is error free.

I think the likely cause is the script cannot find the pixelserv binary or it cannot bind to your specified port. Try this from command prompt to see the error messages:
  • <path to pixelserv binary>/pixelserv <your router's internal IP> -p 8080
 
Thanks, that showed me it was permissions so I chmod +xx and now its running fine.

Now I am off to work out how to get lonley cders advanced adblock to work with this until you share #9

edit, got it working but youtube hangs before every video with this method for some reason whereas with the 0.0.0.0 method it does not, any way around this?
 
Last edited:
Here is the new version of Pixelserv /w HTTPS. ARM & MIPS binaries for Asuswrt-Merlin are available on github kvic-z/pixelserv-tls. Direct download link to V35.HZ12.Kc.

The first two tuples in the version string carries from this fork's parent. The "K" in the third tuple means nothing. The small letter starting with "a" will increment by one alphabet with each release. Current version is "c" (next release will be "d" and so on). Hopefully we won't hit "z" to get this project done.

Which binary to run on your Asus?

Naming scheme of the binaries is inherited from the fork's parent. From my test, go for "performance.static" by all means. It only consumes 280KiB RAM at runtime (compared to 790KiB RAM for its dynamic sibling).

Both static and dynamic binaries have OpenSSL 1.0.2d from Asuswrt-Merlin 378.55. The dynamic binaries have much smaller size. So good for limited disk space.

What are the new command line switches?

$ ./pixelserv.arm.performance.static --help
Usage:./pixelserv.arm.performance.static
ip_addr/hostname (all if omitted)
-2 (disable HTTP 204 reply to generate_204 URLs)
-f (stay in foreground - don't daemonize)
-k https_port (443 if omitted)
-l (log access to syslog)
-n i/f (all interfaces if omitted)
-o select_timeout (10 seconds)
-p http_port (80 if omitted)
-r (deprecated - ignored)
-R (disable redirect to encoded path in tracker links)
-s /relative_stats_html_URL (/servstats if omitted)
-t /relative_stats_txt_URL (/servstats.txt if omitted)
-u user ("nobody" if omitted)
-z path_to_https_certs (/opt/var/cache/pixelserv if omitted)


-k, -l, -z are newly added options. -k specifies one https port and use multiple times for more ports.

"-l" will log detailed access info to syslog. If you don't specify in the command line, then no access logging. This is an opt-in feature. Access logging can generate lots of data. E.g. running for a day may generate 2MiB of data. Hence, either use it only when trouble shooting browsing issue or have a more capable syslog installed (e.g. syslog-ng + logrotate from Entware)

"-z" specifies the path to certs storage. Each ad domain and its sub-domain will require one wildcard cert. That's also how Pixelserv will look up certs from the given directory.

For example, ad.doubleclick.net, ad2.doubleclick.net will load the same wildcard cert named, _.doubleclick.net which need to exist in the directory. Now if there are two sub-domains to ad.doubleclick.net, say, ap.ad.doubleclick.net and eu.ad.doubleclick.net both will require a new wildcard cert named, _.ad.doubleclick.net. That's unfortunately due to how wildcard cert can work at its best.

If a cert is missing and https request made to an ad domain, then a line indicating the ad domain and the missing cert name will be logged to syslog. This is logged regardless of "-l" switch is supplied or not. Such https will fail with security handshake error. If your browers do not have Adblock plugin, then the advert frame will show such error. Or else you won't see.

How to generate certs for missing ad domains?

Go with the tutorial of EasyRSA 3.0 here (let's leave EasyRSA 2.x behind). More details to be added..
 
got it working but youtube hangs before every video with this method for some reason whereas with the 0.0.0.0 method it does not, any way around this?

Redirecting ad requests to pixselserv shall not hang youtube. My guess is that your change to scripts that prepare dnsmasq files might go wrong in some way. So that the files prepared with your router's IP address may contain more domain entries than before.

Actually now it's a good use of the new pixelserv. If you run it with "-f -l" command line option. It will display info to console. This way can help you to spot what additional domains blocked which renders youtube experience being hung..
 
Hi,

I found this thread while looking for a more robust implementation of the perl script version of pixelserv.

I currently use a Raspberry Pi (B+) running Raspbian with Bind as my local resolver and DNS interception/redirection for ad blocking. These redirections point to the aforementioned pixelserv.pl 'server' which is running on a secondary IP on the Raspberry Pi ethernet interface.

This all works well enough, but the perl script often dies, doesn't support HTTPS, and a number of other limitations.

I've tried compiling the pixelserv source on the RPi, but as I'm not a coder haven't made much progress. I'm sure I could make it work eventually, but figured I'd ask if anyone with more experience has attempted to build a binary for the RPi.
 
I've compiled and run an old C pixelserv natively on a RPi before, I even had a RPi gif to use as a demo, but that was when I used a simple script to compile. The current version in this thread uses a Makefile, which is clearly intended for cross compiling on a 64-bit host. I built the latest one on a 32 bit Ubuntu VM yesterday, but the Makefile needed tweaking to add the library links to the x86 target (I know RPi not x86, but all this does is force 32 bit I think):-

Code:
x86: printver dist
    @echo "=== Building x86 ==="
    $(CC32) $(CFLAGS_D) $(LDFLAGS_D) $(OPTS) $(SRCS) -o dist/$(DISTNAME).$@.debug.dynamic $(SHAREDLIB)
    $(CC32) $(CFLAGS_P) $(LDFLAGS_P) $(OPTS) $(SRCS) -o dist/$(DISTNAME).$@.performance.dynamic $(SHAREDLIB)
    $(CC32) $(CFLAGS_D) -static $(LDFLAGS_D) $(OPTS) $(SRCS) -o dist/$(DISTNAME).$@.debug.static $(SHAREDLIB)
    $(CC32) $(CFLAGS_P) -static $(LDFLAGS_P) $(OPTS) $(SRCS) -o dist/$(DISTNAME).$@.performance.static $(SHAREDLIB)
    $(STRIP) dist/$(DISTNAME).$@.performance.*
    $(UPX) dist/$(DISTNAME).$@.performance.*
    rm -f dist/$(DISTNAME).$(PVERSION).$@.zip
    $(PCMD) dist/$(DISTNAME).$(PVERSION).$@.zip $(PFILES)

Then "make x86" should also work on the Pi. You need "build-essential openssl libssl-dev" packages at least and my Ubuntu 14.04 has a multiver bug which seems to put an h file in the wrong place, so as a workaround I made a copy in the place other h files were expecting it:-

Code:
sudo cp /usr/include/i386-linux-gnu/openssl/opensslconf.h /usr/include/openssl/

You can see the actual commands that get used here

Code:
VirtualBox:/media/sf_VMShare/pixelserv-tls-35.HZ12.Kc$ make x86
=== Building pixelserv version V35.HZ12.Kc ===
=== Building x86 ===
gcc -m32 -I./openssl/include -g -Wall -DDEBUG -DTEST -L./openssl/ -DDROP_ROOT -DIF_MODE util.c socket_handler.c pixelserv.c -o dist/pixelserv.x86.debug.dynamic -lssl -lcrypto -ldl
gcc -m32 -I./openssl/include -O3 -s -Wall -ffunction-sections -fdata-sections -fno-strict-aliasing -L./openssl/ -Wl,--gc-sections -DDROP_ROOT -DIF_MODE util.c socket_handler.c pixelserv.c -o dist/pixelserv.x86.performance.dynamic -lssl -lcrypto -ldl
...

The first couple of dynamic linked versions did compile without error, the static ones did compile with lots of warnings, make failed since I don't have upx compression installed

Code:
VirtualBox:/media/sf_VMShare/pixelserv-tls-35.HZ12.Kc$ ls -laF dist
total 4477
-rwxrwx--- 1 root vboxsf   79072 Oct  3 21:57 pixelserv.x86.debug.dynamic*
-rwxrwx--- 1 root vboxsf 2413058 Oct  3 21:57 pixelserv.x86.debug.static*
-rwxrwx--- 1 root vboxsf   26116 Oct  3 21:57 pixelserv.x86.performance.dynamic*
-rwxrwx--- 1 root vboxsf 2056408 Oct  3 21:57 pixelserv.x86.performance.static*

the debug version passed a first run test

Code:
VirtualBox:/media/sf_VMShare/pixelserv-tls-35.HZ12.Kc$ ./dist/pixelserv.x86.debug.dynamic -p 8080
pixelserv[2142]: ./dist/pixelserv.x86.debug.dynamic version: V35.HZ12.Kc compiled: Oct  3 2015 21:57:07 options: -p 8080
pixelserv[2142]: Listening on :*:8080

then browsing to
http://localhost:8080/anything.gif

gets a null gif

http://localhost:8080/servstats

gets the status message

Code:
./dist/pixelserv.x86.debug.dynamic version: V35.HZ12.Kc compiled: Oct 3 2015 21:57:07 options: -p 8080
92 uts, 4 req, 289 avg, 299 rmx, 14 tav, 29 tmx, 0 err, 0 tmo, 0 cls, 0 nou, 0 pth, 0 nfe, 0 ufe, 1 gif, 0 bad, 0 txt, 0 jpg, 0 png, 0 swf, 1 ico, 0 ssl, 2 sta, 0 stt, 0 204, 0 rdr, 0 pst, 0 hed

The non-debug version didn't immediately work (over stripped I think), nor did https connections, but a lot to learn in setting up the latter!
 
Last edited:
I think it's best to explain what the compile shows when I attempt to build.

I've modified the ARMPREFIX to suit the RPi. When I enable just the 'performance.static' option in the Makefile, I get the following:

Code:
make arm
=== Building pixelserv version V35.HZ12 ===
=== Building ARM ===
arm-linux-gnueabihf-gcc  -O3 -s -Wall -ffunction-sections -fdata-sections -fno-strict-aliasing -static  -Wl,--gc-sections -DDROP_ROOT -DIF_MODE util.c socket_handler.c pixelserv.c -o dist/pixelserv.arm.performance.static
/tmp/ccyhU32g.o: In function `main':
pixelserv.c:(.text.startup.main+0x660): warning: Using 'getpwnam' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
pixelserv.c:(.text.startup.main+0x324): warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/tmp/cc3C36BS.o: In function `get_time':
util.c:(.text.get_time+0x34): undefined reference to `clock_gettime'
/tmp/cc3C36BS.o: In function `get_version':
util.c:(.text.get_version+0x158): undefined reference to `clock_gettime'
/tmp/cc3C36BS.o: In function `get_stats':
util.c:(.text.get_stats+0x5c): undefined reference to `clock_gettime'
collect2: ld returned 1 exit status
Makefile:111: recipe for target 'arm' failed
make: *** [arm] Error 1

If I build with just the 'performance.dynamic' I get this:

Code:
make arm
=== Building pixelserv version V35.HZ12 ===
=== Building ARM ===
arm-linux-gnueabihf-gcc  -O3 -s -Wall -ffunction-sections -fdata-sections -fno-strict-aliasing  -Wl,--gc-sections -DDROP_ROOT -DIF_MODE util.c socket_handler.c pixelserv.c -o dist/pixelserv.arm.performance.dynamic
/tmp/cctR76rl.o: In function `get_time':
util.c:(.text.get_time+0x34): undefined reference to `clock_gettime'
/tmp/cctR76rl.o: In function `get_version':
util.c:(.text.get_version+0x158): undefined reference to `clock_gettime'
/tmp/cctR76rl.o: In function `get_stats':
util.c:(.text.get_stats+0x5c): undefined reference to `clock_gettime'
collect2: ld returned 1 exit status
Makefile:111: recipe for target 'arm' failed
make: *** [arm] Error 1

So, both options complain about undefined references to 'clock_gettime', and the static additionally warns about glibc.

Is this what others see? Are there libraries I'm missing which 'clock_gettime' requires?
 
seems you need "-lrt" for clock_gettime

http://www.raspberry-projects.com/pi/programming-in-c/timing/clock_gettime-for-acurate-timing

Note routers usually use a cut-down uclibc, I'm sure the RPi uses full libc from Debian, so some differences to be expected. Pixelserv uses a lot of old standard library calls so they should work in either..

Have you included the openssl stuff? Guess not and you are using HunterZ's HZ12, that's also been built for Android devices? Are you cross-compiling or compiling natively on the RPi?
 
Last edited:
OK, adding '-lrt' works, I now have a functional dynamic binary. still get errors building the static version. Might be worth splitting the RPi build into a separate section/architecture in the Makefile.

I haven't started configuring the ssl stuff yet, will try soon.

This is compiling natively on the RPi.
 
I wouldn't worry about static version, dynamic linking to native libraries should be fine - static may make the binary work on different devices with different built in libraries, but the system calls are all old/standard used by many other apps...

I was intrigued re native compile, because of the named compiler - I just used 'gcc' !
 
arm-linux-gnueabihf-gcc is just a symlink to gcc-4.6, as is gcc.

Code:
$ ls -l /usr/bin/arm-linux-gnueabihf-gcc
lrwxrwxrwx 1 root root 7 Jan  1  1970 /usr/bin/arm-linux-gnueabihf-gcc -> gcc-4.6

$ ls -l /usr/bin/gcc
lrwxrwxrwx 1 root root 7 Jan  1  1970 /usr/bin/gcc -> gcc-4.6

It appears the reason strip kills the binary is because of '-R .gnu.version_r', it works fine if you exclude it.
 
When you guys once have Makefile ready for 32-bit x86 and Raspberry Pi, perhaps I will include and push to the repository in next round. Good to see more platforms will benefit!

I primarily focus on arm & mips for asuswrt/merlin. Use amd64* for quick tests. The '-R .gnu.version_r' rings a bell on me wrt amd64. I just had a closer look..I think the option itself is not the culprit. The root cause is a dynamic binary not fully linked and strip introduces further destruction.

We can use "readelf -d <a.out>" to see if all required shared libraries are linked in. e.g
Code:
~/pixelserv-tls/dist$ readelf -d pixelserv.amd64.performance.dynamic

Dynamic section at offset 0x6018 contains 27 entries:
  Tag        Type                         Name/Value
0x0000000000000001 (NEEDED)             Shared library: [libssl.so.1.0.0]
0x0000000000000001 (NEEDED)             Shared library: [libcrypto.so.1.0.0]
0x0000000000000001 (NEEDED)             Shared library: [libdl.so.2]
0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
0x000000000000000c (INIT)               0x401618
0x000000000000000d (FINI)               0x4043a4
0x0000000000000019 (INIT_ARRAY)         0x606000

We can verify with "objdump -h <a.out>" and indeed without .gnu.version_r is fine. Perhaps something similar happened to 32bit x86. I never attempted. So not sure. Nice, I think we ironed out another mystery.

*I changed the target name from x86_64 to amd64 for less clunky typing on command line. I'm also among those ppl who think the 64-bit revolution in PC shall be credited to AMD..
 
There is a note in the Makefile that the aggressive STRIP breaks the 64 bit compile too, so it is commented out for that. Its probably my bad for the strip arguments, we used to build a less functional tiny 4k binary that could be compressed/encoded into NVRAM for routers without jffs or usb!
http://dd-wrt.com/phpBB2/viewtopic.php?p=434446
 
Last edited:
4k binary on nvram is like a stunt. The current upx'ed performance.dynamic binary for ARM is about 15k. Still not bad. I prefer not to upx, so commented out in the Makefile. UPX is good for disk space bad for RAM usage at runtime for small binaries.
 
I just pushed my Makefile patch to HunterZ, I'm assuming that's someone reading this thread. This is the first time I've used Github so I could well have done it wrong.
 
I just pushed my Makefile patch to HunterZ, I'm assuming that's someone reading this thread. This is the first time I've used Github so I could well have done it wrong.

I'm not sure if HunterZ is on this forum. mstombs, one of the two earlier authors, is here. :)

@kvic: does the pixelserv source belong in a repository? If yes, what is the most common repository it should be placed in?

I did find this: https://github.com/Entware/entware but not sure that it is the repository for the packages when I type:

ipkg list

I think PixelServ hasn't been an Entware package. So it's expected not to be found on that list.

I might have confused ppl a bit in post #1. I mentioned Entware in the installation simply because I thought ppl intent to run Pixelserv might have Entware installed already. So I suggested one way to reuse Entware's launch scripts for starting Pixelserv.

It's to good to have Entware. We will need a couple of tools from it later.

@kvic: please PM with any feedback for the kickoff of pixelserv wiki:

I look forward to working with you to developing a wiki entry that users can leverage to amplify your success

I think it's about right. Starting with installation perhaps is best. Feel free to document from your own experience. At the moment the process is a bit manual but the essential steps are outlined in post #1. Hope that's sufficient to get you started. If any issue, feel free to ask...
 
How to generate certs for missing ad domains?

Download EasyRSA-3.0.0.0. Unzip to a Linux PC or Asus Router. The benefit of putting on router is total automation (later on) of the steps we're about to go through.

If decided to put on the router, it's better to have Entware installed. We can extract EasyRSA 3 to "/opt/local". EasyRSA 3 requires mktemp (available from Entware). PC Linux most likely comes with mktemp pre-installed.

The next few steps are pretty much a recap of EasyRSA 3 QuickStart. We initialise this copy of EasyRSA with
  • ./easyrsa init-pki
If I remember right, the above steps essentially created the "pki" sub-directory and a few text files such as serial, index..etc. Now we're ready to create the most important cert, Root CA.
  • ./easyrsa build-ca
Enter data as prompt. Mostly I go with defaults except for "Common Name" which I enter "Pixelserv CA" for easy identification. At the end of this step, we get in "pki" sub-dir three important files
  • ca.crt - which is a public cert of our Root CA enterprise...
  • ca.key - under "pki/private" sub-dir. This is the private key to the Root CA
  • ca.key.passphrase - under "pki/private" sub-dir. It's the password we entered earlier to protect the private key.
Now there are tonnes of advice on how to secure the second and last files but let's not too paranoid for our adblock enterprise. ca.crt need to be installed on clients e.g. Android/iOS/OS X/Windows. It's only cert we need to install on clients.

We'll be using this Root CA to sign all other certs that we're going to generate for our ad domains.

If you've been running pixelserv version V35.HZ12.Kc, you find in syslog.log (or your log file) lines similar to the below one:

Oct 7 00:18:14 RT-AC56U pixelserv[13904]: settings.crashlytics.com _.crashlytics.com missing

This tells us that in your Dnsmasq you've blocked the ad domain "settings.crashlytics.com" by redirecting to pixelserv. A little while back, your client browser tried to https to this ad domain. Pixelserv got the request but failed to load the cert (named _.crashlytics.com) since this is new and no cert has been created for it yet.

Now let's generate a wildcard cert for "*.crashlytics.com" which covers well "settings.crashlytics.com". Go back to your EasyRSA directory, then
  • ./easyrsa gen-req _.crashlytics.com
Follow screen prompts, I simply go by default except for "Common Name" which is essential here. We have to type in "*.crashlytics.com". Now you have generate a so called CSR (Certificate Signing Request) and an associated private key to this CSR. CSR need to be signed by Root CA to turn into a cert. Let's do that,
  • ./easyrsa sign-req server _.crashlytics.com
Our ad server (living inside pixelserv) is.... a server. Hence the first argument "server" The second argument is simply the same name we initially give to this cert. Screen will prompt us for a few things. Just go with the follow. Then it'll ask you to type in passphrase which is in "ca.key.passphrase" mentioned above.

Congrats! Our first CSR is signed and now turned into a cert...and it's a wildcard cert!

To prepare the cert for use in Pixelserv, we need to concate the public key and private key of this cert into a single file. Input files:
  • _.crashlytics.com.crt (in "pki/issued")
  • _.crashlytics.com.key (in "pki/private")
Output file:
  • _.crashlytics.com (under anywhere you like...)
Copy the lines between "--BEGIN xxxxx --" and "--END xxxx --" from the two input files and store them in the output file.

We're done. Now copy _.crashlytics.com to /opt/var/cache/pixelserv on the router. Or another directory of your choice (you just then have to let pixelserv know the dir through -z command line option).

For any missing ad domains, we repeat the last three steps: gen-req, sign-req and prepare the final file for pixelserv.
 
Last edited:

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