summaryrefslogtreecommitdiffstats
path: root/src/server/sockhelper.c
blob: 83bea2eb7e0c9c37563db08e3ce439f90c210bfb (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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#include "sockhelper.h"
#include "memlog.h"
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

static inline int connect_shared(const int client_sock, void* addr, const int addrlen, int connect_ms, int rw_ms)
{
	struct timeval tv;
	// Connect to server
	tv.tv_sec = connect_ms / 1000;
	tv.tv_usec = connect_ms * 1000;
	setsockopt( client_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv) );
	setsockopt( client_sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv) );
	if ( connect( client_sock, (struct sockaddr *)addr, addrlen ) == -1 ) {
		close( client_sock );
		return -1;
	}
	// Apply read/write timeout
	tv.tv_sec = rw_ms / 1000;
	tv.tv_usec = rw_ms * 1000;
	setsockopt( client_sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv) );
	setsockopt( client_sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv) );
	return client_sock;
}

int sock_connect4(struct sockaddr_in *addr, const int connect_ms, const int rw_ms)
{
	int client_sock = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP );
	if ( client_sock == -1 ) return -1;
	return connect_shared( client_sock, addr, sizeof(struct sockaddr_in), connect_ms, rw_ms );
}

int sock_connect6(struct sockaddr_in6 *addr, const int connect_ms, const int rw_ms)
{
#ifdef WITH_IPV6
	int client_sock = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
	if (client_sock == -1) return -1;
	return connect_shared(client_sock, addr, sizeof(struct sockaddr_in6), connect_ms, rw_ms);
#else
	printf( "[DEBUG] Not compiled with IPv6 support.\n" );
	return -1;
#endif
}

int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const int rw_ms)
{
	if ( addr->type == AF_INET ) {
		// Set host (IPv4)
		struct sockaddr_in addr4;
		memset( &addr4, 0, sizeof(addr4) );
		addr4.sin_family = AF_INET;
		memcpy( &addr4.sin_addr, addr->addr, 4 );
		addr4.sin_port = addr->port;
		return sock_connect4( &addr4, connect_ms, rw_ms );
	}
#ifdef WITH_IPV6
	else if (addr->type == AF_INET6)
	{
		// Set host (IPv6)
		struct sockaddr_in6 addr6;
		memset(&addr6, 0, sizeof(addr6));
		addr6.sin6_family = AF_INET6;
		memcpy(&addr6.sin6_addr, addr->addr, 16);
		addr6.sin6_port = addr->port;
		return sock_connect6(&addr6, connect_ms, rw_ms);
	}
#endif
	printf( "[DEBUG] Unsupported address type: %d\n", (int)addr->type );
	return -1;
}

void sock_set_timeout(const int sockfd, const int milliseconds)
{
	struct timeval tv;
	tv.tv_sec = milliseconds / 1000;
	tv.tv_usec = (milliseconds * 1000) % 1000000;
	setsockopt( sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv) );
	setsockopt( sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv) );
}

int sock_listen_any(int protocol_family, uint16_t port, char* bind_addr)
{
	struct sockaddr_storage addr;
	struct in_addr local;
	if (bind_addr != NULL) {
		if (!inet_aton(bind_addr, &local)) return -1;
	}
	memset( &addr, 0, sizeof(addr) );
	if ( protocol_family == PF_INET ) {
		struct sockaddr_in *v4 = (struct sockaddr_in *)&addr;
		if (bind_addr == NULL) {
			v4->sin_addr.s_addr = INADDR_ANY;
		} else {
			v4->sin_addr = local;
		}
		v4->sin_port = htons( port );
		v4->sin_family = AF_INET;
	}
#ifdef WITH_IPV6
	else if (protocol_family == PF_INET6)
	{
		struct sockaddr_in6 *v6 = (struct sockaddr_in6 *)&addr;
		v6->sin6_addr = in6addr_any;
		v6->sin6_port = htons(port);
		v6->sin6_family = AF_INET6;
	}
#endif
	else {
		printf( "[DEBUG] sock_listen: Unsupported protocol: %d\n", protocol_family );
		return -1;
	}
	return sock_listen( &addr, sizeof(addr) );
}

int sock_listen(struct sockaddr_storage *addr, int addrlen)
{
	int pf; // On Linux AF_* == PF_*, but this is not guaranteed on all platforms, so let's be safe here:
	if ( addr->ss_family == AF_INET ) pf = PF_INET;
#ifdef WITH_IPV6
	else if (addr->ss_family == AF_INET6)
	pf = PF_INET6;
#endif
	else {
		printf( "[DEBUG] sock_listen: unsupported address type: %d\n", (int)addr->ss_family );
		return -1;
	}
	int sock;

	// Create socket
	sock = socket( pf, SOCK_STREAM, IPPROTO_TCP );
	if ( sock < 0 ) {
		memlogf( "[ERROR] sock_listen: Socket setup failure" ); // TODO: print port number to help troubleshooting
		return -1;
	}
	const int on = 1;
	setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );
	if ( pf == PF_INET6 ) setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on) );

	// Bind to socket
	if ( bind( sock, (struct sockaddr *)addr, addrlen ) < 0 ) {
		int e = errno;
		close( sock );
		memlogf( "[ERROR] Bind failure (%d)", e ); // TODO: print port number to help troubleshooting
		return -1;
	}

	// Listen on socket
	if ( listen( sock, 20 ) == -1 ) {
		close( sock );
		memlogf( "[ERROR] Listen failure" ); // TODO ...
		return -1;
	}

	return sock;
}

int accept_any(const int * const sockets, const int socket_count, struct sockaddr_storage *addr, socklen_t *length_ptr)
{
	fd_set set;
	FD_ZERO( &set );
	int max = 0;
	for (int i = 0; i < socket_count; ++i) {
		FD_SET( sockets[i], &set );
		if ( sockets[i] > max ) max = sockets[i];
	}
	if ( select( max + 1, &set, NULL, NULL, NULL ) <= 0 ) return -1;
	for (int i = 0; i < socket_count; ++i) {
		if ( FD_ISSET(sockets[i], &set)) return accept( sockets[i], (struct sockaddr *)addr, length_ptr );
	}
	return -1;
}

void sock_set_nonblock(int sock)
{
	int flags = fcntl( sock, F_GETFL, 0 );
	if ( flags == -1 ) flags = 0;
	fcntl( sock, F_SETFL, flags | O_NONBLOCK );
}

void sock_set_block(int sock)
{
	int flags = fcntl( sock, F_GETFL, 0 );
	if ( flags == -1 ) flags = 0;
	fcntl( sock, F_SETFL, flags & ~(int)O_NONBLOCK );
}

int sock_add_array(const int sock, int *array, int *array_fill, const int array_length)
{
	if ( sock == -1 ) return TRUE;
	for (int i = 0; i < *array_fill; ++i) {
		if ( array[i] == -1 ) {
			array[i] = sock;
			return TRUE;
		}
	}
	if ( *array_fill >= array_length ) return FALSE;
	array[*array_fill] = sock;
	(*array_fill) += 1;
	return TRUE;
}