What's new

Fast HTTP pixel image server for ad blocking

  • 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!

ASAT

Senior Member
A simple HTTP server returns a static 1x1 pixels image. Similar to pixelserv. Written in less than 100 lines of C code. Compiles with the ARM toolchain included with Asuswrt-Merlin firmware. No Optware or Entware required.

nweb23.c
Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define VERSION 23
#define BUFSIZE 256

char imagedata[] = {0x47,0x49,0x46,0x38,0x39,0x61,0x01,0x00,0x01,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x21,0xF9,0x04,0x01,0x00,0x00,0x01,0x00,0x2C,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0x02,0x01,0x4C,0x00,0x3B};

/* this is a child web server process, so we can exit on errors */
void web(int fd, int hit)
{
  long len=sizeof(imagedata);
  static char buffer[BUFSIZE+1]; /* static so zero filled */

  (void)sprintf(buffer,"HTTP/1.1 200 OK\nServer: nweb/%d.0\nContent-Length: %ld\nConnection: close\nContent-Type: image/gif\n\n", VERSION, len); /* Header + a blank line */
  (void)write(fd,buffer,strlen(buffer));

  /* send image data */
  (void)write(fd,imagedata,len);

  sleep(1);    /* allow socket to drain before signalling the socket is closed */
  close(fd);
  exit(1);
}

int main(int argc, char **argv)
{
  int i, port, pid, listenfd, socketfd, hit;
  socklen_t length;
  static struct sockaddr_in cli_addr; /* static = initialised to zeros */
  static struct sockaddr_in serv_addr; /* static = initialised to zeros */

  if( argc < 2  || argc > 2 || !strcmp(argv[1], "-?") ) {
    (void)printf("hint: nweb Port-Number\t\tversion 23a\n\n"
    "\tnweb is a small and very safe mini web server\n"
    "\tThere is no fancy features = safe and secure.\n\n"
    "\tExample: nweb 8181 &\n\n"  );

    (void)printf("\n\tNot Supported: URLs including \"..\", Java, Javascript, CGI\n"
    "\tNo warranty given or implied\n\tNigel Griffiths nag@uk.ibm.com\n"  );
    exit(0);
  }
  /* Become deamon + unstopable and no zombies children (= no wait()) */
  if(fork() != 0)
    return 0; /* parent returns OK to shell */
  (void)signal(SIGCLD, SIG_IGN); /* ignore child death */
  (void)signal(SIGHUP, SIG_IGN); /* ignore terminal hangups */
  for(i=0;i<32;i++)
    (void)close(i);        /* close open files */
  (void)setpgrp();        /* break away from process group */
  /* setup the network socket */
  if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0) {
  //    logger(ERROR, "system call","socket",0);
  }
  port = atoi(argv[1]);
  if(port < 0 || port >60000) {
  //    logger(ERROR,"Invalid port number (try 1->60000)",argv[1],0);
  }
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  serv_addr.sin_port = htons(port);
  if(bind(listenfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr)) <0) {
  //    logger(ERROR,"system call","bind",0);
  }
  if( listen(listenfd,64) <0) {
  //    logger(ERROR,"system call","listen",0);
  }
  for(hit=1; ;hit++) {
    length = sizeof(cli_addr);
    if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0) {
    //    logger(ERROR,"system call","accept",0);
    }
    if((pid = fork()) < 0) {
    //    logger(ERROR,"system call","fork",0);
    }
    else {
      if(pid == 0) {     /* child */
    (void)close(listenfd);
    web(socketfd,hit); /* never returns */
      } else {     /* parent */
    (void)close(socketfd);
      }
    }
  }
}


How to cross-compile this C code for the RT-AC68U, RT-AC56U and other ARMv7 Asuswrt-Merlin routers? You must have a working Linux build environment for compiling the Asuswrt-Merlin firmware because you need the ARM toolchain. To compile this program, type:
Code:
arm-brcm-linux-uclibcgnueabi-gcc -static -ffunction-sections -fdata-sections -O3 -pipe -march=armv7-a -mtune=cortex-a9 -fno-caller-saves -mfloat-abi=soft -Wall -fPIC -D_REENTRANT nweb23.c -o nweb

Then, reduce the size of binary file. After this, the binary file size will be ~71K.
Code:
arm-brcm-linux-uclibcgnueabi-strip nweb

Copy the Nweb binary to the router at /jffs/bin.

To start it, type:
Code:
/jffs/bin/nweb 81 &
# OR
# Start nweb if it is not already running
/bin/pidof nweb > /dev/null 2>&1 || (/jffs/bin/nweb 81 &)

Create a virtual IP address for Nweb. I also run a TOR transparent proxy for some devices on my network. That's why the additional NAT prerouting. And the filter, REJECT tcp-reset, apparently forces browsers to give up sooner on port 443 requests, such as https://1.0.0.1
Code:
iptables -t nat -I PREROUTING -d 1.0.0.1 -j RETURN
iptables -t nat -I PREROUTING -d 1.0.0.1 -p tcp --dport 80 -j REDIRECT --to-ports 81
iptables -I FORWARD -d 1.0.0.1 -j REJECT
iptables -I FORWARD -d 1.0.0.1 -p tcp -j REJECT --reject-with tcp-reset

Try it out. In a web browser, type:
http://1.0.0.1
OR
http://1.0.0.1/sddsf/sdfsd/fsdf/s/dfsd/fsd/fsd/fsd/fsdf/123456.html

You immediately get served with a (GIF Image, 1 x 1 pixels).

Will it work with /etc/hosts file for super fast ad blocking and page rendering?

The original C code was gotten here and modified.
nweb: a tiny, safe Web server (static pages only)
http://www.ibm.com/developerworks/systems/library/es-nweb/index.html
 
Last edited:
good stuff. I use lighttpd for this. Wonder if there would be a perf difference with a lighter alternative like this.

btw, you may be better off not using 443 as you'll get browser errors because of mixed content. You may want to just want to reject ssl packets and only serve 80.
Code:
iptables -I INPUT 1 -i br0 -p tcp --dport 443 -j REJECT
 
You should check PC to router comms using wireshark, if you close the read socket without reading all the contents the kernel sends an aggressive tcp reset. I guess this is what the "chop;chop" does in the original perl

http://proxytunnel.sourceforge.net/files/pixelserv.pl.txt

Then you will find if you send a gif reply to a script request dumb browsers such as IE will try to execute the script and report script errors. Then you want to be able to listen on multiple ports and report statistic ... BTDTGTTS

http://www.linksysinfo.org/index.php?threads/pixelserv-compiled-to-run-on-router-wrt54g.30509/

Now developed and shared on GitHub

https://github.com/HunterZ/pixelserv

I use a recent small version from LinksysInfo with Merlin and mips N66, but that version also compiles on ARM (and any Linux x86 host).

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
132670 uts, 5443 req, 895 avg, 20274 rmx, 1023 tav, 4262 tmx, 0 err, 381 tmo, 41 cls, 0 nou, 0 pth, 241 nfe, 28 ufe, 11 gif, 1 bad, 690 txt, 0 jpg, 6 png, 1 swf, 0 ico, 3800 ssl, 1 sta, 0 stt, 0 204, 191 rdr, 51 pst, 0 hed
 
I use a recent small version from LinksysInfo with Merlin and mips N66, but that version also compiles on ARM (and any Linux x86 host).
Is this better than just running lighttpd with mod_rewrite to just serve empty pages to all 80 requests?
 
you may be better off not using 443 as you'll get browser errors because of mixed content
I updated the iptables rules. My router is also a TOR transparent proxy for some devices on my network, that's why the additional iptables rules.
 
Last edited:
My test was https://1.0.0.1 Note that this is a port 443 request. Both Firefox and IE took over 90 seconds before giving up. By adding the filter REJECT tcp-reset, it is done in 3 seconds or less. Maybe there is an explanation for it?
 
Is this better than just running lighttpd with mod_rewrite to just serve empty pages to all 80 requests?
Presumably a tiny <10kB compiled binary is more efficient, but the original intent was to make something usable on older routers without usb, there was even a version that could be stored in nvram. I don't think simple gif very effective these days - few ad requests are for a simple image, many moving to https, not always on standard ports, so you need to be more selective about responses.
 
Hi there,

programming for an arm
with an arm-brcm-linux-gnueabi-gcc.exe compiler
I can't get the bind( ) function to work properly
It keeps returning -1
With a similar code as yours.
Any guess? ...
Thanx in advance
 
Oh, yes, this is my code:

SOCKET createConnectSocket(int port){

struct sockaddr_in sad;
sad.sin_family = AF_INET;
sad.sin_addr.s_addr = 0;
addr.sin_port = htons(port);

SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (bind(sock, (struct sockaddr *)&sad, sizeof(sad)) != 0)
return 0;

if (listen(sock, 4) != 0)
return 0;

return sock;

}
 
with an arm-brcm-linux-gnueabi-gcc.exe compiler
I can't get the bind( ) function to work properly
It keeps returning -1
With a similar code as yours.
Any guess?
I used the ARM toolchain of the Asuswrt-Merlin firmware. Now I can see her. :cool:
 

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