| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | /* | ||
| 3 | * This file is part of the Falcon Player (FPP) and is Copyright (C) | ||
| 4 | * 2013-2022 by the Falcon Player Developers. | ||
| 5 | * | ||
| 6 | * The Falcon Player (FPP) is free software, and is covered under | ||
| 7 | * multiple Open Source licenses. Please see the included 'LICENSES' | ||
| 8 | * file for descriptions of what files are covered by each license. | ||
| 9 | * | ||
| 10 | * This source file is covered under the LGPL v2.1 as described in the | ||
| 11 | * included LICENSE.LGPL file. | ||
| 12 | */ | ||
| 13 | |||
| 14 | // FPP-internal, deliberately NOT part of the plugin API. | ||
| 15 | // | ||
| 16 | // This lives outside fpphttp.h because it is the only helper of its kind that | ||
| 17 | // needs HttpRequest's *members* (getHeader/getPeerAddr) rather than just the | ||
| 18 | // HttpRequestPtr alias. Plugins vendor fpphttp.h (along with Plugin.h, | ||
| 19 | // fpphttp_types.h and fpp-json-fwd.h) and compile it against their own stand-in | ||
| 20 | // for drogon; a non-template inline function is emitted in every translation | ||
| 21 | // unit that includes its header, so putting this in fpphttp.h would force every | ||
| 22 | // such stub to grow both methods. Nothing outside FPP's own sources includes | ||
| 23 | // this file, so the requirement stays where it belongs. | ||
| 24 | |||
| 25 | #include "fpphttp.h" | ||
| 26 | |||
| 27 | #include <string> | ||
| 28 | |||
| 29 | // Best-effort real client IP for a request that may have come through FPP's | ||
| 30 | // local Apache reverse proxy (see etc/apache2.site - it proxies /api/* and | ||
| 31 | // friends to this process on 127.0.0.1:FPP_HTTP_PORT and, by Apache's | ||
| 32 | // mod_proxy default (ProxyAddHeaders On), appends X-Forwarded-For; the same | ||
| 33 | // default applies to the cross-host /proxy/<ip>/api/... rules used to reach | ||
| 34 | // another FPP's API through this one's Apache, so a multi-hop chain of FPP | ||
| 35 | // devices relaying a request ends up as multiple comma-separated entries). | ||
| 36 | // Logging the raw TCP peer for those requests always shows Apache's own | ||
| 37 | // loopback connection, indistinguishable from a local script/plugin calling | ||
| 38 | // this process directly. | ||
| 39 | // | ||
| 40 | // This is intentionally simple and NOT a trust/security check: if | ||
| 41 | // X-Forwarded-For is present at all, use its LEFTMOST entry (the original | ||
| 42 | // client, as far back through any proxy chain as the header reaches); | ||
| 43 | // otherwise fall back to the raw TCP peer. The header is trivially spoofable | ||
| 44 | // by anything that talks to this port directly - Apache only ever appends to | ||
| 45 | // whatever value it received, never validates or replaces what's already | ||
| 46 | // there - so treat this as best-effort attribution for logs, never as an | ||
| 47 | // access-control decision. | ||
| 48 | 683292 | inline std::string getEffectiveClientIP(const HttpRequestPtr& req) { | |
| 49 |
3/6✓ Branch 0 taken 683292 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 683292 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 683292 times.
✗ Branch 5 not taken.
|
1366584 | std::string xff = req->getHeader("x-forwarded-for"); |
| 50 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 683292 times.
|
683292 | if (!xff.empty()) { |
| 51 | ✗ | std::string first = xff.substr(0, xff.find(',')); | |
| 52 | ✗ | size_t b = first.find_first_not_of(" \t"); | |
| 53 | ✗ | size_t e = first.find_last_not_of(" \t"); | |
| 54 | ✗ | if (b != std::string::npos) { | |
| 55 | ✗ | return first.substr(b, e - b + 1); | |
| 56 | } | ||
| 57 | ✗ | } | |
| 58 |
2/4✓ Branch 0 taken 683292 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 683292 times.
✗ Branch 3 not taken.
|
683292 | return req->getPeerAddr().toIp(); |
| 59 | 683292 | } | |
| 60 |