blob: 2be2c9bf05ca1802860d959bdd65688346c03dc1 [file]
/*****************************************************************************\
url_parser_internal.c - url parser handler
*****************************************************************************
* Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include "http_parser_url_port.h"
#include "slurm/slurm.h"
#include "src/common/http.h"
#include "src/common/log.h"
#include "src/common/read_config.h"
#include "src/common/xmalloc.h"
#include "src/common/xstring.h"
#include "src/interfaces/url_parser.h"
/* Required Slurm plugin symbols: */
const char plugin_name[] = "Slurm url_parser plugin";
const char plugin_type[] = URL_PARSER_PREFIX URL_PARSER_INTERNAL_PLUGIN;
const uint32_t plugin_version = SLURM_VERSION_NUMBER;
#define URL_PARSE_ERROR(error_number, name, buffer) \
_on_url_parse_error(error_number, name, buffer, __func__)
#define LOG_URL_PARSE(name, buffer, fmt, ...) \
_log_url_parse(name, buffer, __func__, fmt, ##__VA_ARGS__)
static void _log_url_parse_buffer(const char *name, const char *caller,
const char *log_str, const buf_t *buffer)
{
const void *begin = get_buf_data(buffer);
const size_t bytes = get_buf_offset(buffer);
xassert(buffer->magic == BUF_MAGIC);
xassert(begin);
xassert(begin <= (begin + bytes));
log_flag(DATA, "%s: [%s] URL PARSE [0,%zu)@0x%"PRIxPTR" %s",
caller, name, bytes, (uintptr_t) begin, log_str);
log_flag_hex_range(NET_RAW, begin, bytes, 0, bytes, "%s: [%s] %s",
caller, name, log_str);
}
static void _log_url_parse(const char *name, const buf_t *buffer,
const char *caller, const char *fmt, ...)
{
char *log_str = NULL;
if (!(slurm_conf.debug_flags & DEBUG_FLAG_DATA) ||
(get_log_level() < LOG_LEVEL_VERBOSE))
return;
xassert(fmt);
if (fmt) {
va_list ap;
va_start(ap, fmt);
log_str = vxstrfmt(fmt, ap);
va_end(ap);
}
if (buffer)
_log_url_parse_buffer(name, caller, log_str, buffer);
else
log_flag(DATA, "%s: [%s] URL PARSE %s", caller, name, log_str);
xfree(log_str);
}
/*
* Log that URL parsing failed
* NOTE: use URL_PARSE_ERROR() instead of calling directly
* IN error_number - Slurm error encountered
* IN buffer - buffer being parsed
* RET error_number
*/
static int _on_url_parse_error(slurm_err_t error_number, const char *name,
const buf_t *buffer, const char *caller)
{
LOG_URL_PARSE(name, buffer, "Parsing failed: %s",
slurm_strerror(error_number));
return error_number;
}
/*
* Load and initialize URL parser plugin
* RET SLURM_SUCCESS or error
*/
extern int url_parser_p_init(void)
{
return SLURM_SUCCESS;
}
/* Unload URL plugin */
extern void url_parser_p_fini(void) {}
/*
* Parse URL using a copy of libhttp_parser's URL parser implementation
* Examples:
* host:port
* https://user@[host]:port/path/?query#fragment
* RET
* SLURM_SUCCESS: parsed port successfully
* ESLURM_URL_UNSUPPORTED_FORMAT: library doesn't support format
* *: error
*/
static int _library_url_parse(const char *name, const buf_t *buffer, url_t *dst)
{
struct parsed_url url;
const void *data = get_buf_data(buffer);
const size_t bytes = get_buf_offset(buffer);
int rc = EINVAL;
memset(&url, 0, sizeof(url));
xassert(data);
xassert(bytes > 0);
xassert(size_buf(buffer) >= bytes);
/* Try parsing a full URL and then try parsing only a host:port pair */
if (parse_url(data, bytes, false, &url) &&
parse_url(data, bytes, true, &url))
return ESLURM_URL_UNSUPPORTED_FORMAT;
if ((url.field_set & (1 << UF_SCHEMA)) &&
(rc = url_get_scheme((data + url.field_data[UF_SCHEMA].off),
url.field_data[UF_SCHEMA].len, &dst->scheme)))
return rc;
if (url.field_set & (1 << UF_HOST)) {
xassert(url.field_data[UF_HOST].len <= bytes);
dst->host = xstrndup((data + url.field_data[UF_HOST].off),
url.field_data[UF_HOST].len);
}
if (url.field_set & (1 << UF_PORT)) {
xassert(url.field_data[UF_PORT].len <= bytes);
dst->port = xstrndup((data + url.field_data[UF_PORT].off),
url.field_data[UF_PORT].len);
}
if (url.field_set & (1 << UF_PATH)) {
xassert(url.field_data[UF_PATH].len <= bytes);
dst->path = xstrndup((data + url.field_data[UF_PATH].off),
url.field_data[UF_PATH].len);
}
if (url.field_set & (1 << UF_QUERY)) {
xassert(url.field_data[UF_QUERY].len <= bytes);
dst->query = xstrndup((data + url.field_data[UF_QUERY].off),
url.field_data[UF_QUERY].len);
}
if (url.field_set & (1 << UF_FRAGMENT)) {
xassert(url.field_data[UF_FRAGMENT].len <= bytes);
dst->fragment =
xstrndup((data + url.field_data[UF_FRAGMENT].off),
url.field_data[UF_FRAGMENT].len);
}
if (url.field_set & (1 << UF_USERINFO)) {
xassert(url.field_data[UF_USERINFO].len <= bytes);
dst->user = xstrndup((data + url.field_data[UF_USERINFO].off),
url.field_data[UF_USERINFO].len);
}
return SLURM_SUCCESS;
}
/*
* Parse URL where only the port is given.
* Examples:
* :8080
* :ssh
*
* RET
* SLURM_SUCCESS: parsed port successfully
* ESLURM_URL_UNSUPPORTED_FORMAT: not a port only URL
* *: error
*/
static int _parse_only_port(const char *name, const buf_t *buffer, url_t *dst)
{
const char *data = get_buf_data(buffer);
const size_t bytes = get_buf_offset(buffer);
if ((bytes < 1) || (data[0] != ':'))
return ESLURM_URL_UNSUPPORTED_FORMAT;
if (bytes == 1)
return ESLURM_URL_EMPTY;
dst->port = xstrndup((data + 1), (bytes - 1));
return SLURM_SUCCESS;
}
/*
* Parse URL where only the port is given.
* Examples:
* unix:/path/to/socket
*
* RET
* SLURM_SUCCESS: parsed port successfully
* ESLURM_URL_UNSUPPORTED_FORMAT: not a port only URL
* *: error
*/
static int _parse_unix_url(const char *name, const buf_t *buffer, url_t *dst)
{
const char *data = get_buf_data(buffer);
const size_t bytes = get_buf_offset(buffer);
if ((bytes < UNIX_PREFIX_BYTES) ||
xstrncmp(UNIX_PREFIX, data, UNIX_PREFIX_BYTES))
return ESLURM_URL_UNSUPPORTED_FORMAT;
if (bytes == UNIX_PREFIX_BYTES)
return ESLURM_URL_EMPTY;
dst->scheme = URL_SCHEME_UNIX;
dst->path = xstrndup((data + UNIX_PREFIX_BYTES),
(bytes - UNIX_PREFIX_BYTES));
return SLURM_SUCCESS;
}
/*
* Parse URL
* IN name - name used for logging
* IN buffer - buffer containing string to parse
* IN/OUT url - URL to populate with parsed components of URL
* RET SLURM_SUCCESS or error
*/
extern int url_parser_p_parse(const char *name, const buf_t *buffer, url_t *dst)
{
int rc = EINVAL;
xassert(!buffer || (buffer->magic == BUF_MAGIC));
xassert(dst);
xassert(name && name[0]);
url_free_members(dst);
if (!buffer || !get_buf_offset(buffer))
return URL_PARSE_ERROR(ESLURM_URL_EMPTY, name, buffer);
/* Catch any errant NULL terminators */
if (strnlen(get_buf_data(buffer), get_buf_offset(buffer)) !=
get_buf_offset(buffer))
return URL_PARSE_ERROR(ESLURM_URL_NON_NULL_TERMINATOR, name,
buffer);
rc = _library_url_parse(name, buffer, dst);
if (rc == ESLURM_URL_UNSUPPORTED_FORMAT) {
url_free_members(dst);
rc = _parse_only_port(name, buffer, dst);
}
if (rc == ESLURM_URL_UNSUPPORTED_FORMAT) {
url_free_members(dst);
rc = _parse_unix_url(name, buffer, dst);
}
if (rc) {
/*
* If none of the parsers apply, then consider the URL to be an
* invalid format
*/
if (rc == ESLURM_URL_UNSUPPORTED_FORMAT)
rc = ESLURM_URL_INVALID_FORMATING;
url_free_members(dst);
return URL_PARSE_ERROR(rc, name, buffer);
}
LOG_URL_PARSE(
name, buffer,
"Parsed URL scheme:%s host:%s port:%s user:%s path:%s query:%s fragment:%s",
url_get_scheme_string(dst->scheme), dst->host, dst->port,
dst->user, dst->path, dst->query, dst->fragment);
return rc;
}