summaryrefslogtreecommitdiffstats
path: root/src/server/helper.c
blob: 2dbc3ea67481cf34b855ea8bbee3d994cee74518 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include "helper.h"
#include <arpa/inet.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>

#ifdef HAVE_THREAD_NAMES
#include <sys/prctl.h> // For thread names
#endif

/**
 * Parse IPv4 or IPv6 address in string representation to a suitable format usable by the BSD socket library
 * !! Contents of 'string' might be modified by this function !!
 *
 * @param 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"
 * @param host pointer to dnbd3_host_t that will be filled with the following data:
 *    .type will contain either HOST_IP4 or HOST_IP6
 *    .addr will contain the address in network representation
 *    .port will contain the port in network representation, defaulting to #define PORT if none was given
 * @return true on success, false in failure. contents of af, addr and port are undefined in the latter case
 */
bool parse_address(char *string, dnbd3_host_t *host)
{
	struct in_addr v4;
	struct in6_addr v6;

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

	// Scan for port
	char *portpos = NULL, *ptr = string;
	while ( *ptr ) {
		if ( *ptr == ':' ) portpos = ptr;
		++ptr;
	}
	if ( portpos == NULL ) return false; // 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 false; // Invalid port
	host->port = htons( (uint16_t)p );

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

	// FAIL
	return false;
}

/**
 * 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
 */
bool 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 == HOST_IP6 ) {
		*target++ = '[';
		inet_ntop( AF_INET6, host->addr, target, (socklen_t)targetlen - 10 );
		target += strlen( target );
		*target++ = ']';
	} else if ( host->type == HOST_IP4 ) {
		inet_ntop( AF_INET, host->addr, target, (socklen_t)targetlen - 8 );
		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;
}

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

void trim_right(char * const string)
{
	char *end = string + strlen( string ) - 1;
	while ( end >= string && (*end == '\r' || *end == '\n' || *end == ' ' || *end == '\t') )
		*end-- = '\0';
}

void setThreadName(const char *name)
{
	char newName[16];
	if ( strlen( name ) > 15 ) {
		snprintf( newName, sizeof(newName), "%s", name );
		newName[15] = '\0';
		name = newName;
	}
#ifdef HAVE_THREAD_NAMES
	prctl( PR_SET_NAME, (unsigned long)name, 0, 0, 0 );
#endif
	//TODO: On FreeBSD set threadname with pthread_setname_np
}

void blockNoncriticalSignals()
{
	sigset_t sigmask;
	sigemptyset( &sigmask );
	sigaddset( &sigmask, SIGUSR1 );
	sigaddset( &sigmask, SIGUSR2 );
	sigaddset( &sigmask, SIGHUP );
	sigaddset( &sigmask, SIGPIPE );
	pthread_sigmask( SIG_BLOCK, &sigmask, NULL );
}