Summary
In the announce handler, the result of the hit INSERT is discarded. In index.php the block that should handle a failed write is empty:
if(!($stmt->execute(
array(
":hash" => htmlentities(bin2hex($_GET["info_hash"]), ENT_QUOTES),
":timestamp" => time(),
":addr" => $addrs,
":agent" => htmlentities($_SERVER["HTTP_USER_AGENT"],ENT_QUOTES)
)
))){
//failed to insert.
}
The PDO connection uses PDO::ERRMODE_SILENT, so a failed write raises no exception, and the handler continues on to emit the normal failure reason reply containing the caller's IP. A client (and the operator watching the panel) cannot tell a dropped write from a stored one.
For a tool whose purpose is to confirm which address leaked, an empty hit list then reads as "nothing leaked" when it may actually mean "nothing was saved" — for example after a backup or permissions change leaves the database file or its directory read-only.
Reproduce
With the app running, issue one good announce, then make the database directory unwritable by the web server user (SQLite needs to create a journal there to commit a write, but can still read):
# H = a hash issued by the app; announce = GET /?info_hash=<percent-encoded H>
curl -s -A ok "http://localhost/?info_hash=$ENC" >/dev/null # stored (1 row)
# make /var/www/html (the DB's directory) owned by root, mode 755
curl -s -A dropped "http://localhost/?info_hash=$ENC" # returns the normal IP reply...
# ...but the row count is unchanged: the 'dropped' write was silently lost
Observed: the second announce returns d14:failure reason14:IP: 172.17.0.1e (a normal, success-shaped reply) while no row is added.
Fix
A PR follows that emits the same Database failure bencoded reply the prepare-failure path above already uses, so the client is told the write did not happen.
Found during a review of the codebase; part of the same review as #20.
Summary
In the announce handler, the result of the hit
INSERTis discarded. Inindex.phpthe block that should handle a failed write is empty:The PDO connection uses
PDO::ERRMODE_SILENT, so a failed write raises no exception, and the handler continues on to emit the normalfailure reasonreply containing the caller's IP. A client (and the operator watching the panel) cannot tell a dropped write from a stored one.For a tool whose purpose is to confirm which address leaked, an empty hit list then reads as "nothing leaked" when it may actually mean "nothing was saved" — for example after a backup or permissions change leaves the database file or its directory read-only.
Reproduce
With the app running, issue one good announce, then make the database directory unwritable by the web server user (SQLite needs to create a journal there to commit a write, but can still read):
Observed: the second announce returns
d14:failure reason14:IP: 172.17.0.1e(a normal, success-shaped reply) while no row is added.Fix
A PR follows that emits the same
Database failurebencoded reply the prepare-failure path above already uses, so the client is told the write did not happen.Found during a review of the codebase; part of the same review as #20.