1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 13:18:13 +00:00

LibC: Move parsing of service file line to static helper function

This patch adds `parse_service_file_line()` which both `getservent()`
and `getaddrinfo()` makes use of.
This commit is contained in:
Marcus Nilsson 2023-08-18 18:06:40 +02:00 committed by Andrew Kaster
parent e2ca144cf9
commit a2f0804f22

View file

@ -45,7 +45,14 @@ static constexpr u32 lookup_server_endpoint_magic = "LookupServer"sv.hash();
static FILE* services_file = nullptr; static FILE* services_file = nullptr;
static char const* services_path = "/etc/services"; static char const* services_path = "/etc/services";
static bool fill_getserv_buffers(char const* line, ssize_t read); struct ServiceFileLine {
String name;
String protocol;
int port;
Vector<ByteBuffer> aliases;
};
static ErrorOr<Optional<ServiceFileLine>> parse_service_file_line(char const* line, ssize_t read);
static servent __getserv_buffer; static servent __getserv_buffer;
static DeprecatedString __getserv_name_buffer; static DeprecatedString __getserv_name_buffer;
static DeprecatedString __getserv_protocol_buffer; static DeprecatedString __getserv_protocol_buffer;
@ -438,13 +445,22 @@ struct servent* getservent()
} }
}); });
Optional<ServiceFileLine> service_file_line = {};
// Read lines from services file until an actual service name is found. // Read lines from services file until an actual service name is found.
do { do {
read = getline(&line, &len, services_file); read = getline(&line, &len, services_file);
service_file_offset += read; service_file_offset += read;
if (read > 0 && (line[0] >= 65 && line[0] <= 122)) {
auto service_file_line_or_error = parse_service_file_line(line, read);
if (service_file_line_or_error.is_error())
return nullptr;
service_file_line = service_file_line_or_error.release_value();
if (service_file_line.has_value())
break; break;
}
} while (read != -1); } while (read != -1);
if (read == -1) { if (read == -1) {
fclose(services_file); fclose(services_file);
@ -453,10 +469,16 @@ struct servent* getservent()
return nullptr; return nullptr;
} }
servent* service_entry = nullptr; if (!service_file_line.has_value())
if (!fill_getserv_buffers(line, read))
return nullptr; return nullptr;
servent* service_entry = nullptr;
__getserv_name_buffer = service_file_line.value().name.to_deprecated_string();
__getserv_port_buffer = service_file_line.value().port;
__getserv_protocol_buffer = service_file_line.value().protocol.to_deprecated_string();
__getserv_alias_list_buffer = service_file_line.value().aliases;
__getserv_buffer.s_name = const_cast<char*>(__getserv_name_buffer.characters()); __getserv_buffer.s_name = const_cast<char*>(__getserv_name_buffer.characters());
__getserv_buffer.s_port = htons(__getserv_port_buffer); __getserv_buffer.s_port = htons(__getserv_port_buffer);
__getserv_buffer.s_proto = const_cast<char*>(__getserv_protocol_buffer.characters()); __getserv_buffer.s_proto = const_cast<char*>(__getserv_protocol_buffer.characters());
@ -550,53 +572,53 @@ void endservent()
services_file = nullptr; services_file = nullptr;
} }
// Fill the service entry buffer with the information contained static ErrorOr<Optional<ServiceFileLine>> parse_service_file_line(char const* line, ssize_t read)
// in the currently read line, returns true if successful,
// false if failure occurs.
static bool fill_getserv_buffers(char const* line, ssize_t read)
{ {
// Splitting the line by tab delimiter and filling the servent buffers name, port, and protocol members. // If the line isn't a service (eg. empty or a comment)
if (read <= 0 || line[0] < 65 || line[0] > 122)
return { Optional<ServiceFileLine> {} };
auto split_line = StringView(line, read).replace(" "sv, "\t"sv, ReplaceMode::All).split('\t'); auto split_line = StringView(line, read).replace(" "sv, "\t"sv, ReplaceMode::All).split('\t');
// This indicates an incorrect file format. if (split_line.size() < 2)
// Services file entries should always at least contain return Error::from_string_view("malformed service file"sv);
// name and port/protocol, separated by tabs.
if (split_line.size() < 2) {
warnln("getservent(): malformed services file");
return false;
}
__getserv_name_buffer = split_line[0];
auto port_protocol_split = DeprecatedString(split_line[1]).split('/'); auto name = TRY(String::from_deprecated_string(split_line[0]));
if (port_protocol_split.size() < 2) {
warnln("getservent(): malformed services file");
return false;
}
auto number = port_protocol_split[0].to_int();
if (!number.has_value())
return false;
__getserv_port_buffer = number.value(); auto port_protocol = TRY(String::from_deprecated_string(split_line[1]));
auto port_protocol_split = TRY(port_protocol.split('/'));
// Remove any annoying whitespace at the end of the protocol. if (port_protocol_split.size() < 2)
__getserv_protocol_buffer = port_protocol_split[1].replace(" "sv, ""sv, ReplaceMode::All).replace("\t"sv, ""sv, ReplaceMode::All).replace("\n"sv, ""sv, ReplaceMode::All); return Error::from_string_view("malformed service file"sv);
__getserv_alias_list_buffer.clear();
// If there are aliases for the service, we will fill the alias list buffer. auto port = port_protocol_split[0].to_number<int>();
if (!port.has_value())
return Error::from_string_view("port isn't a number"sv);
// Remove whitespace at the end of the protocol
auto protocol = TRY(port_protocol_split[1].replace(" "sv, ""sv, ReplaceMode::All));
protocol = TRY(protocol.replace("\t"sv, ""sv, ReplaceMode::All));
protocol = TRY(protocol.replace("\n"sv, ""sv, ReplaceMode::All));
Vector<ByteBuffer> aliases;
// If there are aliases for the service, we will fill the aliases list
if (split_line.size() > 2 && !split_line[2].starts_with('#')) { if (split_line.size() > 2 && !split_line[2].starts_with('#')) {
for (size_t i = 2; i < split_line.size(); i++) { for (size_t i = 2; i < split_line.size(); i++) {
if (split_line[i].starts_with('#')) { if (split_line[i].starts_with('#')) {
break; break;
} }
auto alias = split_line[i].to_byte_buffer(); auto alias = split_line[i].to_byte_buffer();
if (alias.try_append("\0", sizeof(char)).is_error()) if (alias.try_append("\0", sizeof(char)).is_error())
return false; return Error::from_string_view("Failed to add null-byte to service alias"sv);
__getserv_alias_list_buffer.append(move(alias));
aliases.append(move(alias));
} }
} }
return true; return ServiceFileLine {
name, protocol, port.value(), aliases
};
} }
struct protoent* getprotoent() struct protoent* getprotoent()
@ -823,11 +845,7 @@ int getaddrinfo(char const* __restrict node, char const* __restrict service, con
long port; long port;
int socktype; int socktype;
struct ServiceData { Optional<ServiceFileLine> service_file_line = {};
String protocol;
u32 port;
};
Optional<ServiceData> service_data = {};
if ((!hints || (hints->ai_flags & AI_NUMERICSERV) == 0) && service) { if ((!hints || (hints->ai_flags & AI_NUMERICSERV) == 0) && service) {
services_file = fopen(services_path, "r"); services_file = fopen(services_path, "r");
@ -835,83 +853,42 @@ int getaddrinfo(char const* __restrict node, char const* __restrict service, con
return EAI_FAIL; return EAI_FAIL;
} }
while (true) { auto close_services_file_handler = ScopeGuard([&] {
char* line = nullptr; fclose(services_file);
size_t length = 0; });
ssize_t read;
// Read lines from services file until an actual service name is found. char* line = nullptr;
size_t length = 0;
ssize_t read;
while (true) {
do { do {
read = getline(&line, &length, services_file); read = getline(&line, &length, services_file);
if (read > 0 && (line[0] >= 65 && line[0] <= 122)) {
auto service_file_line_or_error = parse_service_file_line(line, read);
if (service_file_line_or_error.is_error())
return EAI_SYSTEM;
service_file_line = service_file_line_or_error.release_value();
if (service_file_line.has_value())
break; break;
}
} while (read != -1); } while (read != -1);
if (read == -1) { if (read == -1 || !service_file_line.has_value())
break; break;
}
auto split_line = StringView(line, read).replace(" "sv, "\t"sv, ReplaceMode::All).split('\t'); if (service_file_line.value().name != service)
if (split_line.size() < 2)
return EAI_FAIL;
auto name_or_error = String::from_deprecated_string(split_line[0]);
if (name_or_error.is_error())
return EAI_SYSTEM;
auto name = name_or_error.release_value();
if (name != service)
continue; continue;
auto port_protocol_or_error = String::from_deprecated_string(split_line[1]); if (service_file_line.value().protocol != proto)
if (port_protocol_or_error.is_error())
return EAI_SYSTEM;
auto port_protocol = port_protocol_or_error.release_value();
auto port_protocol_split_or_error = port_protocol.split('/');
if (port_protocol_split_or_error.is_error())
return EAI_SYSTEM;
auto port_protocol_split = port_protocol_split_or_error.release_value();
if (port_protocol_split.size() < 2)
return EAI_SYSTEM;
auto number = port_protocol_split[0].to_number<u32>();
if (!number.has_value())
return EAI_SYSTEM;
auto port = number.value();
// Remove any annoying whitespace at the end of the protocol.
auto protocol_or_error = port_protocol_split[1].replace(" "sv, ""sv, ReplaceMode::All);
if (protocol_or_error.is_error())
return EAI_SYSTEM;
protocol_or_error = protocol_or_error.value().replace("\t"sv, ""sv, ReplaceMode::All);
if (protocol_or_error.is_error())
return EAI_SYSTEM;
protocol_or_error = protocol_or_error.value().replace("\n"sv, ""sv, ReplaceMode::All);
if (protocol_or_error.is_error())
return EAI_SYSTEM;
auto protocol = protocol_or_error.release_value();
if (protocol != proto)
continue; continue;
service_data = { { protocol,
port } };
break; break;
} }
if (fclose(services_file) < 0)
return EAI_SYSTEM;
} }
if (!service_data.has_value()) {
if (!service_file_line.has_value()) {
if (service) { if (service) {
char* end; char* end;
port = htons(strtol(service, &end, 10)); port = htons(strtol(service, &end, 10));
@ -926,8 +903,8 @@ int getaddrinfo(char const* __restrict node, char const* __restrict service, con
else else
socktype = SOCK_STREAM; socktype = SOCK_STREAM;
} else { } else {
port = service_data.value().port; port = service_file_line.value().port;
socktype = service_data.value().protocol == "tcp" ? SOCK_STREAM : SOCK_DGRAM; socktype = service_file_line.value().protocol == "tcp" ? SOCK_STREAM : SOCK_DGRAM;
} }
addrinfo* first_info = nullptr; addrinfo* first_info = nullptr;