blob: 440db9b9d86599d9d7a053b9fd947f5c5cfe4d9b [file] [log] [blame]
/*
* Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <ctype.h>
#include <string.h>
#include "service_name.h"
/*
* Structure to hold result of parsing the service name. Sets
* indices and length of the following fields. Lengths are set to zero
* if field is not present.
*/
struct sn_parsed_fields
{
size_t authority_pos;
size_t authority_len;
size_t service_pos;
size_t service_len;
size_t version_pos;
size_t version_len;
size_t instance_pos;
size_t instance_len;
};
/*
* Find the lengthof the current field, up to the specified separator or end
* of the service name.
*/
static size_t field_len(const char *sn, size_t from, size_t len, char separator)
{
size_t field_len = 0;
size_t pos = from;
while (pos < len) {
if (sn[pos] == separator) break;
++pos;
++field_len;
}
return field_len;
}
/*
* Find the next field in the service name after the specified separator from the
* specified position.
*/
static bool find_next(const char *sn, size_t from, size_t len, char separator, size_t *next)
{
bool found = false;
size_t pos = from;
while (pos < len) {
if (sn[pos] == separator) {
*next = pos + 1;
found = (*next < len);
break;
}
++pos;
}
return found;
}
/*
* Parse the service name. Returns true if is a legal service
* name and mandatory fields are present;
*/
static bool sn_parse(const char *sn, struct sn_parsed_fields *fields)
{
size_t sn_len = strlen(sn);
size_t field_pos = 0;
fields->authority_pos = 0;
fields->authority_len = 0;
fields->service_pos = 0;
fields->service_len = 0;
fields->version_pos = 0;
fields->version_len = 0;
fields->instance_pos = 0;
fields->instance_len = 0;
/* Absorb urn: if present */
if (memcmp("urn", &sn[field_pos], strlen("urn")) == 0) {
if (!find_next(sn, field_pos, sn_len, ':', &field_pos)) return false;
}
/* Check it is a service name */
if (memcmp("sn", &sn[field_pos], strlen("sn")) != 0) {
/* Not a service name */
return false;
}
/* Expect the authority field */
if (find_next(sn, field_pos, sn_len, ':', &field_pos)) {
fields->authority_pos = field_pos;
fields->authority_len = field_len(sn, field_pos, sn_len, ':');
}
else {
/* Missing mandatory authority field */
return false;
}
/* Expect the service field */
if (find_next(sn, field_pos, sn_len, ':', &field_pos)) {
fields->service_pos = field_pos;
fields->service_len = field_len(sn, field_pos, sn_len, ':');
/* Check for the optional version */
if (find_next(sn, field_pos, field_pos + fields->service_len, '.', &field_pos)) {
fields->version_pos = field_pos;
fields->version_len = field_len(sn, field_pos, sn_len, ':');
}
}
else {
/* Missing mandatory service field */
return false;
}
/* Check for optional instance */
if (find_next(sn, field_pos, sn_len, ':', &field_pos)) {
fields->instance_pos = field_pos;
fields->instance_len = sn_len - field_pos;
}
return true;
}
bool sn_is_valid(const char *sn)
{
struct sn_parsed_fields fields;
return sn_parse(sn, &fields);
}
bool sn_check_authority(const char *sn, const char *auth)
{
bool matched = false;
struct sn_parsed_fields fields;
if (sn_parse(sn, &fields) && fields.authority_len) {
matched = (memcmp(auth, &sn[fields.authority_pos], strlen(auth)) == 0);
}
return matched;
}
bool sn_check_service(const char *sn, const char *service)
{
bool matched = false;
struct sn_parsed_fields fields;
if (sn_parse(sn, &fields) && fields.service_len) {
matched = (memcmp(service, &sn[fields.service_pos], strlen(service)) == 0);
}
return matched;
}
unsigned int sn_get_service_instance(const char *sn)
{
unsigned int instance = 0;
struct sn_parsed_fields fields;
if (sn_parse(sn, &fields) && fields.instance_len) {
/* Instance must be expressed as a decimal */
unsigned int multiplier = 1;
for (size_t i = 0; i < fields.instance_len; i++) {
size_t digit_index = fields.instance_pos + fields.instance_len - 1 - i;
char digit = sn[digit_index];
if (isdigit((int)digit)) {
instance += ((digit - '0') * multiplier);
multiplier *= 10;
}
else {
/* Invalid instance string */
instance = 0;
break;
}
}
}
return instance;
}
size_t sn_read_service(const char *sn, char *buf, size_t buf_len)
{
size_t field_len = 0;
memset(buf, 0, buf_len);
struct sn_parsed_fields fields;
if (sn_parse(sn, &fields) && fields.service_len && (fields.service_len < buf_len)) {
field_len = fields.service_len;
memcpy(buf, &sn[fields.service_pos], field_len);
buf[field_len] = '\0';
}
return field_len;
}