Watcher – an introduction
This is hardly news: people like to connect stuff to the Internet. The ones I’ve always found the most interesting are IP cameras, which people knowingly or unknowingly have left publicly accessible. For more than 10 years many of these could be found via well-targeted Google queries and nowadays Shodan will do some of the heavy lifting.
However, I’ve always wanted to write a program which would automate this search and present the results in an usable way: present a map of Earth, lay down all the cameras and then be able to click on them and begin streaming.
There were three important considerations when deciding on how this program would be made:
- It must run locally rather than being an online service.
- Needs to run on both Windows and Linux.
- Must not require elevated privileges to run.
This is being written in C++, more specifically the C++17 variant. This lets me write code which can be (mostly) shared between Windows and Linux, without requiring too many platform-specific implementations.
Using SDL provides the basic window creation, OpenGL backed rendering and input handling, without having to deal with the OS implementations.
A support library for SDL, SDL_image is capable of loading image files. Since I wanted to have a map of Earth, having to store everything as BMPs (supported by the base SDL) would mean a pretty large download. SDL_image allows me to load other image formats, such as PNG or JPG, with a minimum amount of extra code.
Given that Watcher needs to scan the internet to find accessible cameras, this information has to go somewhere. To keep the required set up needed in order to be able to run the program, I’ve settled on sqlite3 as a database backend as it just stores the database as a regular file. Getting sqlite3 to perform properly has required some care, as it is easy to rack up write requests when there are large amounts of threads doing work. I’ll go into further detail about this in a future post.
The ever-dependent curl is used to download pages from HTTP servers we find and certain types of camera streams.
Niels Lohmann’s JSON library handles reading and writing of JSON streams, which are used in various configuration files and as result of geolocation queries.
The user interface is handled by Omar Cornut’s imgui library. Although meant for debug tooling, it is straightforward to use and for the purposes of this application, it provides more than enough functionality.
One of the ideas of Watcher is that it retrieve data from several sources, as well as be able to stream video feeds when an open camera is detected. It uses a plugin system with each plugin (in the form of a DLL or SO) providing additional functionality without having to recompile the main program.
The normal flow is currently like this:
- A scan is requested. Watcher keeps track of which address blocks haven’t been scanned yet and picks one at random.
- Either zmap or my own basic scanner handle the actual probing, looking for HTTP servers at selected ports (currently 80, 81, 8080).
- Any detected HTTP servers get stored in the database for processing.
- Multiple threads initiate a download of the index page of these servers.
- These pages are parsed against a ruleset which identifies cameras. The vast majority of the relevant pages have title tags which can be used as identifiers.
- Results are stored in the database for geolocation.
- An online service is used to geolocate IP addresses so they can be presented in the world map.
- Having a functional atlas so we can scroll and zoom in.
- Being able to pick individual cameras from the map.
- Streaming open cameras and present the stream within the program.
The plan is to release the code under the GPL once the code is somewhat more sane. There’s been a great deal of prototyping going on so the conceptually pure architecture and the reality are still somewhat at odds.