void websrv_output(struct websrv_context *web, const void *data, size_t length) { data_blob_append(web, &web->output.content, data, length); TEVENT_FD_NOT_READABLE(web->conn->event.fde); TEVENT_FD_WRITEABLE(web->conn->event.fde); web->output.output_pending = true; }
/* call this when the socket becomes readable to kick off the whole stream parsing process */ _PUBLIC_ void packet_recv(struct packet_context *pc) { size_t npending; NTSTATUS status; size_t nread = 0; DATA_BLOB blob; bool recv_retry = false; if (pc->processing) { TEVENT_FD_NOT_READABLE(pc->fde); pc->processing++; return; } if (pc->recv_disable) { pc->recv_need_enable = true; TEVENT_FD_NOT_READABLE(pc->fde); return; } if (pc->packet_size != 0 && pc->num_read >= pc->packet_size) { goto next_partial; } if (pc->packet_size != 0) { /* we've already worked out how long this next packet is, so skip the socket_pending() call */ npending = pc->packet_size - pc->num_read; } else if (pc->initial_read != 0) { npending = pc->initial_read - pc->num_read; } else { if (pc->sock) { status = socket_pending(pc->sock, &npending); } else { status = NT_STATUS_CONNECTION_DISCONNECTED; } if (!NT_STATUS_IS_OK(status)) { packet_error(pc, status); return; } } if (npending == 0) { packet_eof(pc); return; } again: if (npending + pc->num_read < npending) { packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; } if (npending + pc->num_read < pc->num_read) { packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; } /* possibly expand the partial packet buffer */ if (npending + pc->num_read > pc->partial.length) { if (!data_blob_realloc(pc, &pc->partial, npending+pc->num_read)) { packet_error(pc, NT_STATUS_NO_MEMORY); return; } } if (pc->partial.length < pc->num_read + npending) { packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; } if ((uint8_t *)pc->partial.data + pc->num_read < (uint8_t *)pc->partial.data) { packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; } if ((uint8_t *)pc->partial.data + pc->num_read + npending < (uint8_t *)pc->partial.data) { packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; } status = socket_recv(pc->sock, pc->partial.data + pc->num_read, npending, &nread); if (NT_STATUS_IS_ERR(status)) { packet_error(pc, status); return; } if (recv_retry && NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { nread = 0; status = NT_STATUS_OK; } if (!NT_STATUS_IS_OK(status)) { return; } if (nread == 0 && !recv_retry) { packet_eof(pc); return; } pc->num_read += nread; if (pc->unreliable_select && nread != 0) { recv_retry = true; status = socket_pending(pc->sock, &npending); if (!NT_STATUS_IS_OK(status)) { packet_error(pc, status); return; } if (npending != 0) { goto again; } } next_partial: if (pc->partial.length != pc->num_read) { if (!data_blob_realloc(pc, &pc->partial, pc->num_read)) { packet_error(pc, NT_STATUS_NO_MEMORY); return; } } /* see if its a full request */ blob = pc->partial; blob.length = pc->num_read; status = pc->full_request(pc->private_data, blob, &pc->packet_size); if (NT_STATUS_IS_ERR(status)) { packet_error(pc, status); return; } if (!NT_STATUS_IS_OK(status)) { return; } if (pc->packet_size > pc->num_read) { /* the caller made an error */ DEBUG(0,("Invalid packet_size %lu greater than num_read %lu\n", (long)pc->packet_size, (long)pc->num_read)); packet_error(pc, NT_STATUS_INVALID_PARAMETER); return; } /* it is a full request - give it to the caller */ blob = pc->partial; blob.length = pc->num_read; if (pc->packet_size < pc->num_read) { pc->partial = data_blob_talloc(pc, blob.data + pc->packet_size, pc->num_read - pc->packet_size); if (pc->partial.data == NULL) { packet_error(pc, NT_STATUS_NO_MEMORY); return; } /* Trunate the blob sent to the caller to only the packet length */ if (!data_blob_realloc(pc, &blob, pc->packet_size)) { packet_error(pc, NT_STATUS_NO_MEMORY); return; } } else { pc->partial = data_blob(NULL, 0); } pc->num_read -= pc->packet_size; pc->packet_size = 0; if (pc->serialise) { pc->processing = 1; } pc->busy = true; status = pc->callback(pc->private_data, blob); pc->busy = false; if (pc->destructor_called) { talloc_free(pc); return; } if (pc->processing) { if (pc->processing > 1) { TEVENT_FD_READABLE(pc->fde); } pc->processing = 0; } if (!NT_STATUS_IS_OK(status)) { packet_error(pc, status); return; } /* Have we consumed the whole buffer yet? */ if (pc->partial.length == 0) { return; } /* we got multiple packets in one tcp read */ if (pc->ev == NULL) { goto next_partial; } blob = pc->partial; blob.length = pc->num_read; status = pc->full_request(pc->private_data, blob, &pc->packet_size); if (NT_STATUS_IS_ERR(status)) { packet_error(pc, status); return; } if (!NT_STATUS_IS_OK(status)) { return; } tevent_add_timer(pc->ev, pc, timeval_zero(), packet_next_event, pc); }
static void test_event_fd1_fde_handler(struct tevent_context *ev_ctx, struct tevent_fd *fde, uint16_t flags, void *private_data) { struct test_event_fd1_state *state = (struct test_event_fd1_state *)private_data; if (state->drain_done) { state->finished = true; state->error = __location__; return; } if (state->drain) { ssize_t ret; uint8_t c = 0; if (!(flags & TEVENT_FD_READ)) { state->finished = true; state->error = __location__; return; } ret = read(state->sock[0], &c, 1); if (ret == 1) { return; } /* * end of test... */ tevent_fd_set_flags(fde, 0); state->drain_done = true; return; } if (!state->got_write) { uint8_t c = 0; if (flags != TEVENT_FD_WRITE) { state->finished = true; state->error = __location__; return; } state->got_write = true; /* * we write to the other socket... */ write(state->sock[1], &c, 1); TEVENT_FD_NOT_WRITEABLE(fde); TEVENT_FD_READABLE(fde); return; } if (!state->got_read) { if (flags != TEVENT_FD_READ) { state->finished = true; state->error = __location__; return; } state->got_read = true; TEVENT_FD_NOT_READABLE(fde); return; } state->finished = true; state->error = __location__; return; }
/* called when a web connection becomes readable */ static void websrv_recv(struct stream_connection *conn, uint16_t flags) { struct web_server_data *wdata; struct websrv_context *web = talloc_get_type_abort(conn->private_data, struct websrv_context); NTSTATUS status; uint8_t buf[1024]; size_t nread; uint8_t *p; DATA_BLOB b; /* not the most efficient http parser ever, but good enough for us */ status = socket_recv(conn->socket, buf, sizeof(buf), &nread); if (NT_STATUS_IS_ERR(status)) goto failed; if (!NT_STATUS_IS_OK(status)) return; if (!data_blob_append(web, &web->input.partial, buf, nread)) goto failed; /* parse any lines that are available */ b = web->input.partial; while (!web->input.end_of_headers && (p=(uint8_t *)memchr(b.data, '\n', b.length))) { const char *line = (const char *)b.data; *p = 0; if (p != b.data && p[-1] == '\r') { p[-1] = 0; } status = http_parse_header(web, line); if (!NT_STATUS_IS_OK(status)) return; b.length -= (p - b.data) + 1; b.data = p+1; } /* keep any remaining bytes in web->input.partial */ if (b.length == 0) { b.data = NULL; } b = data_blob_talloc(web, b.data, b.length); data_blob_free(&web->input.partial); web->input.partial = b; /* we finish when we have both the full headers (terminated by a blank line) and any post data, as indicated by the content_length */ if (web->input.end_of_headers && web->input.partial.length >= web->input.content_length) { if (web->input.partial.length > web->input.content_length) { web->input.partial.data[web->input.content_length] = 0; } TEVENT_FD_NOT_READABLE(web->conn->event.fde); /* the reference/unlink code here is quite subtle. It is needed because the rendering of the web-pages, and in particular the esp/ejs backend, is semi-async. So we could well end up in the connection timeout code while inside http_process_input(), but we must not destroy the stack variables being used by that rendering process when we handle the timeout. */ if (!talloc_reference(web->task, web)) goto failed; wdata = talloc_get_type_abort(web->task->private_data, struct web_server_data); if (wdata == NULL) goto failed; wdata->http_process_input(wdata, web); talloc_unlink(web->task, web); } return; failed: stream_terminate_connection(conn, "websrv_recv: failed"); }