/*! * \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; }
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) {