summaryrefslogtreecommitdiffstats
path: root/src/server/protocol.h
blob: c0eda5b79ed17eff6c341fddb1dda58401191f42 (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
#ifndef _PROTOCOL_H_
#define _PROTOCOL_H_

#include "../types.h"
#include "../serialize.h"

#define FLAGS8_SERVER (1)

#define REPLY_OK (0)
#define REPLY_ERRNO (-1)
#define REPLY_AGAIN (-2)
#define REPLY_INTR (-3)
#define REPLY_CLOSED (-4)
#define REPLY_INCOMPLETE (-5)
#define REPLY_WRONGMAGIC (-6)

static inline int dnbd3_read_reply(int sock, dnbd3_reply_t *reply, bool wait)
{
	int ret = recv( sock, reply, sizeof(*reply), (wait ? MSG_WAITALL : MSG_DONTWAIT) | MSG_NOSIGNAL );
	if ( ret == 0 ) return REPLY_CLOSED;
	if ( ret < 0 ) {
		if ( ret == EAGAIN || ret == EWOULDBLOCK ) return REPLY_AGAIN;
		if ( ret == EINTR ) return REPLY_INTR;
		return REPLY_ERRNO;
	}
	if ( !wait && ret != sizeof(*reply) ) ret += recv( sock, reply + ret, sizeof(*reply) - ret, MSG_WAITALL | MSG_NOSIGNAL );
	if ( ret != sizeof(*reply) ) return REPLY_INCOMPLETE;
	fixup_reply( *reply );
	if ( reply->magic != dnbd3_packet_magic ) return REPLY_WRONGMAGIC;
	return REPLY_OK;
}

static inline bool dnbd3_get_reply(int sock, dnbd3_reply_t *reply)
{
	return dnbd3_read_reply( sock, reply, true ) == REPLY_OK;
}

static inline bool dnbd3_select_image(int sock, char *lower_name, uint16_t rid, uint8_t flags8)
{
	serialized_buffer_t serialized;
	dnbd3_request_t request;
	struct iovec iov[2];
	serializer_reset_write( &serialized );
	serializer_put_uint16( &serialized, PROTOCOL_VERSION );
	serializer_put_string( &serialized, lower_name );
	serializer_put_uint16( &serialized, rid );
	serializer_put_uint8( &serialized, flags8 );
	const ssize_t len = serializer_get_written_length( &serialized );
	request.magic = dnbd3_packet_magic;
	request.cmd = CMD_SELECT_IMAGE;
	request.size = len;
#ifdef _DEBUG
	request.handle = 0;
	request.offset = 0;
#endif
	fixup_request( request );
	iov[0].iov_base = &request;
	iov[0].iov_len = sizeof(request);
	iov[1].iov_base = &serialized;
	iov[1].iov_len = len;
	return writev( sock, iov, 2 ) == len + sizeof(request);
}

static inline bool dnbd3_get_block(int sock, uint64_t offset, uint32_t size, uint64_t handle)
{
	dnbd3_request_t request;
	request.magic = dnbd3_packet_magic;
	request.handle = handle;
	request.cmd = CMD_GET_BLOCK;
	request.offset = offset;
	request.size = size;
	fixup_request( request );
	return send( sock, &request, sizeof(request), MSG_NOSIGNAL ) == sizeof(request);
}

static inline bool dnbd3_get_crc32(int sock, uint32_t *master, void *buffer, size_t *bufferLen)
{
	dnbd3_request_t request;
	dnbd3_reply_t reply;
	request.magic = dnbd3_packet_magic;
	request.handle = 0;
	request.cmd = CMD_GET_CRC32;
	request.offset = 0;
	request.size = 0;
	fixup_request( request );
	if ( send( sock, &request, sizeof(request), 0 ) != sizeof(request) ) return false;
	if ( !dnbd3_get_reply( sock, &reply ) ) return false;
	if ( reply.size == 0 ) {
		*bufferLen = 0;
		return true;
	}
	if ( reply.size < 4 ) return false;
	reply.size -= 4;
	if ( reply.cmd != CMD_GET_CRC32 || reply.size > *bufferLen ) return false;
	*bufferLen = reply.size;
	if ( recv( sock, master, sizeof(uint32_t), MSG_WAITALL | MSG_NOSIGNAL ) != sizeof(uint32_t) ) return false;
	int done = 0;
	while ( done < reply.size ) {
		const int ret = recv( sock, (char*)buffer + done, reply.size - done, 0 );
		if ( ret <= 0 ) return false;
		done += ret;
	}
	return true;
}

/**
 * Pass a full serialized_buffer_t and a socket fd. Parsed data will be returned in further arguments.
 * Note that all strings will point into the passed buffer, so there's no need to free them.
 * This function will also read the header for you, as this message can only occur during connection,
 * where no unrequested messages could arrive inbetween.
 */
static inline bool dnbd3_select_image_reply(serialized_buffer_t *buffer, int sock, uint16_t *protocol_version, char **name, uint16_t *rid,
        uint64_t *imageSize)
{
	dnbd3_reply_t reply;
	if ( !dnbd3_get_reply( sock, &reply ) ) {
		return false;
	}
	if ( reply.cmd != CMD_SELECT_IMAGE || reply.size < 3 || reply.size > MAX_PAYLOAD ) {
		return false;
	}
// receive reply payload
	if ( recv( sock, buffer, reply.size, MSG_WAITALL | MSG_NOSIGNAL ) != reply.size ) {
		return false;
	}
// handle/check reply payload
	serializer_reset_read( buffer, reply.size );
	*protocol_version = serializer_get_uint16( buffer );
	*name = serializer_get_string( buffer );
	*rid = serializer_get_uint16( buffer );
	*imageSize = serializer_get_uint64( buffer );
	return true;
}

#endif