Review Board 1.7.16


Websocket: Add locking around session access and modification

Review Request #3481 - Created April 25, 2014 and submitted

opticron
branches/11
ASTERISK-23605
Reviewers
asterisk-dev
Asterisk
This resolves a race condition where data could be written to a NULL FILE pointer causing a crash as a websocket connection was in the process of shutting down by adding locking to accesses and modifications of the websocket session struct.

 

Changes between revision 1 and 2

1 2 3 4
1 2 3 4

  1. branches/11/res/res_http_websocket.c: Loading...
branches/11/res/res_http_websocket.c
Diff Revision 1 Diff Revision 2
1
/*
1
/*
2
 * Asterisk -- An open source telephony toolkit.
2
 * Asterisk -- An open source telephony toolkit.
3
 *
3
 *
4
 * Copyright (C) 2012, Digium, Inc.
4
 * Copyright (C) 2012, Digium, Inc.
5
 *
5
 *
6
 * Joshua Colp <jcolp@digium.com>
6
 * Joshua Colp <jcolp@digium.com>
7
 *
7
 *
8
 * See http://www.asterisk.org for more information about
8
 * See http://www.asterisk.org for more information about
9
 * the Asterisk project. Please do not directly contact
9
 * the Asterisk project. Please do not directly contact
10
 * any of the maintainers of this project for assistance;
10
 * any of the maintainers of this project for assistance;
11
 * the project provides a web site, mailing lists and IRC
11
 * the project provides a web site, mailing lists and IRC
12
 * channels for your use.
12
 * channels for your use.
13
 *
13
 *
14
 * This program is free software, distributed under the terms of
14
 * This program is free software, distributed under the terms of
15
 * the GNU General Public License Version 2. See the LICENSE file
15
 * the GNU General Public License Version 2. See the LICENSE file
16
 * at the top of the source tree.
16
 * at the top of the source tree.
17
 */
17
 */
18

    
   
18

   
19
/*! \file
19
/*! \file
20
 *
20
 *
21
 * \brief WebSocket support for the Asterisk internal HTTP server
21
 * \brief WebSocket support for the Asterisk internal HTTP server
22
 *
22
 *
23
 * \author Joshua Colp <jcolp@digium.com>
23
 * \author Joshua Colp <jcolp@digium.com>
24
 */
24
 */
25

    
   
25

   
26
/*** MODULEINFO
26
/*** MODULEINFO
27
	<support_level>extended</support_level>
27
	<support_level>extended</support_level>
28
 ***/
28
 ***/
29

    
   
29

   
30
#include "asterisk.h"
30
#include "asterisk.h"
31

    
   
31

   
32
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
32
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
33

    
   
33

   
34
#include "asterisk/module.h"
34
#include "asterisk/module.h"
35
#include "asterisk/http.h"
35
#include "asterisk/http.h"
36
#include "asterisk/astobj2.h"
36
#include "asterisk/astobj2.h"
37
#include "asterisk/strings.h"
37
#include "asterisk/strings.h"
38
#include "asterisk/file.h"
38
#include "asterisk/file.h"
39
#include "asterisk/unaligned.h"
39
#include "asterisk/unaligned.h"
40

    
   
40

   
41
#define AST_API_MODULE
41
#define AST_API_MODULE
42
#include "asterisk/http_websocket.h"
42
#include "asterisk/http_websocket.h"
43

    
   
43

   
44
/*! \brief GUID used to compute the accept key, defined in the specifications */
44
/*! \brief GUID used to compute the accept key, defined in the specifications */
45
#define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
45
#define WEBSOCKET_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
46

    
   
46

   
47
/*! \brief Number of buckets for registered protocols */
47
/*! \brief Number of buckets for registered protocols */
48
#define MAX_PROTOCOL_BUCKETS 7
48
#define MAX_PROTOCOL_BUCKETS 7
49

    
   
49

   
50
/*! \brief Size of the pre-determined buffer for WebSocket frames */
50
/*! \brief Size of the pre-determined buffer for WebSocket frames */
51
#define MAXIMUM_FRAME_SIZE 8192
51
#define MAXIMUM_FRAME_SIZE 8192
52

    
   
52

   
53
/*! \brief Default reconstruction size for multi-frame payload reconstruction. If exceeded the next frame will start a
53
/*! \brief Default reconstruction size for multi-frame payload reconstruction. If exceeded the next frame will start a
54
 *         payload.
54
 *         payload.
55
 */
55
 */
56
#define DEFAULT_RECONSTRUCTION_CEILING 16384
56
#define DEFAULT_RECONSTRUCTION_CEILING 16384
57

    
   
57

   
58
/*! \brief Maximum reconstruction size for multi-frame payload reconstruction. */
58
/*! \brief Maximum reconstruction size for multi-frame payload reconstruction. */
59
#define MAXIMUM_RECONSTRUCTION_CEILING 16384
59
#define MAXIMUM_RECONSTRUCTION_CEILING 16384
60

    
   
60

   
61
/*! \brief Maximum size of a websocket frame header
61
/*! \brief Maximum size of a websocket frame header
62
 * 1 byte flags and opcode
62
 * 1 byte flags and opcode
63
 * 1 byte mask flag + payload len
63
 * 1 byte mask flag + payload len
64
 * 8 bytes max extended length
64
 * 8 bytes max extended length
65
 * 4 bytes optional masking key
65
 * 4 bytes optional masking key
66
 * ... payload follows ...
66
 * ... payload follows ...
67
 * */
67
 * */
68
#define MAX_WS_HDR_SZ 14
68
#define MAX_WS_HDR_SZ 14
69
#define MIN_WS_HDR_SZ 2
69
#define MIN_WS_HDR_SZ 2
70

    
   
70

   
71
/*! \brief Structure definition for session */
71
/*! \brief Structure definition for session */
72
struct ast_websocket {
72
struct ast_websocket {
73
	FILE *f;                          /*!< Pointer to the file instance used for writing and reading */
73
	FILE *f;                          /*!< Pointer to the file instance used for writing and reading */
74
	int fd;                           /*!< File descriptor for the session, only used for polling */
74
	int fd;                           /*!< File descriptor for the session, only used for polling */
75
	struct ast_sockaddr address;      /*!< Address of the remote client */
75
	struct ast_sockaddr address;      /*!< Address of the remote client */
76
	enum ast_websocket_opcode opcode; /*!< Cached opcode for multi-frame messages */
76
	enum ast_websocket_opcode opcode; /*!< Cached opcode for multi-frame messages */
77
	size_t payload_len;               /*!< Length of the payload */
77
	size_t payload_len;               /*!< Length of the payload */
78
	char *payload;                    /*!< Pointer to the payload */
78
	char *payload;                    /*!< Pointer to the payload */
79
	size_t reconstruct;               /*!< Number of bytes before a reconstructed payload will be returned and a new one started */
79
	size_t reconstruct;               /*!< Number of bytes before a reconstructed payload will be returned and a new one started */
80
	unsigned int secure:1;            /*!< Bit to indicate that the transport is secure */
80
	unsigned int secure:1;            /*!< Bit to indicate that the transport is secure */
81
	unsigned int closing:1;           /*!< Bit to indicate that the session is in the process of being closed */
81
	unsigned int closing:1;           /*!< Bit to indicate that the session is in the process of being closed */
82
};
82
};
83

    
   
83

   
84
/*! \brief Structure definition for protocols */
84
/*! \brief Structure definition for protocols */
85
struct websocket_protocol {
85
struct websocket_protocol {
86
	char *name;                      /*!< Name of the protocol */
86
	char *name;                      /*!< Name of the protocol */
87
	ast_websocket_callback callback; /*!< Callback called when a new session is established */
87
	ast_websocket_callback callback; /*!< Callback called when a new session is established */
88
};
88
};
89

    
   
89

   
90
/*! \brief Container for registered protocols */
90
/*! \brief Container for registered protocols */
91
static struct ao2_container *protocols;
91
static struct ao2_container *protocols;
92

    
   
92

   
93
/*! \brief Hashing function for protocols */
93
/*! \brief Hashing function for protocols */
94
static int protocol_hash_fn(const void *obj, const int flags)
94
static int protocol_hash_fn(const void *obj, const int flags)
95
{
95
{
96
	const struct websocket_protocol *protocol = obj;
96
	const struct websocket_protocol *protocol = obj;
97
	const char *name = obj;
97
	const char *name = obj;
98

    
   
98

   
99
	return ast_str_case_hash(flags & OBJ_KEY ? name : protocol->name);
99
	return ast_str_case_hash(flags & OBJ_KEY ? name : protocol->name);
100
}
100
}
101

    
   
101

   
102
/*! \brief Comparison function for protocols */
102
/*! \brief Comparison function for protocols */
103
static int protocol_cmp_fn(void *obj, void *arg, int flags)
103
static int protocol_cmp_fn(void *obj, void *arg, int flags)
104
{
104
{
105
	const struct websocket_protocol *protocol1 = obj, *protocol2 = arg;
105
	const struct websocket_protocol *protocol1 = obj, *protocol2 = arg;
106
	const char *protocol = arg;
106
	const char *protocol = arg;
107

    
   
107

   
108
	return !strcasecmp(protocol1->name, flags & OBJ_KEY ? protocol : protocol2->name) ? CMP_MATCH | CMP_STOP : 0;
108
	return !strcasecmp(protocol1->name, flags & OBJ_KEY ? protocol : protocol2->name) ? CMP_MATCH | CMP_STOP : 0;
109
}
109
}
110

    
   
110

   
111
/*! \brief Destructor function for protocols */
111
/*! \brief Destructor function for protocols */
112
static void protocol_destroy_fn(void *obj)
112
static void protocol_destroy_fn(void *obj)
113
{
113
{
114
	struct websocket_protocol *protocol = obj;
114
	struct websocket_protocol *protocol = obj;
115
	ast_free(protocol->name);
115
	ast_free(protocol->name);
116
}
116
}
117

    
   
117

   
118
/*! \brief Destructor function for sessions */
118
/*! \brief Destructor function for sessions */
119
static void session_destroy_fn(void *obj)
119
static void session_destroy_fn(void *obj)
120
{
120
{
121
	struct ast_websocket *session = obj;
121
	struct ast_websocket *session = obj;
122

    
   
122

   

    
   
123
	ast_websocket_close(session, 0);

    
   
124

   
123
	if (session->f) {
125
	if (session->f) {
124
		fclose(session->f);
126
		fclose(session->f);
125
		ast_verb(2, "WebSocket connection from '%s' closed\n", ast_sockaddr_stringify(&session->address));
127
		ast_verb(2, "WebSocket connection from '%s' closed\n", ast_sockaddr_stringify(&session->address));
126
	}
128
	}
127

    
   
129

   
128
	ast_free(session->payload);
130
	ast_free(session->payload);
129
}
131
}
130

    
   
132

   
131
int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol)(const char *name, ast_websocket_callback callback)
133
int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol)(const char *name, ast_websocket_callback callback)
132
{
134
{
133
	struct websocket_protocol *protocol;
135
	struct websocket_protocol *protocol;
134

    
   
136

   
135
	ao2_lock(protocols);
137
	ao2_lock(protocols);
136

    
   
138

   
137
	/* Ensure a second protocol handler is not registered for the same protocol */
139
	/* Ensure a second protocol handler is not registered for the same protocol */
138
	if ((protocol = ao2_find(protocols, name, OBJ_KEY | OBJ_NOLOCK))) {
140
	if ((protocol = ao2_find(protocols, name, OBJ_KEY | OBJ_NOLOCK))) {
139
		ao2_ref(protocol, -1);
141
		ao2_ref(protocol, -1);
140
		ao2_unlock(protocols);
142
		ao2_unlock(protocols);
141
		return -1;
143
		return -1;
142
	}
144
	}
143

    
   
145

   
144
	if (!(protocol = ao2_alloc(sizeof(*protocol), protocol_destroy_fn))) {
146
	if (!(protocol = ao2_alloc(sizeof(*protocol), protocol_destroy_fn))) {
145
		ao2_unlock(protocols);
147
		ao2_unlock(protocols);
146
		return -1;
148
		return -1;
147
	}
149
	}
148

    
   
150

   
149
	if (!(protocol->name = ast_strdup(name))) {
151
	if (!(protocol->name = ast_strdup(name))) {
150
		ao2_ref(protocol, -1);
152
		ao2_ref(protocol, -1);
151
		ao2_unlock(protocols);
153
		ao2_unlock(protocols);
152
		return -1;
154
		return -1;
153
	}
155
	}
154

    
   
156

   
155
	protocol->callback = callback;
157
	protocol->callback = callback;
156

    
   
158

   
157
	ao2_link_flags(protocols, protocol, OBJ_NOLOCK);
159
	ao2_link_flags(protocols, protocol, OBJ_NOLOCK);
158
	ao2_unlock(protocols);
160
	ao2_unlock(protocols);
159
	ao2_ref(protocol, -1);
161
	ao2_ref(protocol, -1);
160

    
   
162

   
161
	ast_verb(2, "WebSocket registered sub-protocol '%s'\n", name);
163
	ast_verb(2, "WebSocket registered sub-protocol '%s'\n", name);
162

    
   
164

   
163
	return 0;
165
	return 0;
164
}
166
}
165

    
   
167

   
166
int AST_OPTIONAL_API_NAME(ast_websocket_remove_protocol)(const char *name, ast_websocket_callback callback)
168
int AST_OPTIONAL_API_NAME(ast_websocket_remove_protocol)(const char *name, ast_websocket_callback callback)
167
{
169
{
168
	struct websocket_protocol *protocol;
170
	struct websocket_protocol *protocol;
169

    
   
171

   
170
	if (!(protocol = ao2_find(protocols, name, OBJ_KEY))) {
172
	if (!(protocol = ao2_find(protocols, name, OBJ_KEY))) {
171
		return -1;
173
		return -1;
172
	}
174
	}
173

    
   
175

   
174
	if (protocol->callback != callback) {
176
	if (protocol->callback != callback) {
175
		ao2_ref(protocol, -1);
177
		ao2_ref(protocol, -1);
176
		return -1;
178
		return -1;
177
	}
179
	}
178

    
   
180

   
179
	ao2_unlink(protocols, protocol);
181
	ao2_unlink(protocols, protocol);
180
	ao2_ref(protocol, -1);
182
	ao2_ref(protocol, -1);
181

    
   
183

   
182
	ast_verb(2, "WebSocket unregistered sub-protocol '%s'\n", name);
184
	ast_verb(2, "WebSocket unregistered sub-protocol '%s'\n", name);
183

    
   
185

   
184
	return 0;
186
	return 0;
185
}
187
}
186

    
   
188

   
187
/*! \brief Close function for websocket session */
189
/*! \brief Close function for websocket session */
188
int AST_OPTIONAL_API_NAME(ast_websocket_close)(struct ast_websocket *session, uint16_t reason)
190
int AST_OPTIONAL_API_NAME(ast_websocket_close)(struct ast_websocket *session, uint16_t reason)
189
{
191
{
190
	char frame[4] = { 0, }; /* The header is 2 bytes and the reason code takes up another 2 bytes */
192
	char frame[4] = { 0, }; /* The header is 2 bytes and the reason code takes up another 2 bytes */
191
	int res;
193
	int res;
192

    
   
194

   

    
   
195
	if (session->close_sent) {

    
   
196
		return 0;

    
   
197
	}

    
   
198

   
193
	frame[0] = AST_WEBSOCKET_OPCODE_CLOSE | 0x80;
199
	frame[0] = AST_WEBSOCKET_OPCODE_CLOSE | 0x80;
194
	frame[1] = 2; /* The reason code is always 2 bytes */
200
	frame[1] = 2; /* The reason code is always 2 bytes */
195

    
   
201

   
196
	/* If no reason has been specified assume 1000 which is normal closure */
202
	/* If no reason has been specified assume 1000 which is normal closure */
197
	put_unaligned_uint16(&frame[2], htons(reason ? reason : 1000));
203
	put_unaligned_uint16(&frame[2], htons(reason ? reason : 1000));
198

    
   
204

   
199
	ao2_lock(session);

   
200
	session->closing = 1;
205
	session->closing = 1;

    
   
206
	session->close_sent = 1;
201

    
   
207

   

    
   
208
	ao2_lock(session);
202
	res = (fwrite(frame, 1, 4, session->f) == 4) ? 0 : -1;
209
	res = (fwrite(frame, 1, 4, session->f) == 4) ? 0 : -1;
203
	ao2_unlock(session);
210
	ao2_unlock(session);
204
	return res;
211
	return res;
205
}
212
}
206

    
   
213

   
207

    
   
214

   
208
/*! \brief Write function for websocket traffic */
215
/*! \brief Write function for websocket traffic */
209
int AST_OPTIONAL_API_NAME(ast_websocket_write)(struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t actual_length)
216
int AST_OPTIONAL_API_NAME(ast_websocket_write)(struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t actual_length)
210
{
217
{
211
	size_t header_size = 2; /* The minimum size of a websocket frame is 2 bytes */
218
	size_t header_size = 2; /* The minimum size of a websocket frame is 2 bytes */
212
	char *frame;
219
	char *frame;
213
	uint64_t length = 0;
220
	uint64_t length = 0;
214

    
   
221

   
215
	if (actual_length < 126) {
222
	if (actual_length < 126) {
216
		length = actual_length;
223
		length = actual_length;
217
	} else if (actual_length < (1 << 16)) {
224
	} else if (actual_length < (1 << 16)) {
218
		length = 126;
225
		length = 126;
219
		/* We need an additional 2 bytes to store the extended length */
226
		/* We need an additional 2 bytes to store the extended length */
220
		header_size += 2;
227
		header_size += 2;
221
	} else {
228
	} else {
222
		length = 127;
229
		length = 127;
223
		/* We need an additional 8 bytes to store the really really extended length */
230
		/* We need an additional 8 bytes to store the really really extended length */
224
		header_size += 8;
231
		header_size += 8;
225
	}
232
	}
226

    
   
233

   
227
	frame = ast_alloca(header_size);
234
	frame = ast_alloca(header_size);
228
	memset(frame, 0, sizeof(*frame));
235
	memset(frame, 0, sizeof(*frame));
229

    
   
236

   
230
	frame[0] = opcode | 0x80;
237
	frame[0] = opcode | 0x80;
231
	frame[1] = length;
238
	frame[1] = length;
232

    
   
239

   
233
	/* Use the additional available bytes to store the length */
240
	/* Use the additional available bytes to store the length */
234
	if (length == 126) {
241
	if (length == 126) {
235
		put_unaligned_uint16(&frame[2], htons(actual_length));
242
		put_unaligned_uint16(&frame[2], htons(actual_length));
236
	} else if (length == 127) {
243
	} else if (length == 127) {
237
		put_unaligned_uint64(&frame[2], htonl(actual_length));
244
		put_unaligned_uint64(&frame[2], htonl(actual_length));
238
	}
245
	}
239

    
   
246

   
240
	ao2_lock(session);
247
	ao2_lock(session);

    
   
248
	if (session->closing) {

    
   
249
		ao2_unlock(session);

    
   
250
		return -1;

    
   
251
	}

    
   
252

   
241
	if (fwrite(frame, 1, header_size, session->f) != header_size) {
253
	if (fwrite(frame, 1, header_size, session->f) != header_size) {
242
		ao2_unlock(session);
254
		ao2_unlock(session);
243
		return -1;
255
		return -1;
244
	}
256
	}
245

    
   
257

   
246
	if (fwrite(payload, 1, actual_length, session->f) != actual_length) {
258
	if (fwrite(payload, 1, actual_length, session->f) != actual_length) {
247
		ao2_unlock(session);
259
		ao2_unlock(session);
248
		return -1;
260
		return -1;
249
	}
261
	}
250
	fflush(session->f);
262
	fflush(session->f);
251
	ao2_unlock(session);
263
	ao2_unlock(session);
252

    
   
264

   
253
	return 0;
265
	return 0;
254
}
266
}
255

    
   
267

   
256
void AST_OPTIONAL_API_NAME(ast_websocket_reconstruct_enable)(struct ast_websocket *session, size_t bytes)
268
void AST_OPTIONAL_API_NAME(ast_websocket_reconstruct_enable)(struct ast_websocket *session, size_t bytes)
257
{
269
{
258
	ao2_lock(session);

   
259
	session->reconstruct = MIN(bytes, MAXIMUM_RECONSTRUCTION_CEILING);
270
	session->reconstruct = MIN(bytes, MAXIMUM_RECONSTRUCTION_CEILING);
260
	ao2_unlock(session);

   
261
}
271
}
262

    
   
272

   
263
void AST_OPTIONAL_API_NAME(ast_websocket_reconstruct_disable)(struct ast_websocket *session)
273
void AST_OPTIONAL_API_NAME(ast_websocket_reconstruct_disable)(struct ast_websocket *session)
264
{
274
{
265
	ao2_lock(session);

   
266
	session->reconstruct = 0;
275
	session->reconstruct = 0;
267
	ao2_unlock(session);

   
268
}
276
}
269

    
   
277

   
270
void AST_OPTIONAL_API_NAME(ast_websocket_ref)(struct ast_websocket *session)
278
void AST_OPTIONAL_API_NAME(ast_websocket_ref)(struct ast_websocket *session)
271
{
279
{
272
	ao2_ref(session, +1);
280
	ao2_ref(session, +1);
273
}
281
}
274

    
   
282

   
275
void AST_OPTIONAL_API_NAME(ast_websocket_unref)(struct ast_websocket *session)
283
void AST_OPTIONAL_API_NAME(ast_websocket_unref)(struct ast_websocket *session)
276
{
284
{
277
	ao2_ref(session, -1);
285
	ao2_ref(session, -1);
278
}
286
}
279

    
   
287

   
280
int AST_OPTIONAL_API_NAME(ast_websocket_fd)(struct ast_websocket *session)
288
int AST_OPTIONAL_API_NAME(ast_websocket_fd)(struct ast_websocket *session)
281
{
289
{
282
	int res;
290
	return session->closing ? -1 : session->fd;
283
	ao2_lock(session);

   
284
	res = session->closing ? -1 : session->fd;

   
285
	ao2_unlock(session);

   
286
	return res;

   
287
}
291
}
288

    
   
292

   
289
struct ast_sockaddr * AST_OPTIONAL_API_NAME(ast_websocket_remote_address)(struct ast_websocket *session)
293
struct ast_sockaddr * AST_OPTIONAL_API_NAME(ast_websocket_remote_address)(struct ast_websocket *session)
290
{
294
{
291
	struct ast_sockaddr *res;
295
	return &session->address;
292
	ao2_lock(session);

   
293
	res = &session->address;

   
294
	ao2_unlock(session);

   
295
	return res;

   
296
}
296
}
297

    
   
297

   
298
int AST_OPTIONAL_API_NAME(ast_websocket_is_secure)(struct ast_websocket *session)
298
int AST_OPTIONAL_API_NAME(ast_websocket_is_secure)(struct ast_websocket *session)
299
{
299
{
300
	int res;
300
	return session->secure;
301
	ao2_lock(session);

   
302
	res = session->secure;

   
303
	ao2_unlock(session);

   
304
	return res;

   
305
}
301
}
306

    
   
302

   
307
int AST_OPTIONAL_API_NAME(ast_websocket_set_nonblock)(struct ast_websocket *session)
303
int AST_OPTIONAL_API_NAME(ast_websocket_set_nonblock)(struct ast_websocket *session)
308
{
304
{
309
	int flags;
305
	int flags;
310

    
   
306

   
311
	ao2_lock(session);

   
312
	if ((flags = fcntl(session->fd, F_GETFL)) == -1) {
307
	if ((flags = fcntl(session->fd, F_GETFL)) == -1) {
313
		ao2_unlock(session);

   
314
		return -1;
308
		return -1;
315
	}
309
	}
316

    
   
310

   
317
	flags |= O_NONBLOCK;
311
	flags |= O_NONBLOCK;
318

    
   
312

   
319
	if ((flags = fcntl(session->fd, F_SETFL, flags)) == -1) {
313
	if ((flags = fcntl(session->fd, F_SETFL, flags)) == -1) {
320
		ao2_unlock(session);

   
321
		return -1;
314
		return -1;
322
	}
315
	}
323
	ao2_unlock(session);

   
324

    
   
316

   
325
	return 0;
317
	return 0;
326
}
318
}
327

    
   
319

   
328
/* MAINTENANCE WARNING on ast_websocket_read()!
320
/* MAINTENANCE WARNING on ast_websocket_read()!
329
 *
321
 *
330
 * We have to keep in mind during this function that the fact that session->fd seems ready
322
 * We have to keep in mind during this function that the fact that session->fd seems ready
331
 * (via poll) does not necessarily mean we have application data ready, because in the case
323
 * (via poll) does not necessarily mean we have application data ready, because in the case
332
 * of an SSL socket, there is some encryption data overhead that needs to be read from the
324
 * of an SSL socket, there is some encryption data overhead that needs to be read from the
333
 * TCP socket, so poll() may say there are bytes to be read, but whether it is just 1 byte
325
 * TCP socket, so poll() may say there are bytes to be read, but whether it is just 1 byte
334
 * or N bytes we do not know that, and we do not know how many of those bytes (if any) are
326
 * or N bytes we do not know that, and we do not know how many of those bytes (if any) are
335
 * for application data (for us) and not just for the SSL protocol consumption
327
 * for application data (for us) and not just for the SSL protocol consumption
336
 *
328
 *
337
 * There used to be a couple of nasty bugs here that were fixed in last refactoring but I
329
 * There used to be a couple of nasty bugs here that were fixed in last refactoring but I
338
 * want to document them so the constraints are clear and we do not re-introduce them:
330
 * want to document them so the constraints are clear and we do not re-introduce them:
339
 *
331
 *
340
 * - This function would incorrectly assume that fread() would necessarily return more than
332
 * - This function would incorrectly assume that fread() would necessarily return more than
341
 *   1 byte of data, just because a websocket frame is always >= 2 bytes, but the thing
333
 *   1 byte of data, just because a websocket frame is always >= 2 bytes, but the thing
342
 *   is we're dealing with a TCP bitstream here, we could read just one byte and that's normal.
334
 *   is we're dealing with a TCP bitstream here, we could read just one byte and that's normal.
343
 *   The problem before was that if just one byte was read, the function bailed out and returned
335
 *   The problem before was that if just one byte was read, the function bailed out and returned
344
 *   an error, effectively dropping the first byte of a websocket frame header!
336
 *   an error, effectively dropping the first byte of a websocket frame header!
345
 *
337
 *
346
 * - Another subtle bug was that it would just read up to MAX_WS_HDR_SZ (14 bytes) via fread()
338
 * - Another subtle bug was that it would just read up to MAX_WS_HDR_SZ (14 bytes) via fread()
347
 *   then assume that executing poll() would tell you if there is more to read, but since
339
 *   then assume that executing poll() would tell you if there is more to read, but since
348
 *   we're dealing with a buffered stream (session->f is a FILE*), poll would say there is
340
 *   we're dealing with a buffered stream (session->f is a FILE*), poll would say there is
349
 *   nothing else to read (in the real tcp socket session->fd) and we would get stuck here
341
 *   nothing else to read (in the real tcp socket session->fd) and we would get stuck here
350
 *   without processing the rest of the data in session->f internal buffers until another packet
342
 *   without processing the rest of the data in session->f internal buffers until another packet
351
 *   came on the network to unblock us!
343
 *   came on the network to unblock us!
352
 *
344
 *
353
 * Note during the header parsing stage we try to read in small chunks just what we need, this
345
 * Note during the header parsing stage we try to read in small chunks just what we need, this
354
 * is buffered data anyways, no expensive syscall required most of the time ...
346
 * is buffered data anyways, no expensive syscall required most of the time ...
355
 */
347
 */
356
static inline int ws_safe_read(struct ast_websocket *session, char *buf, int len, enum ast_websocket_opcode *opcode)
348
static inline int ws_safe_read(struct ast_websocket *session, char *buf, int len, enum ast_websocket_opcode *opcode)
357
{
349
{
358
	int sanity;
350
	int sanity;
359
	size_t rlen;
351
	size_t rlen;
360
	int xlen = len;
352
	int xlen = len;
361
	char *rbuf = buf;
353
	char *rbuf = buf;
362
	ao2_lock(session);

   
363
	for (sanity = 10; sanity; sanity--) {
354
	for (sanity = 10; sanity; sanity--) {
364
		clearerr(session->f);
355
		clearerr(session->f);
365
		rlen = fread(rbuf, 1, xlen, session->f);
356
		rlen = fread(rbuf, 1, xlen, session->f);
366
		if (0 == rlen && ferror(session->f) && errno != EAGAIN) {
357
		if (0 == rlen && ferror(session->f) && errno != EAGAIN) {
367
			ast_log(LOG_ERROR, "Error reading from web socket: %s\n", strerror(errno));
358
			ast_log(LOG_ERROR, "Error reading from web socket: %s\n", strerror(errno));
368
			(*opcode) = AST_WEBSOCKET_OPCODE_CLOSE;
359
			(*opcode) = AST_WEBSOCKET_OPCODE_CLOSE;
369
			session->closing = 1;
360
			session->closing = 1;
370
			ao2_unlock(session);

   
371
			return -1;
361
			return -1;
372
		}
362
		}
373
		xlen = (xlen - rlen);
363
		xlen = (xlen - rlen);
374
		rbuf = rbuf + rlen;
364
		rbuf = rbuf + rlen;
375
		if (0 == xlen) {
365
		if (0 == xlen) {
376
			break;
366
			break;
377
		}
367
		}
378
		if (ast_wait_for_input(session->fd, 1000) < 0) {
368
		if (ast_wait_for_input(session->fd, 1000) < 0) {
379
			ast_log(LOG_ERROR, "ast_wait_for_input returned err: %s\n", strerror(errno));
369
			ast_log(LOG_ERROR, "ast_wait_for_input returned err: %s\n", strerror(errno));
380
			(*opcode) = AST_WEBSOCKET_OPCODE_CLOSE;
370
			(*opcode) = AST_WEBSOCKET_OPCODE_CLOSE;
381
			session->closing = 1;
371
			session->closing = 1;
382
			ao2_unlock(session);

   
383
			return -1;
372
			return -1;
384
		}
373
		}
385
	}
374
	}
386
	if (!sanity) {
375
	if (!sanity) {
387
		ast_log(LOG_WARNING, "Websocket seems unresponsive, disconnecting ...\n");
376
		ast_log(LOG_WARNING, "Websocket seems unresponsive, disconnecting ...\n");
388
		(*opcode) = AST_WEBSOCKET_OPCODE_CLOSE;
377
		(*opcode) = AST_WEBSOCKET_OPCODE_CLOSE;
389
		session->closing = 1;
378
		session->closing = 1;
390
		ao2_unlock(session);

   
391
		return -1;
379
		return -1;
392
	}
380
	}
393
	ao2_unlock(session);

   
394
	return 0;
381
	return 0;
395
}
382
}
396

    
   
383

   
397
int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, char **payload, uint64_t *payload_len, enum ast_websocket_opcode *opcode, int *fragmented)
384
int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, char **payload, uint64_t *payload_len, enum ast_websocket_opcode *opcode, int *fragmented)
398
{
385
{
399
	char buf[MAXIMUM_FRAME_SIZE] = "";
386
	char buf[MAXIMUM_FRAME_SIZE] = "";
400
	int fin = 0;
387
	int fin = 0;
401
	int mask_present = 0;
388
	int mask_present = 0;
402
	char *mask = NULL, *new_payload = NULL;
389
	char *mask = NULL, *new_payload = NULL;
403
	size_t options_len = 0, frame_size = 0;
390
	size_t options_len = 0, frame_size = 0;
404

    
   
391

   
405
	*payload = NULL;
392
	*payload = NULL;
406
	*payload_len = 0;
393
	*payload_len = 0;
407
	*fragmented = 0;
394
	*fragmented = 0;
408

    
   
395

   
409
	if (ws_safe_read(session, &buf[0], MIN_WS_HDR_SZ, opcode)) {
396
	if (ws_safe_read(session, &buf[0], MIN_WS_HDR_SZ, opcode)) {
410
		return 0;
397
		return 0;
411
	}
398
	}
412
	frame_size += MIN_WS_HDR_SZ;
399
	frame_size += MIN_WS_HDR_SZ;
413

    
   
400

   
414
	/* ok, now we have the first 2 bytes, so we know some flags, opcode and payload length (or whether payload length extension will be required) */
401
	/* ok, now we have the first 2 bytes, so we know some flags, opcode and payload length (or whether payload length extension will be required) */
415
	*opcode = buf[0] & 0xf;
402
	*opcode = buf[0] & 0xf;
416
	*payload_len = buf[1] & 0x7f;
403
	*payload_len = buf[1] & 0x7f;
417
	if (*opcode == AST_WEBSOCKET_OPCODE_TEXT || *opcode == AST_WEBSOCKET_OPCODE_BINARY || *opcode == AST_WEBSOCKET_OPCODE_CONTINUATION ||
404
	if (*opcode == AST_WEBSOCKET_OPCODE_TEXT || *opcode == AST_WEBSOCKET_OPCODE_BINARY || *opcode == AST_WEBSOCKET_OPCODE_CONTINUATION ||
418
	    *opcode == AST_WEBSOCKET_OPCODE_PING || *opcode == AST_WEBSOCKET_OPCODE_PONG) {
405
	    *opcode == AST_WEBSOCKET_OPCODE_PING || *opcode == AST_WEBSOCKET_OPCODE_PONG) {
419
		fin = (buf[0] >> 7) & 1;
406
		fin = (buf[0] >> 7) & 1;
420
		mask_present = (buf[1] >> 7) & 1;
407
		mask_present = (buf[1] >> 7) & 1;
421

    
   
408

   
422
		/* Based on the mask flag and payload length, determine how much more we need to read before start parsing the rest of the header */
409
		/* Based on the mask flag and payload length, determine how much more we need to read before start parsing the rest of the header */
423
		options_len += mask_present ? 4 : 0;
410
		options_len += mask_present ? 4 : 0;
424
		options_len += (*payload_len == 126) ? 2 : (*payload_len == 127) ? 8 : 0;
411
		options_len += (*payload_len == 126) ? 2 : (*payload_len == 127) ? 8 : 0;
425
		if (options_len) {
412
		if (options_len) {
426
			/* read the rest of the header options */
413
			/* read the rest of the header options */
427
			if (ws_safe_read(session, &buf[frame_size], options_len, opcode)) {
414
			if (ws_safe_read(session, &buf[frame_size], options_len, opcode)) {
428
				return 0;
415
				return 0;
429
			}
416
			}
430
			frame_size += options_len;
417
			frame_size += options_len;
431
		}
418
		}
432

    
   
419

   
433
		if (*payload_len == 126) {
420
		if (*payload_len == 126) {
434
			/* Grab the 2-byte payload length  */
421
			/* Grab the 2-byte payload length  */
435
			*payload_len = ntohs(get_unaligned_uint16(&buf[2]));
422
			*payload_len = ntohs(get_unaligned_uint16(&buf[2]));
436
			mask = &buf[4];
423
			mask = &buf[4];
437
		} else if (*payload_len == 127) {
424
		} else if (*payload_len == 127) {
438
			/* Grab the 8-byte payload length  */
425
			/* Grab the 8-byte payload length  */
439
			*payload_len = ntohl(get_unaligned_uint64(&buf[2]));
426
			*payload_len = ntohl(get_unaligned_uint64(&buf[2]));
440
			mask = &buf[10];
427
			mask = &buf[10];
441
		} else {
428
		} else {
442
			/* Just set the mask after the small 2-byte header */
429
			/* Just set the mask after the small 2-byte header */
443
			mask = &buf[2];
430
			mask = &buf[2];
444
		}
431
		}
445

    
   
432

   
446
		/* Now read the rest of the payload */
433
		/* Now read the rest of the payload */
447
		*payload = &buf[frame_size]; /* payload will start here, at the end of the options, if any */
434
		*payload = &buf[frame_size]; /* payload will start here, at the end of the options, if any */
448
		frame_size = frame_size + (*payload_len); /* final frame size is header + optional headers + payload data */
435
		frame_size = frame_size + (*payload_len); /* final frame size is header + optional headers + payload data */
449
		if (frame_size > MAXIMUM_FRAME_SIZE) {
436
		if (frame_size > MAXIMUM_FRAME_SIZE) {
450
			ast_log(LOG_WARNING, "Cannot fit huge websocket frame of %zd bytes\n", frame_size);
437
			ast_log(LOG_WARNING, "Cannot fit huge websocket frame of %zd bytes\n", frame_size);
451
			/* The frame won't fit :-( */
438
			/* The frame won't fit :-( */
452
			ast_websocket_close(session, 1009);
439
			ast_websocket_close(session, 1009);
453
			return -1;
440
			return -1;
454
		}
441
		}
455

    
   
442

   
456
		if (ws_safe_read(session, (*payload), (*payload_len), opcode)) {
443
		if (ws_safe_read(session, (*payload), (*payload_len), opcode)) {
457
			return 0;
444
			return 0;
458
		}
445
		}
459

    
   
446

   
460
		/* If a mask is present unmask the payload */
447
		/* If a mask is present unmask the payload */
461
		if (mask_present) {
448
		if (mask_present) {
462
			unsigned int pos;
449
			unsigned int pos;
463
			for (pos = 0; pos < *payload_len; pos++) {
450
			for (pos = 0; pos < *payload_len; pos++) {
464
				(*payload)[pos] ^= mask[pos % 4];
451
				(*payload)[pos] ^= mask[pos % 4];
465
			}
452
			}
466
		}
453
		}
467

    
   
454

   
468
		ao2_lock(session);

   
469
		if (!(new_payload = ast_realloc(session->payload, (session->payload_len + *payload_len)))) {
455
		if (!(new_payload = ast_realloc(session->payload, (session->payload_len + *payload_len)))) {
470
			ast_log(LOG_WARNING, "Failed allocation: %p, %zd, %"PRIu64"\n",
456
			ast_log(LOG_WARNING, "Failed allocation: %p, %zd, %"PRIu64"\n",
471
				session->payload, session->payload_len, *payload_len);
457
				session->payload, session->payload_len, *payload_len);
472
			ao2_unlock(session);

   
473
			*payload_len = 0;
458
			*payload_len = 0;
474
			ast_websocket_close(session, 1009);
459
			ast_websocket_close(session, 1009);
475
			return 0;
460
			return 0;
476
		}
461
		}
477

    
   
462

   
478
		/* Per the RFC for PING we need to send back an opcode with the application data as received */
463
		/* Per the RFC for PING we need to send back an opcode with the application data as received */
479
		if (*opcode == AST_WEBSOCKET_OPCODE_PING) {
464
		if (*opcode == AST_WEBSOCKET_OPCODE_PING) {
480
			ast_websocket_write(session, AST_WEBSOCKET_OPCODE_PONG, *payload, *payload_len);
465
			ast_websocket_write(session, AST_WEBSOCKET_OPCODE_PONG, *payload, *payload_len);
481
		}
466
		}
482

    
   
467

   
483
		session->payload = new_payload;
468
		session->payload = new_payload;
484
		memcpy((session->payload + session->payload_len), (*payload), (*payload_len));
469
		memcpy((session->payload + session->payload_len), (*payload), (*payload_len));
485
		session->payload_len += *payload_len;
470
		session->payload_len += *payload_len;
486

    
   
471

   
487
		if (!fin && session->reconstruct && (session->payload_len < session->reconstruct)) {
472
		if (!fin && session->reconstruct && (session->payload_len < session->reconstruct)) {
488
			/* If this is not a final message we need to defer returning it until later */
473
			/* If this is not a final message we need to defer returning it until later */
489
			if (*opcode != AST_WEBSOCKET_OPCODE_CONTINUATION) {
474
			if (*opcode != AST_WEBSOCKET_OPCODE_CONTINUATION) {
490
				session->opcode = *opcode;
475
				session->opcode = *opcode;
491
			}
476
			}
492
			*opcode = AST_WEBSOCKET_OPCODE_CONTINUATION;
477
			*opcode = AST_WEBSOCKET_OPCODE_CONTINUATION;
493
			*payload_len = 0;
478
			*payload_len = 0;
494
			*payload = NULL;
479
			*payload = NULL;
495
		} else {
480
		} else {
496
			if (*opcode == AST_WEBSOCKET_OPCODE_CONTINUATION) {
481
			if (*opcode == AST_WEBSOCKET_OPCODE_CONTINUATION) {
497
				if (!fin) {
482
				if (!fin) {
498
					/* If this was not actually the final message tell the user it is fragmented so they can deal with it accordingly */
483
					/* If this was not actually the final message tell the user it is fragmented so they can deal with it accordingly */
499
					*fragmented = 1;
484
					*fragmented = 1;
500
				} else {
485
				} else {
501
					/* Final frame in multi-frame so push up the actual opcode */
486
					/* Final frame in multi-frame so push up the actual opcode */
502
					*opcode = session->opcode;
487
					*opcode = session->opcode;
503
				}
488
				}
504
			}
489
			}
505
			*payload_len = session->payload_len;
490
			*payload_len = session->payload_len;
506
			*payload = session->payload;
491
			*payload = session->payload;
507
			session->payload_len = 0;
492
			session->payload_len = 0;
508
		}
493
		}
509
		ao2_unlock(session);

   
510
	} else if (*opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
494
	} else if (*opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
511
		/* Make the payload available so the user can look at the reason code if they so desire */
495
		/* Make the payload available so the user can look at the reason code if they so desire */
512
		ao2_lock(session);

   
513
		if ((*payload_len) && (new_payload = ast_realloc(session->payload, *payload_len))) {
496
		if ((*payload_len) && (new_payload = ast_realloc(session->payload, *payload_len))) {
514
			if (ws_safe_read(session, &buf[frame_size], (*payload_len), opcode)) {
497
			if (ws_safe_read(session, &buf[frame_size], (*payload_len), opcode)) {
515
				return 0;
498
				return 0;
516
			}
499
			}
517
			session->payload = new_payload;
500
			session->payload = new_payload;
518
			memcpy(session->payload, &buf[frame_size], *payload_len);
501
			memcpy(session->payload, &buf[frame_size], *payload_len);
519
			*payload = session->payload;
502
			*payload = session->payload;
520
			frame_size += (*payload_len);
503
			frame_size += (*payload_len);
521
		}
504
		}
522

    
   
505

   
523
		if (!session->closing) {
506
		session->closing = 1;
524
			ast_websocket_close(session, 0);

   
525
		}

   
526

    
   

   
527
		fclose(session->f);

   
528
		session->f = NULL;

   
529
		ast_verb(2, "WebSocket connection from '%s' closed\n", ast_sockaddr_stringify(&session->address));
507
		ast_verb(2, "WebSocket connection from '%s' closed\n", ast_sockaddr_stringify(&session->address));
530
		ao2_unlock(session);

   
531
	} else {
508
	} else {
532
		ast_log(LOG_WARNING, "WebSocket unknown opcode %d\n", *opcode);
509
		ast_log(LOG_WARNING, "WebSocket unknown opcode %d\n", *opcode);
533
		/* We received an opcode that we don't understand, the RFC states that 1003 is for a type of data that can't be accepted... opcodes
510
		/* We received an opcode that we don't understand, the RFC states that 1003 is for a type of data that can't be accepted... opcodes
534
		 * fit that, I think. */
511
		 * fit that, I think. */
535
		ast_websocket_close(session, 1003);
512
		ast_websocket_close(session, 1003);
536
	}
513
	}
537

    
   
514

   
538
	return 0;
515
	return 0;
539
}
516
}
540

    
   
517

   
541
/*! \brief Callback that is executed everytime an HTTP request is received by this module */
518
/*! \brief Callback that is executed everytime an HTTP request is received by this module */
542
static int websocket_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
519
static int websocket_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
543
{
520
{
544
	struct ast_variable *v;
521
	struct ast_variable *v;
545
	char *upgrade = NULL, *key = NULL, *key1 = NULL, *key2 = NULL, *protos = NULL, *requested_protocols = NULL, *protocol = NULL;
522
	char *upgrade = NULL, *key = NULL, *key1 = NULL, *key2 = NULL, *protos = NULL, *requested_protocols = NULL, *protocol = NULL;
546
	int version = 0, flags = 1;
523
	int version = 0, flags = 1;
547
	struct websocket_protocol *protocol_handler = NULL;
524
	struct websocket_protocol *protocol_handler = NULL;
548
	struct ast_websocket *session;
525
	struct ast_websocket *session;
549

    
   
526

   
550
	/* Upgrade requests are only permitted on GET methods */
527
	/* Upgrade requests are only permitted on GET methods */
551
	if (method != AST_HTTP_GET) {
528
	if (method != AST_HTTP_GET) {
552
		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
529
		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
553
		return -1;
530
		return -1;
554
	}
531
	}
555

    
   
532

   
556
	/* Get the minimum headers required to satisfy our needs */
533
	/* Get the minimum headers required to satisfy our needs */
557
	for (v = headers; v; v = v->next) {
534
	for (v = headers; v; v = v->next) {
558
		if (!strcasecmp(v->name, "Upgrade")) {
535
		if (!strcasecmp(v->name, "Upgrade")) {
559
			upgrade = ast_strip(ast_strdupa(v->value));
536
			upgrade = ast_strip(ast_strdupa(v->value));
560
		} else if (!strcasecmp(v->name, "Sec-WebSocket-Key")) {
537
		} else if (!strcasecmp(v->name, "Sec-WebSocket-Key")) {
561
			key = ast_strip(ast_strdupa(v->value));
538
			key = ast_strip(ast_strdupa(v->value));
562
		} else if (!strcasecmp(v->name, "Sec-WebSocket-Key1")) {
539
		} else if (!strcasecmp(v->name, "Sec-WebSocket-Key1")) {
563
			key1 = ast_strip(ast_strdupa(v->value));
540
			key1 = ast_strip(ast_strdupa(v->value));
564
		} else if (!strcasecmp(v->name, "Sec-WebSocket-Key2")) {
541
		} else if (!strcasecmp(v->name, "Sec-WebSocket-Key2")) {
565
			key2 = ast_strip(ast_strdupa(v->value));
542
			key2 = ast_strip(ast_strdupa(v->value));
566
		} else if (!strcasecmp(v->name, "Sec-WebSocket-Protocol")) {
543
		} else if (!strcasecmp(v->name, "Sec-WebSocket-Protocol")) {
567
			requested_protocols = ast_strip(ast_strdupa(v->value));
544
			requested_protocols = ast_strip(ast_strdupa(v->value));
568
			protos = ast_strdupa(requested_protocols);
545
			protos = ast_strdupa(requested_protocols);
569
		} else if (!strcasecmp(v->name, "Sec-WebSocket-Version")) {
546
		} else if (!strcasecmp(v->name, "Sec-WebSocket-Version")) {
570
			if (sscanf(v->value, "%30d", &version) != 1) {
547
			if (sscanf(v->value, "%30d", &version) != 1) {
571
				version = 0;
548
				version = 0;
572
			}
549
			}
573
		}
550
		}
574
	}
551
	}
575

    
   
552

   
576
	/* If this is not a websocket upgrade abort */
553
	/* If this is not a websocket upgrade abort */
577
	if (!upgrade || strcasecmp(upgrade, "websocket")) {
554
	if (!upgrade || strcasecmp(upgrade, "websocket")) {
578
		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - did not request WebSocket\n",
555
		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - did not request WebSocket\n",
579
			ast_sockaddr_stringify(&ser->remote_address));
556
			ast_sockaddr_stringify(&ser->remote_address));
580
		ast_http_error(ser, 426, "Upgrade Required", NULL);
557
		ast_http_error(ser, 426, "Upgrade Required", NULL);
581
		return -1;
558
		return -1;
582
	} else if (ast_strlen_zero(requested_protocols)) {
559
	} else if (ast_strlen_zero(requested_protocols)) {
583
		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols requested\n",
560
		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols requested\n",
584
			ast_sockaddr_stringify(&ser->remote_address));
561
			ast_sockaddr_stringify(&ser->remote_address));
585
		fputs("HTTP/1.1 400 Bad Request\r\n"
562
		fputs("HTTP/1.1 400 Bad Request\r\n"
586
		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
563
		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
587
		return -1;
564
		return -1;
588
	} else if (key1 && key2) {
565
	} else if (key1 && key2) {
589
		/* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 and
566
		/* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 and
590
		 * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -- not currently supported*/
567
		 * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -- not currently supported*/
591
		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '00/76' chosen\n",
568
		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '00/76' chosen\n",
592
			ast_sockaddr_stringify(&ser->remote_address));
569
			ast_sockaddr_stringify(&ser->remote_address));
593
		fputs("HTTP/1.1 400 Bad Request\r\n"
570
		fputs("HTTP/1.1 400 Bad Request\r\n"
594
		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
571
		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
595
		return 0;
572
		return 0;
596
	}
573
	}
597

    
   
574

   
598
	/* Iterate through the requested protocols trying to find one that we have a handler for */
575
	/* Iterate through the requested protocols trying to find one that we have a handler for */
599
	while ((protocol = strsep(&requested_protocols, ","))) {
576
	while ((protocol = strsep(&requested_protocols, ","))) {
600
		if ((protocol_handler = ao2_find(protocols, ast_strip(protocol), OBJ_KEY))) {
577
		if ((protocol_handler = ao2_find(protocols, ast_strip(protocol), OBJ_KEY))) {
601
			break;
578
			break;
602
		}
579
		}
603
	}
580
	}
604

    
   
581

   
605
	/* If no protocol handler exists bump this back to the requester */
582
	/* If no protocol handler exists bump this back to the requester */
606
	if (!protocol_handler) {
583
	if (!protocol_handler) {
607
		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols out of '%s' supported\n",
584
		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols out of '%s' supported\n",
608
			ast_sockaddr_stringify(&ser->remote_address), protos);
585
			ast_sockaddr_stringify(&ser->remote_address), protos);
609
		fputs("HTTP/1.1 400 Bad Request\r\n"
586
		fputs("HTTP/1.1 400 Bad Request\r\n"
610
		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
587
		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
611
		return 0;
588
		return 0;
612
	}
589
	}
613

    
   
590

   
614
	/* Determine how to respond depending on the version */
591
	/* Determine how to respond depending on the version */
615
	if (version == 7 || version == 8 || version == 13) {
592
	if (version == 7 || version == 8 || version == 13) {
616
		/* Version 7 defined in specification http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07 */
593
		/* Version 7 defined in specification http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07 */
617
		/* Version 8 defined in specification http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10 */
594
		/* Version 8 defined in specification http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10 */
618
		/* Version 13 defined in specification http://tools.ietf.org/html/rfc6455 */
595
		/* Version 13 defined in specification http://tools.ietf.org/html/rfc6455 */
619
		char *combined, base64[64];
596
		char *combined, base64[64];
620
		unsigned combined_length;
597
		unsigned combined_length;
621
		uint8_t sha[20];
598
		uint8_t sha[20];
622

    
   
599

   
623
		combined_length = (key ? strlen(key) : 0) + strlen(WEBSOCKET_GUID) + 1;
600
		combined_length = (key ? strlen(key) : 0) + strlen(WEBSOCKET_GUID) + 1;
624
		if (!key || combined_length > 8192) { /* no stack overflows please */
601
		if (!key || combined_length > 8192) { /* no stack overflows please */
625
			fputs("HTTP/1.1 400 Bad Request\r\n"
602
			fputs("HTTP/1.1 400 Bad Request\r\n"
626
			      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
603
			      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
627
			ao2_ref(protocol_handler, -1);
604
			ao2_ref(protocol_handler, -1);
628
			return 0;
605
			return 0;
629
		}
606
		}
630

    
   
607

   
631
		if (!(session = ao2_alloc(sizeof(*session), session_destroy_fn))) {
608
		if (!(session = ao2_alloc(sizeof(*session), session_destroy_fn))) {
632
			ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted\n",
609
			ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted\n",
633
				ast_sockaddr_stringify(&ser->remote_address));
610
				ast_sockaddr_stringify(&ser->remote_address));
634
			fputs("HTTP/1.1 400 Bad Request\r\n"
611
			fputs("HTTP/1.1 400 Bad Request\r\n"
635
			      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
612
			      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
636
			ao2_ref(protocol_handler, -1);
613
			ao2_ref(protocol_handler, -1);
637
			return 0;
614
			return 0;
638
		}
615
		}
639

    
   
616

   
640
		combined = ast_alloca(combined_length);
617
		combined = ast_alloca(combined_length);
641
		snprintf(combined, combined_length, "%s%s", key, WEBSOCKET_GUID);
618
		snprintf(combined, combined_length, "%s%s", key, WEBSOCKET_GUID);
642
		ast_sha1_hash_uint(sha, combined);
619
		ast_sha1_hash_uint(sha, combined);
643
		ast_base64encode(base64, (const unsigned char*)sha, 20, sizeof(base64));
620
		ast_base64encode(base64, (const unsigned char*)sha, 20, sizeof(base64));
644

    
   
621

   
645
		fprintf(ser->f, "HTTP/1.1 101 Switching Protocols\r\n"
622
		fprintf(ser->f, "HTTP/1.1 101 Switching Protocols\r\n"
646
			"Upgrade: %s\r\n"
623
			"Upgrade: %s\r\n"
647
			"Connection: Upgrade\r\n"
624
			"Connection: Upgrade\r\n"
648
			"Sec-WebSocket-Accept: %s\r\n"
625
			"Sec-WebSocket-Accept: %s\r\n"
649
			"Sec-WebSocket-Protocol: %s\r\n\r\n",
626
			"Sec-WebSocket-Protocol: %s\r\n\r\n",
650
			upgrade,
627
			upgrade,
651
			base64,
628
			base64,
652
			protocol);
629
			protocol);
653
		fflush(ser->f);
630
		fflush(ser->f);
654
	} else {
631
	} else {
655

    
   
632

   
656
		/* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 or completely unknown */
633
		/* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 or completely unknown */
657
		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '%d' chosen\n",
634
		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '%d' chosen\n",
658
			ast_sockaddr_stringify(&ser->remote_address), version ? version : 75);
635
			ast_sockaddr_stringify(&ser->remote_address), version ? version : 75);
659
		fputs("HTTP/1.1 400 Bad Request\r\n"
636
		fputs("HTTP/1.1 400 Bad Request\r\n"
660
		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
637
		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
661
		ao2_ref(protocol_handler, -1);
638
		ao2_ref(protocol_handler, -1);
662
		return 0;
639
		return 0;
663
	}
640
	}
664

    
   
641

   
665
	/* Enable keepalive on all sessions so the underlying user does not have to */
642
	/* Enable keepalive on all sessions so the underlying user does not have to */
666
	if (setsockopt(ser->fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
643
	if (setsockopt(ser->fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
667
		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to enable keepalive\n",
644
		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to enable keepalive\n",
668
			ast_sockaddr_stringify(&ser->remote_address));
645
			ast_sockaddr_stringify(&ser->remote_address));
669
		fputs("HTTP/1.1 400 Bad Request\r\n"
646
		fputs("HTTP/1.1 400 Bad Request\r\n"
670
		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
647
		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
671
		ao2_ref(session, -1);
648
		ao2_ref(session, -1);
672
		ao2_ref(protocol_handler, -1);
649
		ao2_ref(protocol_handler, -1);
673
		return 0;
650
		return 0;
674
	}
651
	}
675

    
   
652

   
676
	ast_verb(2, "WebSocket connection from '%s' for protocol '%s' accepted using version '%d'\n", ast_sockaddr_stringify(&ser->remote_address), protocol, version);
653
	ast_verb(2, "WebSocket connection from '%s' for protocol '%s' accepted using version '%d'\n", ast_sockaddr_stringify(&ser->remote_address), protocol, version);
677

    
   
654

   
678
	/* Populate the session with all the needed details */
655
	/* Populate the session with all the needed details */
679
	ao2_lock(session);

   
680
	session->f = ser->f;
656
	session->f = ser->f;
681
	session->fd = ser->fd;
657
	session->fd = ser->fd;
682
	ast_sockaddr_copy(&session->address, &ser->remote_address);
658
	ast_sockaddr_copy(&session->address, &ser->remote_address);
683
	session->opcode = -1;
659
	session->opcode = -1;
684
	session->reconstruct = DEFAULT_RECONSTRUCTION_CEILING;
660
	session->reconstruct = DEFAULT_RECONSTRUCTION_CEILING;
685
	session->secure = ser->ssl ? 1 : 0;
661
	session->secure = ser->ssl ? 1 : 0;
686
	ao2_unlock(session);

   
687

    
   
662

   
688
	/* Give up ownership of the socket and pass it to the protocol handler */
663
	/* Give up ownership of the socket and pass it to the protocol handler */
689
	protocol_handler->callback(session, get_vars, headers);
664
	protocol_handler->callback(session, get_vars, headers);
690
	ao2_ref(protocol_handler, -1);
665
	ao2_ref(protocol_handler, -1);
691

    
   
666

   
692
	/* By dropping the FILE* from the session it won't get closed when the HTTP server cleans up */
667
	/* By dropping the FILE* from the session it won't get closed when the HTTP server cleans up */
693
	ser->f = NULL;
668
	ser->f = NULL;
694

    
   
669

   
695
	return 0;
670
	return 0;
696
}
671
}
697

    
   
672

   
698
static struct ast_http_uri websocketuri = {
673
static struct ast_http_uri websocketuri = {
699
	.callback = websocket_callback,
674
	.callback = websocket_callback,
700
	.description = "Asterisk HTTP WebSocket",
675
	.description = "Asterisk HTTP WebSocket",
701
	.uri = "ws",
676
	.uri = "ws",
702
	.has_subtree = 0,
677
	.has_subtree = 0,
703
	.data = NULL,
678
	.data = NULL,
704
	.key = __FILE__,
679
	.key = __FILE__,
705
};
680
};
706

    
   
681

   
707
/*! \brief Simple echo implementation which echoes received text and binary frames */
682
/*! \brief Simple echo implementation which echoes received text and binary frames */
708
static void websocket_echo_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
683
static void websocket_echo_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
709
{
684
{
710
	int flags, res;
685
	int flags, res;
711

    
   
686

   
712
	ast_debug(1, "Entering WebSocket echo loop\n");
687
	ast_debug(1, "Entering WebSocket echo loop\n");
713

    
   
688

   
714
	if ((flags = fcntl(ast_websocket_fd(session), F_GETFL)) == -1) {
689
	if ((flags = fcntl(ast_websocket_fd(session), F_GETFL)) == -1) {
715
		goto end;
690
		goto end;
716
	}
691
	}
717

    
   
692

   
718
	flags |= O_NONBLOCK;
693
	flags |= O_NONBLOCK;
719

    
   
694

   
720
	if (fcntl(ast_websocket_fd(session), F_SETFL, flags) == -1) {
695
	if (fcntl(ast_websocket_fd(session), F_SETFL, flags) == -1) {
721
		goto end;
696
		goto end;
722
	}
697
	}
723

    
   
698

   
724
	while ((res = ast_wait_for_input(ast_websocket_fd(session), -1)) > 0) {
699
	while ((res = ast_wait_for_input(ast_websocket_fd(session), -1)) > 0) {
725
		char *payload;
700
		char *payload;
726
		uint64_t payload_len;
701
		uint64_t payload_len;
727
		enum ast_websocket_opcode opcode;
702
		enum ast_websocket_opcode opcode;
728
		int fragmented;
703
		int fragmented;
729

    
   
704

   
730
		if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) {
705
		if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) {
731
			/* We err on the side of caution and terminate the session if any error occurs */
706
			/* We err on the side of caution and terminate the session if any error occurs */
732
			ast_log(LOG_WARNING, "Read failure during WebSocket echo loop\n");
707
			ast_log(LOG_WARNING, "Read failure during WebSocket echo loop\n");
733
			break;
708
			break;
734
		}
709
		}
735

    
   
710

   
736
		if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
711
		if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
737
			ast_websocket_write(session, opcode, payload, payload_len);
712
			ast_websocket_write(session, opcode, payload, payload_len);
738
		} else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
713
		} else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
739
			break;
714
			break;
740
		} else {
715
		} else {
741
			ast_debug(1, "Ignored WebSocket opcode %d\n", opcode);
716
			ast_debug(1, "Ignored WebSocket opcode %d\n", opcode);
742
		}
717
		}
743
	}
718
	}
744

    
   
719

   
745
end:
720
end:
746
	ast_debug(1, "Exitting WebSocket echo loop\n");
721
	ast_debug(1, "Exitting WebSocket echo loop\n");
747
	ast_websocket_unref(session);
722
	ast_websocket_unref(session);
748
}
723
}
749

    
   
724

   
750
static int load_module(void)
725
static int load_module(void)
751
{
726
{
752
	protocols = ao2_container_alloc(MAX_PROTOCOL_BUCKETS, protocol_hash_fn, protocol_cmp_fn);
727
	protocols = ao2_container_alloc(MAX_PROTOCOL_BUCKETS, protocol_hash_fn, protocol_cmp_fn);
753
	ast_http_uri_link(&websocketuri);
728
	ast_http_uri_link(&websocketuri);
754
	ast_websocket_add_protocol("echo", websocket_echo_callback);
729
	ast_websocket_add_protocol("echo", websocket_echo_callback);
755

    
   
730

   
756
	return 0;
731
	return 0;
757
}
732
}
758

    
   
733

   
759
static int unload_module(void)
734
static int unload_module(void)
760
{
735
{
761
	ast_websocket_remove_protocol("echo", websocket_echo_callback);
736
	ast_websocket_remove_protocol("echo", websocket_echo_callback);
762
	ast_http_uri_unlink(&websocketuri);
737
	ast_http_uri_unlink(&websocketuri);
763
	ao2_ref(protocols, -1);
738
	ao2_ref(protocols, -1);
764

    
   
739

   
765
	return 0;
740
	return 0;
766
}
741
}
767

    
   
742

   
768
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "HTTP WebSocket Support",
743
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "HTTP WebSocket Support",
769
		.load = load_module,
744
		.load = load_module,
770
		.unload = unload_module,
745
		.unload = unload_module,
771
		.load_pri = AST_MODPRI_CHANNEL_DEPEND,
746
		.load_pri = AST_MODPRI_CHANNEL_DEPEND,
772
	);
747
	);
  1. branches/11/res/res_http_websocket.c: Loading...

https://reviewboard.asterisk.org/ runs on a server provided by Digium, Inc. and uses bandwidth donated to the open source Asterisk community by API Digital Communications in Huntsville, AL USA.
Please report problems with this site to asteriskteam@digium.com.