handle client ip without forwarding headers
All checks were successful
Generic zig build / build (push) Successful in 1m17s
Generic zig build / deploy (push) Successful in 13s

This commit is contained in:
Emil Lerch 2026-01-05 16:13:59 -08:00
parent bf4ea51424
commit 6e829eea30
Signed by: lobo
GPG key ID: A7B62D657EF764F8
2 changed files with 27 additions and 19 deletions

View file

@ -21,6 +21,17 @@ pub fn handleWeather(
req: *httpz.Request, req: *httpz.Request,
res: *httpz.Response, res: *httpz.Response,
) !void { ) !void {
var client_ip_buf: [47]u8 = undefined;
// We need IP possibly for location, and possibly to determine implicit Imperial/Metric
// Let's get it once here
const client_ip = blk: {
const client_ip = getClientIpFromHeaders(req);
if (client_ip.len == 0) {
const full_location = try std.fmt.bufPrint(&client_ip_buf, "{f}", .{req.conn.address});
break :blk full_location[0..std.mem.lastIndexOf(u8, full_location, ":").?];
}
break :blk client_ip;
};
// Get location from path parameter or query string // Get location from path parameter or query string
const location = req.param("location") orelse blk: { const location = req.param("location") orelse blk: {
// Check query string for location parameter // Check query string for location parameter
@ -33,14 +44,14 @@ pub fn handleWeather(
if (params.location) |loc| { if (params.location) |loc| {
break :blk loc; break :blk loc;
} else { } else break :blk client_ip; // no location, just use client ip instead
// Fall back to IP-based detection
const client_ip = getClientIP(req);
std.log.debug("No location requested, using IP address '{s}'", .{client_ip});
break :blk client_ip;
}
}; };
if (location.len == 0) {
res.content_type = .TEXT;
res.body = "Sorry, we are unable to determine your location at this time. Try with /<location> or /?location=<location>\n";
return;
}
// Handle special endpoints // Handle special endpoints
if (location[0] == ':') { if (location[0] == ':') {
if (std.mem.eql(u8, location, ":help")) { if (std.mem.eql(u8, location, ":help")) {
@ -54,10 +65,10 @@ pub fn handleWeather(
} }
} }
try handleWeatherInternal(opts, req, res, location); try handleWeatherInternal(opts, req, res, location, client_ip);
} }
fn getClientIP(req: *httpz.Request) []const u8 { fn getClientIpFromHeaders(req: *httpz.Request) []const u8 {
// Check X-Forwarded-For header first (for proxies) // Check X-Forwarded-For header first (for proxies)
if (req.header("x-forwarded-for")) |xff| { if (req.header("x-forwarded-for")) |xff| {
return parseXForwardedFor(xff); return parseXForwardedFor(xff);
@ -68,7 +79,7 @@ fn getClientIP(req: *httpz.Request) []const u8 {
return real_ip; return real_ip;
} }
// Fall back to empty (no IP available) // Fall back to client connection
return ""; return "";
} }
@ -86,6 +97,7 @@ fn handleWeatherInternal(
req: *httpz.Request, req: *httpz.Request,
res: *httpz.Response, res: *httpz.Response,
location_query: []const u8, location_query: []const u8,
client_ip: []const u8,
) !void { ) !void {
const req_alloc = req.arena; const req_alloc = req.arena;
@ -151,12 +163,8 @@ fn handleWeatherInternal(
break :blk true; break :blk true;
} }
} }
const client_ip = getClientIP(req); if (client_ip.len > 0 and opts.geoip.isUSIp(client_ip))
if (client_ip.len > 0) { break :blk true;
if (opts.geoip.isUSIP(client_ip)) {
break :blk true;
}
}
break :blk false; break :blk false;
}; };

View file

@ -89,7 +89,7 @@ fn lookupInternal(mmdb: *c.MMDB_s, ip: []const u8) !c.MMDB_lookup_result_s {
return result; return result;
} }
pub fn isUSIP(self: *GeoIP, ip: []const u8) bool { pub fn isUSIp(self: *GeoIP, ip: []const u8) bool {
const result = lookupInternal(&self.mmdb, ip) catch return false; const result = lookupInternal(&self.mmdb, ip) catch return false;
if (!result.found_entry) return false; if (!result.found_entry) return false;
@ -156,10 +156,10 @@ test "isUSIP detects US IPs" {
defer geoip.deinit(); defer geoip.deinit();
// Test that the function doesn't crash with various IPs // Test that the function doesn't crash with various IPs
_ = geoip.isUSIP("8.8.8.8"); _ = geoip.isUSIp("8.8.8.8");
_ = geoip.isUSIP("1.1.1.1"); _ = geoip.isUSIp("1.1.1.1");
// Test invalid IP returns false // Test invalid IP returns false
const invalid = geoip.isUSIP("invalid"); const invalid = geoip.isUSIp("invalid");
try std.testing.expect(!invalid); try std.testing.expect(!invalid);
} }