/*!
 * \brief Destroy the pjsip transport.
 *
 * Called by pjsip transport manager.
 */
static pj_status_t ws_destroy(pjsip_transport *transport)
{
	struct ws_transport *wstransport = (struct ws_transport *)transport;
	int fd = ast_websocket_fd(wstransport->ws_session);

	if (fd > 0) {
		ast_websocket_close(wstransport->ws_session, 1000);
		shutdown(fd, SHUT_RDWR);
	}

	ao2_ref(wstransport, -1);

	return PJ_SUCCESS;
}
Example #2
0
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)
{
	char buf[MAXIMUM_FRAME_SIZE] = "";
	size_t frame_size, expected = 2;

	*payload = NULL;
	*payload_len = 0;
	*fragmented = 0;

	/* We try to read in 14 bytes, which is the largest possible WebSocket header */
	if ((frame_size = fread(&buf, 1, 14, session->f)) < 1) {
		return -1;
	}

	/* The minimum size for a WebSocket frame is 2 bytes */
	if (frame_size < expected) {
		return -1;
	}

	*opcode = buf[0] & 0xf;

	if (*opcode == AST_WEBSOCKET_OPCODE_TEXT || *opcode == AST_WEBSOCKET_OPCODE_BINARY || *opcode == AST_WEBSOCKET_OPCODE_CONTINUATION ||
	    *opcode == AST_WEBSOCKET_OPCODE_PING || *opcode == AST_WEBSOCKET_OPCODE_PONG) {
		int fin = (buf[0] >> 7) & 1;
		int mask_present = (buf[1] >> 7) & 1;
		char *mask = NULL, *new_payload;
		size_t remaining;

		if (mask_present) {
			/* The mask should take up 4 bytes */
			expected += 4;

			if (frame_size < expected) {
				/* Per the RFC 1009 means we received a message that was too large for us to process */
				ast_websocket_close(session, 1009);
				return 0;
			}
		}

		/* Assume no extended length and no masking at the beginning */
		*payload_len = buf[1] & 0x7f;
		*payload = &buf[2];

		/* Determine if extended length is being used */
		if (*payload_len == 126) {
			/* Use the next 2 bytes to get a uint16_t */
			expected += 2;
			*payload += 2;

			if (frame_size < expected) {
				ast_websocket_close(session, 1009);
				return 0;
			}

			*payload_len = ntohs(get_unaligned_uint16(&buf[2]));
		} else if (*payload_len == 127) {
			/* Use the next 8 bytes to get a uint64_t */
			expected += 8;
			*payload += 8;

			if (frame_size < expected) {
				ast_websocket_close(session, 1009);
				return 0;
			}

			*payload_len = ntohl(get_unaligned_uint64(&buf[2]));
		}

		/* If masking is present the payload currently points to the mask, so move it over 4 bytes to the actual payload */
		if (mask_present) {
			mask = *payload;
			*payload += 4;
		}

		/* Determine how much payload we need to read in as we may have already read some in */
		remaining = *payload_len - (frame_size - expected);

		/* If how much payload they want us to read in exceeds what we are capable of close the session, things
		 * will fail no matter what most likely */
		if (remaining > (MAXIMUM_FRAME_SIZE - frame_size)) {
			ast_websocket_close(session, 1009);
			return 0;
		}

		new_payload = *payload + (frame_size - expected);

		/* Read in the remaining payload */
		while (remaining > 0) {
			size_t payload_read;

			/* Wait for data to come in */
			if (ast_wait_for_input(session->fd, -1) <= 0) {
				*opcode = AST_WEBSOCKET_OPCODE_CLOSE;
				*payload = NULL;
				session->closing = 1;
				return 0;
			}

			/* If some sort of failure occurs notify the caller */
			if ((payload_read = fread(new_payload, 1, remaining, session->f)) < 1) {
				return -1;
			}

			remaining -= payload_read;
			new_payload += payload_read;
		}

		/* If a mask is present unmask the payload */
		if (mask_present) {
			unsigned int pos;
			for (pos = 0; pos < *payload_len; pos++) {
				(*payload)[pos] ^= mask[pos % 4];
			}
		}

		if (!(new_payload = ast_realloc(session->payload, session->payload_len + *payload_len))) {
			*payload_len = 0;
			ast_websocket_close(session, 1009);
			return 0;
		}

		/* Per the RFC for PING we need to send back an opcode with the application data as received */
		if (*opcode == AST_WEBSOCKET_OPCODE_PING) {
			ast_websocket_write(session, AST_WEBSOCKET_OPCODE_PONG, *payload, *payload_len);
		}

		session->payload = new_payload;
		memcpy(session->payload + session->payload_len, *payload, *payload_len);
		session->payload_len += *payload_len;

		if (!fin && session->reconstruct && (session->payload_len < session->reconstruct)) {
			/* If this is not a final message we need to defer returning it until later */
			if (*opcode != AST_WEBSOCKET_OPCODE_CONTINUATION) {
				session->opcode = *opcode;
			}
			*opcode = AST_WEBSOCKET_OPCODE_CONTINUATION;
			*payload_len = 0;
			*payload = NULL;
		} else {
			if (*opcode == AST_WEBSOCKET_OPCODE_CONTINUATION) {
				if (!fin) {
					/* If this was not actually the final message tell the user it is fragmented so they can deal with it accordingly */
					*fragmented = 1;
				} else {
					/* Final frame in multi-frame so push up the actual opcode */
					*opcode = session->opcode;
				}
			}
			*payload_len = session->payload_len;
			*payload = session->payload;
			session->payload_len = 0;
		}
	} else if (*opcode == AST_WEBSOCKET_OPCODE_CLOSE) {