Extract OSC 7 hostname parsing into helper functions

This commit is contained in:
Kristófer R
2024-10-31 21:55:02 -04:00
parent 3a3da82aa9
commit 3b0a34afbc

View File

@ -1030,6 +1030,48 @@ pub const StreamHandler = struct {
self.terminal.markSemanticPrompt(.command); self.terminal.markSemanticPrompt(.command);
} }
pub fn bufPrintHostnameFromFileUri(buf: []u8, uri: std.Uri) ![]const u8 {
// Get the raw string of the URI. Its unclear to me if the various
// tags of this enum guarantee no percent-encoding so we just
// check all of it. This isn't a performance critical path.
const host_component = uri.host orelse return error.NoHostnameInUri;
const host = switch (host_component) {
.raw => |v| v,
.percent_encoded => |v| v,
};
// When the "Private Wi-Fi address" setting is toggled on macOS the hostname
// is set to a string of digits separated by a colon, e.g. '12:34:56:78:90:12'.
// The URI will be parsed as if the last set o digit is a port, so we need to
// make sure that part is included when it's set.
if (uri.port) |port| {
var fbs = std.io.fixedBufferStream(buf);
std.fmt.format(fbs.writer().any(), "{s}:{d}", .{ host, port }) catch |err| switch (err) {
error.NoSpaceLeft => return error.NoSpaceLeft,
else => unreachable,
};
return fbs.getWritten();
}
return host;
}
pub fn isLocalHostname(hostname: []const u8) !bool {
// A 'localhost' hostname is always considered local.
if (std.mem.eql(u8, "localhost", hostname)) {
return true;
}
// If hostname is not "localhost" it must match our hostname.
var buf: [posix.HOST_NAME_MAX]u8 = undefined;
const ourHostname = posix.gethostname(&buf) catch |err| {
return err;
};
return std.mem.eql(u8, hostname, ourHostname);
}
pub fn reportPwd(self: *StreamHandler, url: []const u8) !void { pub fn reportPwd(self: *StreamHandler, url: []const u8) !void {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
log.warn("reportPwd unimplemented on windows", .{}); log.warn("reportPwd unimplemented on windows", .{});
@ -1048,57 +1090,34 @@ pub const StreamHandler = struct {
return; return;
} }
// RFC 793 defines port numbers as 16-bit numbers. 5 digits is sufficient to represent
// the maximum since 2^16 - 1 = 65_535.
// See https://www.rfc-editor.org/rfc/rfc793#section-3.1.
const PORT_NUMBER_MAX_DIGITS = 5;
// Make sure there is space for a max length hostname + the max number of digits.
var host_and_port_buf: [posix.HOST_NAME_MAX + PORT_NUMBER_MAX_DIGITS]u8 = undefined;
const hostname = bufPrintHostnameFromFileUri(&host_and_port_buf, uri) catch |err| switch (err) {
error.NoHostnameInUri => {
log.warn("OSC 7 uri must contain a hostname: {}", .{err});
return;
},
error.NoSpaceLeft => |e| {
log.warn("failed to get full hostname for OSC 7 validation: {}", .{e});
return;
},
};
// OSC 7 is a little sketchy because anyone can send any value from // OSC 7 is a little sketchy because anyone can send any value from
// any host (such an SSH session). The best practice terminals follow // any host (such an SSH session). The best practice terminals follow
// is to valid the hostname to be local. // is to valid the hostname to be local.
const host_valid = host_valid: { const host_valid = isLocalHostname(hostname) catch |err| switch (err) {
const host_component = uri.host orelse break :host_valid false; error.PermissionDenied, error.Unexpected => {
// Get the raw string of the URI. Its unclear to me if the various
// tags of this enum guarantee no percent-encoding so we just
// check all of it. This isn't a performance critical path.
const host = host: {
const h = switch (host_component) {
.raw => |v| v,
.percent_encoded => |v| v,
};
if (h.len == 0 or std.mem.eql(u8, "localhost", h)) {
break :host_valid true;
}
// When the "Private Wi-Fi address" setting is toggled on macOS the hostname
// is set to a string of digits separated by a colon, e.g. '12:34:56:78:90:12'.
// The URI will be parsed as if the last set o digit is a port, so we need to
// make sure that part is included when it's set.
if (uri.port) |port| {
// RFC 793 defines port numbers as 16-bit numbers. 5 digits is sufficient to represent
// the maximum since 2^16 - 1 = 65_535.
// See https://www.rfc-editor.org/rfc/rfc793#section-3.1.
const PORT_NUMBER_MAX_DIGITS = 5;
// Make sure there is space for a max length hostname + the max number of digits.
var host_and_port_buf: [posix.HOST_NAME_MAX + PORT_NUMBER_MAX_DIGITS]u8 = undefined;
const host_and_port = std.fmt.bufPrint(&host_and_port_buf, "{s}:{d}", .{ h, port }) catch |err| {
log.warn("failed to get full hostname for OSC 7 validation: {}", .{err});
break :host_valid false;
};
break :host host_and_port;
} else {
break :host h;
}
};
// Otherwise, it must match our hostname.
var buf: [posix.HOST_NAME_MAX]u8 = undefined;
const hostname = posix.gethostname(&buf) catch |err| {
log.warn("failed to get hostname for OSC 7 validation: {}", .{err}); log.warn("failed to get hostname for OSC 7 validation: {}", .{err});
break :host_valid false; return;
}; },
break :host_valid std.mem.eql(u8, host, hostname);
}; };
if (!host_valid) { if (!host_valid) {
log.warn("OSC 7 host must be local", .{}); log.warn("OSC 7 host must be local", .{});
return; return;