summaryrefslogblamecommitdiffstats
path: root/src/server/helper.c
blob: c86fcb6437bafd2445467f49dbd0f473d9909320 (plain) (tree)

































































































































































                                                                                                                 






                                                
#include "helper.h"
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <glib/gmacros.h>
#include "../config.h"

/**
 * Parse IPv4 or IPv6 address in string representation to a suitable format usable by the BSD socket library
 * @string eg. "1.2.3.4" or "2a01::10:5", optially with port appended, eg "1.2.3.4:6666" or "[2a01::10:5]:6666"
 * @af will contain either AF_INET or AF_INET6
 * @addr will contain the address in network representation
 * @port will contain the port in network representation, defaulting to #define PORT if none was given
 * returns 1 on success, 0 in failure. contents of af, addr and port are undefined in the latter case
 * !! Contents of @string might be modified by this function !!
 */
char parse_address(char *string, dnbd3_host_t *host)
{
	struct in_addr v4;
	struct in6_addr v6;

	// Try IPv4 without port
	if (1 == inet_pton(AF_INET, string, &v4))
	{
		host->type = AF_INET;
		memcpy(host->addr, &v4, 4);
		host->port = htons(PORT);
		return 1;
	}
	// Try IPv6 without port
	if (1 == inet_pton(AF_INET6, string, &v6))
	{
		host->type = AF_INET6;
		memcpy(host->addr, &v6, 16);
		host->port = htons(PORT);
		return 1;
	}

	// Scan for port
	char *portpos = NULL, *ptr = string;
	while (*ptr)
	{
		if (*ptr == ':')
			portpos = ptr;
		++ptr;
	}
	if (portpos == NULL)
		return 0; // No port in string
	// Consider IP being surrounded by [ ]
	if (*string == '[' && *(portpos - 1) == ']')
	{
		++string;
		*(portpos - 1) = '\0';
	}
	*portpos++ = '\0';
	int p = atoi(portpos);
	if (p < 1 || p > 65535)
		return 0; // Invalid port
	host->port = htons((uint16_t)p);

	// Try IPv4 with port
	if (1 == inet_pton(AF_INET, string, &v4))
	{
		host->type = AF_INET;
		memcpy(host->addr, &v4, 4);
		return 1;
	}
	// Try IPv6 with port
	if (1 == inet_pton(AF_INET6, string, &v6))
	{
		host->type = AF_INET6;
		memcpy(host->addr, &v6, 16);
		return 1;
	}

	// FAIL
	return 0;
}

/**
 * Convert a host and port (network byte order) to printable representation.
 * Worst case required buffer len is 48, eg. [1234:1234:1234:1234:1234:1234:1234:1234]:12345 (+ \0)
 * Returns TRUE on success, FALSE on error
 */
char host_to_string(const dnbd3_host_t *host, char *target, size_t targetlen)
{
	// Worst case: Port 5 chars, ':' to separate ip and port 1 char, terminating null 1 char = 7, [] for IPv6
	if (targetlen < 10)
		return FALSE;
	if (host->type == AF_INET6)
	{
		*target++ = '[';
		inet_ntop(AF_INET6, host->addr, target, targetlen - 9);
		target += strlen(target);
		*target++ = ']';
	}
	else if (host->type == AF_INET)
	{
		inet_ntop(AF_INET, host->addr, target, targetlen - 7);
		target += strlen(target);
	}
	else
	{
		snprintf(target, targetlen, "<?addrtype=%d>", (int)host->type);
		return FALSE;
	}
	*target = '\0';
	if (host->port != 0)
	{
		// There are still at least 7 bytes left in the buffer, port is at most 5 bytes + ':' + '\0' = 7
		snprintf(target, 7, ":%d", (int)ntohs(host->port));
	}
	return TRUE;
}

char is_valid_namespace(char *namespace)
{
	if (*namespace == '\0' || *namespace == '/')
		return 0; // Invalid: Length = 0 or starting with a slash
	while (*namespace)
	{
		if (*namespace != '/' && *namespace != '-' && (*namespace < 'a' || *namespace > 'z')
		        && (*namespace < 'A' || *namespace > 'Z')
		        && (*namespace < '0' || *namespace > '9'))
			return 0;
		++namespace;
	}
	if (strstr(namespace, "//") != NULL)
		return 0; // Invalid: Double slash
	if (*(namespace - 1) == '/')
		return 0; // Invalid: Ends in a slash
	return 1;
}

char is_valid_imagename(char *namespace)
{
	if (*namespace == '\0' || *namespace == ' ')
		return 0; // Invalid: Length = 0 or starting with a space
	while (*namespace)
	{
		// Check for invalid chars
		if (*namespace != '.' && *namespace != '-' && *namespace != ' '
		        && *namespace != '(' && *namespace != ')'
		        && (*namespace < 'a' || *namespace > 'z') && (*namespace < 'A' || *namespace > 'Z')
		        && (*namespace < '0' || *namespace > '9'))
			return 0;
		++namespace;
	}
	if (*(namespace - 1) == ' ')
		return 0; // Invalid: Ends in a space
	return 1;
}

void strtolower(char *string)
{
	while (*string)
	{
		if (*string >= 'A' && *string <= 'Z')
			*string += 32;
		++string;
	}
}

void remove_trailing_slash(char *string)
{
	char *ptr = string + strlen(string) - 1;
	while (ptr >= string && *ptr == '/')
		*ptr-- = '\0';
}