static DskIOResult dsk_client_stream_source_read_buffer (DskOctetSource *source, DskBuffer *read_buffer, DskError **error) { int rv; DskClientStream *stream; if (source->stream == NULL) { dsk_set_error (error, "read from dead stream"); return DSK_IO_RESULT_ERROR; } stream = DSK_CLIENT_STREAM (source->stream); if (stream->fd < 0) { dsk_set_error (error, "read from stream with no file-descriptor"); return DSK_IO_RESULT_ERROR; } rv = dsk_buffer_readv (read_buffer, stream->fd); if (rv < 0) { if (errno == EINTR || errno == EAGAIN) return DSK_IO_RESULT_AGAIN; dsk_set_error (error, "error reading data from fd %u: %s", stream->fd, strerror (errno)); return DSK_IO_RESULT_ERROR; } if (rv == 0) return DSK_IO_RESULT_EOF; ping_idle_disconnect_timer (stream); return DSK_IO_RESULT_SUCCESS; }
static dsk_boolean resize_mmap (TrivialTableCheckpoint *cp, unsigned min_needed, DskError **error) { unsigned new_size = (min_needed + MMAP_MIN_RESIZE_GROW + MMAP_GRANULARITY - 1) / MMAP_GRANULARITY * MMAP_GRANULARITY; void *mmapped; if (munmap ((void*) cp->mmapped, cp->mmapped_size) < 0) dsk_warning ("error calling munmap(): %s", strerror (errno)); if (ftruncate (cp->fd, new_size) < 0) { dsk_set_error (error, "error expanding file to %u bytes: %s", new_size, strerror (errno)); return DSK_FALSE; } mmapped = mmap (NULL, new_size, PROT_READ|PROT_WRITE, MAP_SHARED, cp->fd, 0); if (mmapped == MAP_FAILED) { dsk_set_error (error, "mmap of %u bytes failed: %s", new_size, strerror (errno)); return DSK_FALSE; } cp->mmapped = mmapped; cp->mmapped_size = new_size; return DSK_TRUE; }
static DskIOResult dsk_client_stream_sink_write_buffer (DskOctetSink *sink, DskBuffer *write_buffer, DskError **error) { int rv; DskClientStream *stream; if (sink->stream == NULL) { dsk_set_error (error, "write to dead stream"); return DSK_IO_RESULT_ERROR; } stream = DSK_CLIENT_STREAM (sink->stream); if (stream->fd < 0) { dsk_set_error (error, "write to stream with no file-descriptor"); return DSK_IO_RESULT_ERROR; } rv = dsk_buffer_writev (write_buffer, stream->fd); if (rv < 0) { if (errno == EINTR || errno == EAGAIN) return DSK_IO_RESULT_AGAIN; dsk_set_error (error, "error writing data to fd %u: %s", stream->fd, strerror (errno)); return DSK_IO_RESULT_ERROR; } ping_idle_disconnect_timer (stream); return DSK_IO_RESULT_SUCCESS; }
DskJsonValue * dsk_json_parse (size_t len, const uint8_t *data, DskError **error) { DskJsonParser *parser = dsk_json_parser_new (); if (!dsk_json_parser_feed (parser, len, data, error) || !dsk_json_parser_finish (parser, error)) { dsk_json_parser_destroy (parser); return NULL; } DskJsonValue *rv = dsk_json_parser_pop (parser); if (rv == NULL) { dsk_set_error (error, "json parser did not return any values"); dsk_json_parser_destroy (parser); return NULL; } DskJsonValue *test = dsk_json_parser_pop (parser); if (test != NULL) { dsk_set_error (error, "json parser returned multiple values, but only one expected"); dsk_json_parser_destroy (parser); dsk_json_value_free (test); dsk_json_value_free (rv); return NULL; } dsk_json_parser_destroy (parser); return rv; }
dsk_boolean dsk_json_parser_finish (DskJsonParser *parser, DskError **error) { switch (parser->lex_state) { case JSON_LEX_STATE_INIT: return DSK_TRUE; case JSON_LEX_STATE_TRUE: if (parser->fixed_n_chars == 4) { if (!handle_token (parser, JSON_TOKEN_TRUE, error)) return DSK_FALSE; break; } else goto bad_lex_state; case JSON_LEX_STATE_FALSE: if (parser->fixed_n_chars == 5) { if (!handle_token (parser, JSON_TOKEN_FALSE, error)) return DSK_FALSE; break; } else goto bad_lex_state; case JSON_LEX_STATE_NULL: if (parser->fixed_n_chars == 4) { if (!handle_token (parser, JSON_TOKEN_NULL, error)) return DSK_FALSE; break; } else goto bad_lex_state; case JSON_LEX_STATE_IN_DQ: case JSON_LEX_STATE_IN_DQ_BS: goto bad_lex_state; case JSON_LEX_STATE_IN_NUMBER: if (!handle_token (parser, JSON_TOKEN_NUMBER, error)) return DSK_FALSE; break; } if (parser->parse_state != PARSE_INIT) { dsk_set_error (error, "unfinished %s", parser->stack[0].type == STACK_NODE_OBJECT ? "object" : "array"); return DSK_FALSE; } return DSK_TRUE; bad_lex_state: dsk_set_error (error, "invalid lex state %s at end-of-file", lex_state_names[parser->lex_state]); return DSK_FALSE; }
static DskIOResult dsk_ssl_sink_write (DskOctetSink *sink, unsigned max_len, const void *data_out, unsigned *n_written_out, DskError **error) { DskSslStream *stream = DSK_SSL_STREAM (sink->stream); if (stream->handshaking) return DSK_IO_RESULT_AGAIN; int rv = SSL_write (stream->ssl, data_out, max_len); if (rv > 0) { *n_written_out = rv; return DSK_IO_RESULT_SUCCESS; } if (rv == 0) { //dsk_set_error (error, "connection closed"); return DSK_IO_RESULT_EOF; } stream->read_needed_to_write = 0; switch (rv) { switch (SSL_get_error (stream->ssl, rv)) { case SSL_ERROR_WANT_READ: stream->read_needed_to_write = 1; dsk_ssl_stream_update_traps (stream); return DSK_IO_RESULT_AGAIN; case SSL_ERROR_WANT_WRITE: dsk_ssl_stream_update_traps (stream); return DSK_IO_RESULT_AGAIN; case SSL_ERROR_SYSCALL: dsk_set_error (error, "Gsk-BIO interface had problems writing"); break; case SSL_ERROR_NONE: dsk_set_error (error, "error writing to ssl stream, but error code set to none"); break; default: { unsigned long l; l = ERR_peek_error(); dsk_set_error (error, "error writing to ssl stream [in the '%s' library]: %s: %s [is-client=%d]", ERR_lib_error_string(l), ERR_func_error_string(l), ERR_reason_error_string(l), stream->is_client); break; } } } return DSK_IO_RESULT_ERROR; }
static dsk_boolean do_handshake (DskSslStream *stream_ssl, DskError **error) { int rv; //DEBUG (stream_ssl, ("do_handshake[client=%u]: start", stream_ssl->is_client)); rv = SSL_do_handshake (stream_ssl->ssl); if (rv <= 0) { int error_code = SSL_get_error (stream_ssl->ssl, rv); unsigned long l = ERR_peek_error(); switch (error_code) { case SSL_ERROR_NONE: stream_ssl->handshaking = 0; dsk_ssl_stream_update_traps (stream_ssl); break; case SSL_ERROR_SYSCALL: dsk_set_error (error, "error with underlying stream"); return DSK_FALSE; break; case SSL_ERROR_WANT_READ: stream_ssl->read_needed_to_handshake = 1; stream_ssl->write_needed_to_handshake = 0; dsk_ssl_stream_update_traps (stream_ssl); break; case SSL_ERROR_WANT_WRITE: stream_ssl->read_needed_to_handshake = 0; stream_ssl->write_needed_to_handshake = 1; dsk_ssl_stream_update_traps (stream_ssl); break; default: { dsk_set_error (error, "error doing-handshake on SSL socket: %s: %s [code=%08lx (%lu)] [rv=%d]", ERR_func_error_string(l), ERR_reason_error_string(l), l, l, error_code); return DSK_FALSE; } } } else { stream_ssl->handshaking = 0; dsk_ssl_stream_update_traps (stream_ssl); } return DSK_TRUE; }
static DSK_CMDLINE_CALLBACK_DECLARE(handle_make_maze) { unsigned width, height; unsigned y; Game *game; DSK_UNUSED (arg_name); DSK_UNUSED (callback_data); if (arg_value == NULL) width = height = 10; else { if (sscanf (arg_value, "%ux%u", &width, &height) != 2) { dsk_set_error (error, "error parsing WIDTHxHEIGHT for --make-maze"); return DSK_FALSE; } } game = create_game ("name doesn't matter", width, height); for (y = 0; y < height; y++) { render_hwall_line_ascii (width, game->h_walls + width * y); render_vwall_line_ascii (width, game->v_walls + width * y); } render_hwall_line_ascii (width, game->h_walls); exit (0); return DSK_TRUE; }
static dsk_boolean handle_port (const char *arg_name, const char *arg_value, void *callback_data, DskError **error) { DskOctetListenerSocketOptions options = DSK_OCTET_LISTENER_SOCKET_OPTIONS_DEFAULT; DskOctetListener *listener; options.bind_port = strtoul (arg_value, &end, 10); if (arg_value == end || *end != 0) { dsk_set_error (error, "error parsing integer from '%s'", arg_value); return DSK_FALSE; } listener = dsk_octet_listener_socket_new (&options, error); if (listener == NULL) return DSK_FALSE; dsk_hook_trap (&listener->incoming, (DskHookFunc) handle_incoming_connection, NULL, NULL); dsk_main_add_object (listener); return DSK_TRUE; }
static dsk_boolean dsk_bz2lib_decompressor_finish (DskOctetFilter *filter, DskBuffer *out, DskError **error) { DskBz2libDecompressor *decompressor = (DskBz2libDecompressor *) filter; DskBufferFragment *prev_last_frag; dsk_boolean added_fragment = DSK_FALSE; if (decompressor->input_ended) return DSK_TRUE; prev_last_frag = NULL; // silence GCC for (;;) { DskBufferFragment *f; uint8_t *out_start; int zrv; if (out->last_frag == NULL || !fragment_has_empty_space (out->last_frag)) { added_fragment = DSK_TRUE; prev_last_frag = out->last_frag; dsk_buffer_append_empty_fragment (out); } decompressor->bz2lib.next_in = NULL; decompressor->bz2lib.avail_in = 0; f = out->last_frag; out_start = f->buf + f->buf_start + f->buf_length; decompressor->bz2lib.next_out = (char*) out_start; decompressor->bz2lib.avail_out = f->buf_max_size - f->buf_start - f->buf_length; zrv = BZ2_bzDecompress (&decompressor->bz2lib); if (zrv == BZ_OK || zrv == BZ_STREAM_END) { unsigned amt_out = decompressor->bz2lib.next_out - (char*) out_start; f->buf_length += amt_out; out->size += amt_out; if (zrv == BZ_STREAM_END) break; } else { dsk_set_error (error, "error finishing decompression: %s [%d]", bzrv_to_string (zrv), zrv); dsk_buffer_maybe_remove_empty_fragment (out); return DSK_FALSE; } } /* If we added a fragment that we didn't use, remove it. */ if (added_fragment && out->last_frag->buf_length == 0) { dsk_buffer_fragment_free (out->last_frag); out->last_frag = prev_last_frag; if (out->last_frag == NULL) out->first_frag = NULL; } return DSK_TRUE; }
static dsk_boolean dsk_bz2lib_compressor_process(DskOctetFilter *filter, DskBuffer *out, unsigned in_length, const uint8_t *in_data, DskError **error) { DskBz2libCompressor *compressor = (DskBz2libCompressor *) filter; DskBufferFragment *prev_last_frag; dsk_boolean added_fragment = DSK_FALSE; while (in_length > 0) { DskBufferFragment *f; uint8_t *out_start; int zrv; if (out->last_frag == NULL || !fragment_has_empty_space (out->last_frag)) { added_fragment = DSK_TRUE; prev_last_frag = out->last_frag; dsk_buffer_append_empty_fragment (out); } compressor->bz2lib.next_in = (char*)in_data; compressor->bz2lib.avail_in = in_length; f = out->last_frag; out_start = f->buf + f->buf_start + f->buf_length; compressor->bz2lib.next_out = (char *) out_start; compressor->bz2lib.avail_out = f->buf_max_size - f->buf_start - f->buf_length; zrv = BZ2_bzCompress (&compressor->bz2lib, BZ_RUN); if (zrv == BZ_OK) { unsigned amt_in = compressor->bz2lib.next_in - (char*)in_data; unsigned amt_out = compressor->bz2lib.next_out - (char*)out_start; in_length -= amt_in; in_data += amt_in; f->buf_length += amt_out; out->size += amt_out; } else { dsk_set_error (error, "error compressing: %s", bzrv_to_string (zrv)); dsk_buffer_maybe_remove_empty_fragment (out); return DSK_FALSE; } } /* If we added a fragment that we didn't use, remove it. */ if (added_fragment && out->last_frag->buf_length == 0) { dsk_buffer_fragment_free (out->last_frag); out->last_frag = prev_last_frag; if (out->last_frag == NULL) out->first_frag = NULL; } return DSK_TRUE; }
static DskIOResult dsk_client_stream_source_read (DskOctetSource *source, unsigned max_len, void *data_out, unsigned *bytes_read_out, DskError **error) { int n_read; DskClientStream *stream; if (source->stream == NULL) { dsk_set_error (error, "write to dead client stream"); return DSK_IO_RESULT_ERROR; } stream = DSK_CLIENT_STREAM (source->stream); if (stream->fd < 0) { dsk_set_error (error, "no file-descriptor"); return DSK_IO_RESULT_ERROR; } if (stream->is_connecting) { dsk_set_error (error, "file-descriptor %d not connected yet", stream->fd); return DSK_IO_RESULT_ERROR; } if (max_len == 0) { *bytes_read_out = 0; return DSK_IO_RESULT_SUCCESS; } n_read = read (stream->fd, data_out, max_len); if (n_read < 0) { if (errno == EINTR || errno == EAGAIN) return DSK_IO_RESULT_AGAIN; dsk_set_error (error, "error reading from client stream (fd %d): %s", stream->fd, strerror (errno)); return DSK_IO_RESULT_ERROR; } if (n_read == 0) return DSK_IO_RESULT_EOF; *bytes_read_out = n_read; ping_idle_disconnect_timer (stream); return DSK_IO_RESULT_SUCCESS; }
static DskIOResult dsk_client_stream_sink_write (DskOctetSink *sink, unsigned max_len, const void *data_out, unsigned *n_written_out, DskError **error) { int wrote; DskClientStream *stream; if (sink->stream == NULL) { dsk_set_error (error, "write to dead client stream"); return DSK_IO_RESULT_ERROR; } stream = DSK_CLIENT_STREAM (sink->stream); if (stream->fd < 0) { dsk_set_error (error, "no file-descriptor"); return DSK_IO_RESULT_ERROR; } if (stream->is_connecting) { dsk_set_error (error, "file-descriptor %d not connected yet", stream->fd); return DSK_IO_RESULT_ERROR; } if (max_len == 0) { *n_written_out = 0; return DSK_IO_RESULT_SUCCESS; } wrote = write (stream->fd, data_out, max_len); if (wrote < 0) { if (errno == EINTR || errno == EAGAIN) return DSK_IO_RESULT_AGAIN; dsk_set_error (error, "error writing to client stream (fd %d): %s", stream->fd, strerror (errno)); return DSK_IO_RESULT_ERROR; } *n_written_out = wrote; ping_idle_disconnect_timer (stream); return DSK_IO_RESULT_SUCCESS; }
dsk_boolean dsk_url_scheme_parse (const char *url, unsigned *scheme_length_out, DskUrlScheme *scheme_out, DskError **error) { const char *p = url; while (dsk_ascii_isalpha (*p)) p++; if (*p != ':') { dsk_set_error (error, "missing ':' in URL"); return DSK_FALSE; } *scheme_length_out = p + 1 - url; if ((url[0] == 'h' || url[0] == 'H') && (url[1] == 't' || url[1] == 'T') && (url[2] == 't' || url[2] == 'T') && (url[3] == 'p' || url[3] == 'P')) { if (p - url == 4) { *scheme_out = DSK_URL_SCHEME_HTTP; return DSK_TRUE; } else if (p - url == 5 && (p[4] == 's' || p[4] == 'S')) { *scheme_out = DSK_URL_SCHEME_HTTPS; return DSK_TRUE; } } else if ((url[0] == 'f' || url[0] == 'F') && (url[1] == 't' || url[1] == 'T') && (url[2] == 'p' || url[2] == 'P') && p - url == 3) { *scheme_out = DSK_URL_SCHEME_FTP; return DSK_TRUE; } dsk_set_error (error, "unknown scheme %.*s", (int)(p - url), url); return DSK_FALSE; }
static DSK_CMDLINE_CALLBACK_DECLARE(handle_dsk_log_timezone) { char *end; DSK_UNUSED (callback_data); if (!dsk_date_parse_timezone (arg_value, &end, &dsk_daemon_tzoffset)) { dsk_set_error (error, "bad timezone argument '%s' to --%s", arg_value, arg_name); return DSK_FALSE; } dsk_daemon_tzoffset *= 60; return DSK_TRUE; }
static dsk_boolean dsk_hex_decoder_finish(DskOctetFilter *filter, DskBuffer *out, DskError **error) { DskHexDecoder *dec = (DskHexDecoder *) filter; DSK_UNUSED (out); if (dec->has_nibble) { dsk_set_error (error, "stray nibble encountered- incomplete byte in hex-data"); return DSK_FALSE; } return DSK_TRUE; }
DskSslContext * dsk_ssl_context_new (DskSslContextOptions *options, DskError **error) { const SSL_METHOD *method = SSLv3_method (); SSL_CTX *ctx = SSL_CTX_new (method); DskSslContext *rv = dsk_object_new (&dsk_ssl_context_class); rv->ctx = ctx; if (options->password) { rv->password = dsk_strdup (options->password); SSL_CTX_set_default_passwd_cb (ctx, set_password_cb); SSL_CTX_set_default_passwd_cb_userdata (ctx, rv); } if (options->cert_filename) { if (SSL_CTX_use_certificate_file (ctx, options->cert_filename, SSL_FILETYPE_PEM) != 1) { dsk_set_error (error, "error using certificate file %s", options->cert_filename); dsk_object_unref (rv); return NULL; } } if (options->key_filename) { if (SSL_CTX_use_PrivateKey_file (ctx, options->key_filename, SSL_FILETYPE_PEM) != 1) { dsk_set_error (error, "error using key file %s", options->key_filename); dsk_object_unref (rv); return NULL; } } return rv; }
dsk_boolean dsk_ssl_stream_new (DskSslStreamOptions *options, DskSslStream **stream_out, DskOctetSource **source_out, DskOctetSink **sink_out, DskError **error) { DskSslStream *stream; DskSslSink *sink; DskSslSource *source; if (sink_out == NULL || source_out == NULL) { dsk_set_error (error, "dsk_ssl_stream_new: sink/source"); return DSK_FALSE; } sink = dsk_object_new (&dsk_ssl_sink_class); source = dsk_object_new (&dsk_ssl_source_class); stream = dsk_object_new (&dsk_ssl_stream_class); BIO *bio; bio = BIO_new (&bio_method__ssl_underlying_stream); bio->ptr = stream; bio->init = 1; /// HMM... stream->ssl = SSL_new (options->context->ctx); stream->context = dsk_object_ref (options->context); SSL_set_bio (stream->ssl, bio, bio); stream->base_instance.sink = DSK_OCTET_SINK (sink); /* does not own */ stream->base_instance.source = DSK_OCTET_SOURCE (source); /* does not own */ stream->is_client = options->is_client ? 1 : 0; stream->handshaking = DSK_TRUE; sink->base_instance.stream = dsk_object_ref (stream); source->base_instance.stream = dsk_object_ref (stream); if (stream->is_client) SSL_set_connect_state (stream->ssl); else SSL_set_accept_state (stream->ssl); *sink_out = DSK_OCTET_SINK (sink); *source_out = DSK_OCTET_SOURCE (source); if (stream_out != NULL) *stream_out = stream; else dsk_object_unref (stream); return DSK_TRUE; }
static dsk_boolean dsk_hex_decoder_process (DskOctetFilter *filter, DskBuffer *out, unsigned in_length, const uint8_t *in_data, DskError **error) { DskHexDecoder *hexdec = (DskHexDecoder *) filter; DSK_UNUSED (error); while (in_length) { if (dsk_ascii_isxdigit (*in_data)) { if (hexdec->has_nibble) { dsk_buffer_append_byte (out, (hexdec->nibble << 4) | dsk_ascii_xdigit_value (*in_data)); hexdec->has_nibble = DSK_FALSE; } else { hexdec->nibble = dsk_ascii_xdigit_value (*in_data); hexdec->has_nibble = DSK_TRUE; } in_data++; in_length--; } else if (dsk_ascii_isspace (*in_data)) { in_data++; in_length--; } else { dsk_set_error (error, "bad character %s in hex-data", dsk_ascii_byte_name (*in_data)); return DSK_FALSE; } } return DSK_TRUE; }
dsk_boolean dsk_client_stream_new (DskClientStreamOptions *options, DskClientStream **stream_out, DskOctetSink **sink_out, DskOctetSource **source_out, DskError **error) { DskClientStream *rv; dsk_boolean has_address = !ip_address_is_default (&options->address); /* check trivial usage considerations */ dsk_warn_if_fail (!(options->hostname != NULL && has_address), "ignoring ip-address because symbolic name given"); if (options->hostname != NULL || has_address) { if (options->port == 0) { dsk_set_error (error, "port must be non-zero for client (hostname is '%s')", options->hostname); return DSK_FALSE; } dsk_warn_if_fail (options->path == NULL, "cannot decide between tcp and local client"); } rv = dsk_object_new (&dsk_client_stream_class); rv->base_instance.sink = dsk_object_new (&dsk_client_stream_sink_class); rv->base_instance.source = dsk_object_new (&dsk_client_stream_source_class); rv->base_instance.sink->stream = dsk_object_ref (rv); rv->base_instance.source->stream = dsk_object_ref (rv); rv->reconnect_time_ms = -1; rv->idle_disconnect_time_ms = -1; rv->fd = -1; if (options->hostname != NULL) { if (dsk_hostname_looks_numeric (options->hostname)) rv->is_numeric_name = 1; rv->name = dsk_strdup (options->hostname); rv->port = options->port; } else if (has_address) { rv->is_numeric_name = 1; rv->name = dsk_ip_address_to_string (&options->address); rv->port = options->port; } else if (options->path != NULL) { rv->is_local_socket = 1; rv->name = dsk_strdup (options->path); } rv->idle_disconnect_time_ms = options->idle_disconnect_time; rv->reconnect_time_ms = options->reconnect_time; begin_connecting (rv); if (options->idle_disconnect_time >= 0) dsk_client_stream_set_max_idle_time (rv, options->idle_disconnect_time); if (options->reconnect_time >= 0) dsk_client_stream_set_reconnect_time (rv, options->reconnect_time); if (source_out) *source_out = rv->base_instance.source; else if (rv->base_instance.source) dsk_object_unref (rv->base_instance.source); if (sink_out) *sink_out = rv->base_instance.sink; else if (rv->base_instance.sink) dsk_object_unref (rv->base_instance.sink); if (stream_out) *stream_out = rv; else dsk_object_unref (rv); return DSK_TRUE; }
/* Parse a [] character class expression */ static struct CharClass * parse_character_class (const char **p_regex, DskMemPool *pool, DskError **error) { const char *at = *p_regex; dsk_boolean reverse = DSK_FALSE; struct CharClass *out = dsk_mem_pool_alloc0 (pool, sizeof (struct CharClass)); if (*at == '^') { reverse = DSK_TRUE; at++; } while (*at != 0 && *at != ']') { /* this muck is structured annoyingly: we just to the label got_range_start_and_dash whenever we encounter a '-' after a single character (either literally or as a backslash sequence), to handle range expressions. */ unsigned first_value; if (*at == '\\') { struct CharClass *sub; at++; if (!get_backslash_char_class (&at, &sub)) { *p_regex = at; /* for error reporting (maybe?) */ dsk_set_error (error, "bad \\ expression (at %s)", dsk_ascii_byte_name (*at)); return NULL; } if (IS_SINGLE_CHAR_CLASS (sub) && *at == '-') { first_value = SINGLE_CHAR_CLASS_GET_CHAR (sub); at++; goto got_range_start_and_dash; } char_class_union_inplace (out, sub); } else if (at[1] == '-') { first_value = *at; at += 2; goto got_range_start_and_dash; } else { /* single character */ CHAR_CLASS_BITVEC_SET (out, *at); at++; } continue; got_range_start_and_dash: { unsigned last_value; unsigned code; if (*at == '\\') { struct CharClass *sub; const char *start; at++; start = at; if (!get_backslash_char_class (&at, &sub)) { *p_regex = at; /* for error reporting (maybe?) */ dsk_set_error (error, "bad \\ expression (at %s)", dsk_ascii_byte_name (*at)); return NULL; } if (!IS_SINGLE_CHAR_CLASS (sub)) { dsk_set_error (error, "non-single-byte \\%c encountered - cannot use in range", *start); return NULL; } last_value = SINGLE_CHAR_CLASS_GET_CHAR (sub); } else if (*at == ']') { /* syntax error */ dsk_set_error (error, "unterminated character class range"); return NULL; } else { last_value = *at; at++; } if (first_value > last_value) { dsk_set_error (error, "character range is not first<last (first=%s, last=%s)", dsk_ascii_byte_name (first_value), dsk_ascii_byte_name (last_value)); return NULL; } for (code = first_value; code <= last_value; code++) CHAR_CLASS_BITVEC_SET (out, code); } } *p_regex = at; if (reverse) char_class_reverse_inplace (out); return out; }
/* --- lexing --- */ dsk_boolean dsk_json_parser_feed (DskJsonParser *parser, size_t n_bytes, const uint8_t *bytes, DskError **error) { while (n_bytes > 0) { switch (parser->lex_state) { case JSON_LEX_STATE_INIT: while (n_bytes > 0 && dsk_ascii_isspace (*bytes)) { if (*bytes == '\n') parser->line_no++; bytes++; n_bytes--; } if (n_bytes == 0) break; switch (*bytes) { case 't': case 'T': parser->lex_state = JSON_LEX_STATE_TRUE; parser->fixed_n_chars = 1; bytes++; n_bytes--; break; case 'f': case 'F': parser->lex_state = JSON_LEX_STATE_FALSE; parser->fixed_n_chars = 1; bytes++; n_bytes--; break; case 'n': case 'N': parser->lex_state = JSON_LEX_STATE_NULL; parser->fixed_n_chars = 1; bytes++; n_bytes--; break; case '"': parser->lex_state = JSON_LEX_STATE_IN_DQ; parser->str_len = 0; bytes++; n_bytes--; break; case '-': case '+': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': parser->lex_state = JSON_LEX_STATE_IN_NUMBER; parser->str_len = 0; append_to_string_buffer (parser, 1, bytes); bytes++; n_bytes--; break; #define WRITE_CHAR_TOKEN_CASE(character, SHORTNAME) \ case character: \ if (!handle_token (parser, JSON_TOKEN_##SHORTNAME, error)) \ return DSK_FALSE; \ n_bytes--; \ bytes++; \ break WRITE_CHAR_TOKEN_CASE('{', LBRACE); WRITE_CHAR_TOKEN_CASE('}', RBRACE); WRITE_CHAR_TOKEN_CASE('[', LBRACKET); WRITE_CHAR_TOKEN_CASE(']', RBRACKET); WRITE_CHAR_TOKEN_CASE(',', COMMA); WRITE_CHAR_TOKEN_CASE(':', COLON); #undef WRITE_CHAR_TOKEN_CASE case '\n': parser->line_no++; n_bytes--; bytes++; break; case '\t': case '\r': case ' ': n_bytes--; bytes++; break; default: dsk_set_error (error, "unexpected character %s in json (line %u)", dsk_ascii_byte_name (*bytes), parser->line_no); return DSK_FALSE; } break; #define WRITE_FIXED_BAREWORD_CASE(SHORTNAME, lc, UC, length) \ case JSON_LEX_STATE_##SHORTNAME: \ if (parser->fixed_n_chars == length) \ { \ /* are we at end of string? */ \ if (dsk_ascii_isalnum (*bytes)) \ { \ dsk_set_error (error, \ "got %s after '%s' (line %u)", \ dsk_ascii_byte_name (*bytes), lc, \ parser->line_no); \ return DSK_FALSE; \ } \ else \ { \ parser->lex_state = JSON_LEX_STATE_INIT; \ if (!handle_token (parser, JSON_TOKEN_##SHORTNAME, \ error)) \ return DSK_FALSE; \ } \ } \ else if (*bytes == lc[parser->fixed_n_chars] \ || *bytes == UC[parser->fixed_n_chars]) \ { \ parser->fixed_n_chars += 1; \ n_bytes--; \ bytes++; \ } \ else \ { \ dsk_set_error (error, \ "unexpected character %s (parsing %s) (line %u)", \ dsk_ascii_byte_name (*bytes), UC, parser->line_no); \ return DSK_FALSE; \ } \ break; WRITE_FIXED_BAREWORD_CASE(TRUE, "true", "TRUE", 4); WRITE_FIXED_BAREWORD_CASE(FALSE, "false", "FALSE", 5); WRITE_FIXED_BAREWORD_CASE(NULL, "null", "NULL", 4); #undef WRITE_FIXED_BAREWORD_CASE case JSON_LEX_STATE_IN_DQ: if (*bytes == '"') { // TODO ASSERT utf16_surrogate == 0 if (!handle_token (parser, JSON_TOKEN_STRING, error)) return DSK_FALSE; bytes++; n_bytes--; parser->lex_state = JSON_LEX_STATE_INIT; } else if (*bytes == '\\') { n_bytes--; bytes++; parser->bs_sequence_len = 0; parser->lex_state = JSON_LEX_STATE_IN_DQ_BS; } else { // TODO ASSERT utf16_surrogate == 0 unsigned i; if (*bytes == '\n') parser->line_no++; for (i = 1; i < n_bytes; i++) if (bytes[i] == '"' || bytes[i] == '\\') break; else if (bytes[i] == '\n') parser->line_no++; append_to_string_buffer (parser, i, bytes); n_bytes -= i; bytes += i; } break; case JSON_LEX_STATE_IN_DQ_BS: if (parser->bs_sequence_len == 0) { switch (*bytes) { #define WRITE_BS_CHAR_CASE(bschar, cchar) \ case bschar: \ /* TODO ASSERT utf16_surrogate == 0 */ \ append_char_to_string_buffer (parser, cchar); \ bytes++; \ n_bytes--; \ parser->lex_state = JSON_LEX_STATE_IN_DQ; \ break WRITE_BS_CHAR_CASE('b', '\b'); WRITE_BS_CHAR_CASE('f', '\f'); WRITE_BS_CHAR_CASE('n', '\n'); WRITE_BS_CHAR_CASE('r', '\r'); WRITE_BS_CHAR_CASE('t', '\t'); WRITE_BS_CHAR_CASE('/', '/'); WRITE_BS_CHAR_CASE('"', '"'); WRITE_BS_CHAR_CASE('\\', '\\'); #undef WRITE_BS_CHAR_CASE case 'u': parser->bs_sequence[parser->bs_sequence_len++] = *bytes++; n_bytes--; break; default: dsk_set_error (error, "invalid character %s after '\\' (line %u)", dsk_ascii_byte_name (*bytes), parser->line_no); return DSK_FALSE; } } else { /* must be \uxxxx (the only multi-character \ sequence) */ if (!dsk_ascii_isxdigit (*bytes)) { dsk_set_error (error, "expected 4 hex digits after \\u, got %s (line %u)", dsk_ascii_byte_name (*bytes), parser->line_no); return DSK_FALSE; } parser->bs_sequence[parser->bs_sequence_len++] = *bytes++; n_bytes--; if (parser->bs_sequence_len == 5) { char utf8buf[8]; unsigned value; parser->bs_sequence[5] = 0; value = strtoul (parser->bs_sequence + 1, NULL, 16); if (DSK_UTF16_LO_SURROGATE_START <= value && value <= DSK_UTF16_LO_SURROGATE_END) { if (parser->utf16_surrogate == 0) { dsk_set_error (error, "low (second) half of surrogate pair was encountered without high-half, line %u", parser->line_no); return DSK_FALSE; } uint32_t code = dsk_utf16_surrogate_pair_to_codepoint (parser->utf16_surrogate, value); append_to_string_buffer (parser, dsk_utf8_encode_unichar (utf8buf, code), (const uint8_t *) utf8buf); parser->utf16_surrogate = 0; } else if (DSK_UTF16_HI_SURROGATE_START <= value && value <= DSK_UTF16_HI_SURROGATE_END) { if (parser->utf16_surrogate != 0) { dsk_set_error (error, "got two first-half surrogate pairs (UTF16 surrogate \\u%04u was followed by \\%04u), line %u", parser->utf16_surrogate, value, parser->line_no); return DSK_FALSE; } parser->utf16_surrogate = value; } else { if (parser->utf16_surrogate != 0) { dsk_set_error (error, "second half of UTF16 surrogate \\u%04u was not preceded by utf16, line %u", parser->utf16_surrogate, parser->line_no); return DSK_FALSE; } append_to_string_buffer (parser, dsk_utf8_encode_unichar (utf8buf, value), (const uint8_t *) utf8buf); parser->utf16_surrogate = 0; } parser->lex_state = JSON_LEX_STATE_IN_DQ; } #if 0 else { dsk_set_error (error, "internal error: expected 4 hex digits (line %u)", parser->line_no); return DSK_FALSE; } #endif } break; case JSON_LEX_STATE_IN_NUMBER: if (dsk_ascii_isdigit (*bytes) || *bytes == '.' || *bytes == 'e' || *bytes == 'E' || *bytes == '+' || *bytes == '-') { append_to_string_buffer (parser, 1, bytes); bytes++; n_bytes--; } else { /* append the number token */ if (!handle_token (parser, JSON_TOKEN_NUMBER, error)) return DSK_FALSE; /* go back to init state (do not consume character) */ parser->lex_state = JSON_LEX_STATE_INIT; } break; default: dsk_error ("unhandled lex state %u", parser->lex_state); } } return DSK_TRUE; }
static dsk_boolean handle_token (DskJsonParser *parser, JsonTokenType token, DskError **error) { switch (parser->parse_state) { case PARSE_INIT: /* expecting value */ case PARSE_GOT_MEMBER_COLON: /* expecting subvalue */ if (!is_allowed_subvalue_token (token)) goto bad_token; handle_expected_subvalue (parser, token); break; case PARSE_EXPECTING_ELEMENT: /* expecting subvalue */ if (token == JSON_TOKEN_RBRACKET) array_finished (parser); else if (!is_allowed_subvalue_token (token)) goto bad_token; else handle_expected_subvalue (parser, token); break; case PARSE_EXPECTING_MEMBER: if (token == JSON_TOKEN_STRING) { /* add new member; copy string */ char *name; DskJsonMember *member; if (STACK_TOP (parser).n_subs == STACK_TOP (parser).subs_alloced) stack_increase_subs_alloced (&STACK_TOP (parser)); name = dsk_strndup (parser->str_len, parser->str); member = &STACK_TOP (parser).u.members[STACK_TOP (parser).n_subs++]; member->name = name; member->value = NULL; parser->parse_state = PARSE_GOT_MEMBER_NAME; } else if (token == JSON_TOKEN_RBRACE) object_finished (parser); else goto bad_token; break; case PARSE_GOT_MEMBER_NAME: if (token != JSON_TOKEN_COLON) goto bad_token; else parser->parse_state = PARSE_GOT_MEMBER_COLON; break; case PARSE_GOT_MEMBER: /* expecting , or } */ if (token == JSON_TOKEN_COMMA) parser->parse_state = PARSE_EXPECTING_MEMBER; else if (token == JSON_TOKEN_RBRACE) { object_finished (parser); } else goto bad_token; break; case PARSE_GOT_ELEMENT: /* expecting , or ] */ if (token == JSON_TOKEN_COMMA) parser->parse_state = PARSE_EXPECTING_ELEMENT; else if (token == JSON_TOKEN_RBRACKET) { /* create array */ DskJsonValue *array; array = dsk_json_value_new_array (STACK_TOP(parser).n_subs, STACK_TOP(parser).u.values); /* pop the stack */ parser->stack_size--; /* deal with the new array */ handle_subvalue (parser, array); } else goto bad_token; break; } parser->str_len = 0; return DSK_TRUE; bad_token: dsk_set_error (error, "got unexpected token %s: %s (line %u)", token_names[token], parse_state_expecting_strings[parser->parse_state], parser->line_no); parser->str_len = 0; return DSK_FALSE; }
static dsk_boolean tokenize (const char *regex, struct Token **token_list_out, DskMemPool *pool, DskError **error) { struct Token *last = NULL; *token_list_out = NULL; while (*regex) { struct Token *t = dsk_mem_pool_alloc (pool, sizeof (struct Token)); switch (*regex) { case '*': t->type = TOKEN_STAR; regex++; break; case '+': t->type = TOKEN_PLUS; regex++; break; case '?': t->type = TOKEN_QUESTION_MARK; regex++; break; case '(': t->type = TOKEN_LPAREN; regex++; break; case ')': t->type = TOKEN_RPAREN; regex++; break; case '|': t->type = TOKEN_ALTER; regex++; break; case '[': { struct CharClass *cclass; /* parse character class */ regex++; cclass = parse_character_class (®ex, pool, error); if (cclass == NULL || *regex != ']') return DSK_FALSE; regex++; t->type = TOKEN_PATTERN; t->pattern = dsk_mem_pool_alloc (pool, sizeof (struct Pattern)); t->pattern->type = PATTERN_LITERAL; t->pattern->info.literal = cclass; break; } case '\\': { /* parse either char class or special literal */ struct CharClass *cclass; regex++; if (get_backslash_char_class (®ex, &cclass)) { t->type = TOKEN_PATTERN; t->pattern = dsk_mem_pool_alloc (pool, sizeof (struct Pattern)); t->pattern->type = PATTERN_LITERAL; t->pattern->info.literal = cclass; } else { if (regex[1] == 0) dsk_set_error (error, "unexpected backslash sequence in regex"); else dsk_set_error (error, "bad char %s after backslash", dsk_ascii_byte_name (regex[1])); return DSK_FALSE; } break; } case '.': t->type = TOKEN_PATTERN; t->pattern = dsk_mem_pool_alloc (pool, sizeof (struct Pattern)); t->pattern->type = PATTERN_LITERAL; t->pattern->info.literal = &char_class_dot; regex++; break; default: /* character literal */ t->type = TOKEN_PATTERN; t->pattern = dsk_mem_pool_alloc (pool, sizeof (struct Pattern)); t->pattern->type = PATTERN_LITERAL; t->pattern->info.literal = MK_LITERAL_CHAR_CLASS (regex[0]); regex++; break; } /* append to list */ t->prev = last; t->next = NULL; if (last) last->next = t; else *token_list_out = last = t; last = t; } return DSK_TRUE; }
static struct Pattern * parse_pattern (unsigned pattern_index, struct Token *token_list, DskMemPool *pool, DskError **error) { dsk_boolean last_was_alter; dsk_boolean accept_empty; /* Handle parens */ struct Token *token; for (token = token_list; token; token = token->next) if (token->type == TOKEN_LPAREN) { /* find matching rparen (or error) */ struct Token *rparen = token->next; int balance = 1; struct Pattern *subpattern; while (rparen) { if (rparen->type == TOKEN_LPAREN) balance++; else if (rparen->type == TOKEN_RPAREN) { balance--; if (balance == 0) break; } rparen = rparen->next; } if (balance) { /* missing right-paren */ dsk_set_error (error, "missing right-paren in regex"); return NULL; } /* recurse */ rparen->prev->next = NULL; subpattern = parse_pattern (pattern_index, token->next, pool, error); if (subpattern == NULL) return NULL; /* replace parenthesized expr with subpattern; slice out remainder of list */ token->type = TOKEN_PATTERN; token->pattern = subpattern; token->next = rparen->next; if (rparen->next) token->next->prev = token; } else if (token->type == TOKEN_RPAREN) { dsk_set_error (error, "unexpected right-paren in regex"); return NULL; } /* Handle star/plus/qm */ for (token = token_list; token; token = token->next) if (token->type == TOKEN_QUESTION_MARK || token->type == TOKEN_STAR || token->type == TOKEN_PLUS) { struct Pattern *new_pattern; if (token->prev == NULL || token->prev->type != TOKEN_PATTERN) { dsk_set_error (error, "'%c' must be precede by pattern", token->type == TOKEN_QUESTION_MARK ? '?' : token->type == TOKEN_STAR ? '*' : '+'); return NULL; } new_pattern = dsk_mem_pool_alloc (pool, sizeof (struct Pattern)); switch (token->type) { case TOKEN_QUESTION_MARK: new_pattern->type = PATTERN_OPTIONAL; new_pattern->info.optional = token->prev->pattern; break; case TOKEN_STAR: new_pattern->type = PATTERN_STAR; new_pattern->info.star = token->prev->pattern; break; case TOKEN_PLUS: new_pattern->type = PATTERN_PLUS; new_pattern->info.plus = token->prev->pattern; break; default: dsk_assert_not_reached (); } token->prev->pattern = new_pattern; /* remove token */ if (token->prev) token->prev->next = token->next; else token_list = token->next; if (token->next) token->next->prev = token->prev; /* token isn't in the list now! but it doesn't matter b/c token->next is still correct */ } /* Handle concatenation */ for (token = token_list; token && token->next; ) { if (token->type == TOKEN_PATTERN && token->next->type == TOKEN_PATTERN) { /* concat */ struct Pattern *new_pattern = dsk_mem_pool_alloc (pool, sizeof (struct Pattern)); struct Token *kill; new_pattern->type = PATTERN_CONCAT; new_pattern->info.concat.a = token->pattern; new_pattern->info.concat.b = token->next->pattern; token->pattern = new_pattern; /* remove token->next */ kill = token->next; token->next = kill->next; if (kill->next) kill->next->prev = token; } else token = token->next; } /* At this point we consist of nothing but alternations and patterns. Scan through, discarding TOKEN_ALTER, and keeping track of whether the empty pattern matches */ last_was_alter = DSK_TRUE; /* trick the empty pattern detector into triggering on initial '|' */ accept_empty = DSK_FALSE; for (token = token_list; token; token = token->next) if (token->type == TOKEN_ALTER) { if (last_was_alter) accept_empty = DSK_TRUE; last_was_alter = DSK_TRUE; /* remove token from list */ if (token->prev) token->prev->next = token->next; else token_list = token->next; if (token->next) token->next->prev = token->prev; } else { last_was_alter = DSK_FALSE; } if (last_was_alter) accept_empty = DSK_TRUE; /* if we accept an empty token, toss a PATTERN_EMPTY onto the list of patterns in the alternation. */ if (accept_empty || token_list == NULL) { struct Token *t = dsk_mem_pool_alloc (pool, sizeof (struct Token)); t->next = token_list; t->prev = NULL; if (t->next) t->next->prev = t; token_list = t; t->type = TOKEN_PATTERN; t->pattern = dsk_mem_pool_alloc (pool, sizeof (struct Pattern)); } /* At this point, token_list!=NULL, and it consists entirely of patterns. Reduce it to a singleton with the alternation pattern. */ while (token_list->next != NULL) { /* create alternation pattern */ struct Pattern *new_pattern = dsk_mem_pool_alloc (pool, sizeof (struct Pattern)); new_pattern->type = PATTERN_ALT; new_pattern->info.alternation.a = token_list->pattern; new_pattern->info.alternation.b = token_list->next->pattern; token_list->pattern = new_pattern; /* remove token->next */ { struct Token *kill = token_list->next; token_list->next = kill->next; if (kill->next) kill->next->prev = token_list; } } /* Return value consists of merely a single token-list. */ dsk_assert (token_list != NULL && token_list->next == NULL); return token_list->pattern; }
static dsk_boolean dsk_bz2lib_decompressor_process(DskOctetFilter *filter, DskBuffer *out, unsigned in_length, const uint8_t *in_data, DskError **error) { DskBz2libDecompressor *decompressor = (DskBz2libDecompressor *) filter; DskBufferFragment *prev_last_frag; dsk_boolean added_fragment = DSK_FALSE; if (decompressor->input_ended) { if (in_length == 0) return DSK_TRUE; dsk_set_error (error, "garbage after compressed data"); return DSK_FALSE; } prev_last_frag = NULL; // silence GCC while (in_length > 0) { DskBufferFragment *f; uint8_t *out_start; int zrv; if (out->last_frag == NULL || !fragment_has_empty_space (out->last_frag)) { added_fragment = DSK_TRUE; prev_last_frag = out->last_frag; dsk_buffer_append_empty_fragment (out); } decompressor->bz2lib.next_in = (char *) in_data; decompressor->bz2lib.avail_in = in_length; f = out->last_frag; out_start = f->buf + f->buf_start + f->buf_length; decompressor->bz2lib.next_out = (char*) out_start; decompressor->bz2lib.avail_out = f->buf_max_size - f->buf_start - f->buf_length; zrv = BZ2_bzDecompress (&decompressor->bz2lib); if (zrv == BZ_OK || zrv == BZ_STREAM_END) { unsigned amt_in = decompressor->bz2lib.next_in - (char*) in_data; unsigned amt_out = decompressor->bz2lib.next_out - (char*) out_start; in_data += amt_in; in_length -= amt_in; f->buf_length += amt_out; out->size += amt_out; if (zrv == BZ_STREAM_END) { decompressor->input_ended = DSK_TRUE; if (in_length > 0) { dsk_set_error (error, "garbage after compressed data"); dsk_buffer_maybe_remove_empty_fragment (out); return DSK_FALSE; } } } else { dsk_set_error (error, "error decompressing: %s [%d]", bzrv_to_string (zrv), zrv); dsk_buffer_maybe_remove_empty_fragment (out); return DSK_FALSE; } } /* If we added a fragment that we didn't use, remove it. */ if (added_fragment && out->last_frag->buf_length == 0) { dsk_buffer_fragment_free (out->last_frag); out->last_frag = prev_last_frag; if (out->last_frag == NULL) out->first_frag = NULL; } return DSK_TRUE; }
static DskTableCheckpoint * table_checkpoint_trivial__create (DskTableCheckpointInterface *iface, DskDir *dir, const char *basename, unsigned cp_data_len, const uint8_t *cp_data, DskTableCheckpoint *prior, /* optional */ DskError **error) { unsigned mmapped_size; int fd; void *mmapped; TrivialTableCheckpoint *rv; DSK_UNUSED (iface); if (prior == NULL) mmapped_size = DEFAULT_MMAP_SIZE; else { dsk_assert (prior->add == table_checkpoint_trivial__add); mmapped_size = ((TrivialTableCheckpoint *) prior)->mmapped_size; } if (mmapped_size < cp_data_len + 32) { mmapped_size = cp_data_len + 32; mmapped_size += MMAP_MIN_RESIZE_GROW; mmapped_size += MMAP_GRANULARITY - 1; mmapped_size /= MMAP_GRANULARITY; mmapped_size *= MMAP_GRANULARITY; } /* create fd */ fd = dsk_dir_openfd (dir, basename, DSK_DIR_OPENFD_WRITABLE|DSK_DIR_OPENFD_TRUNCATE|DSK_DIR_OPENFD_MAY_CREATE, 0666, error); if (fd < 0) return NULL; /* truncate / fallocate */ if (ftruncate (fd, mmapped_size) < 0) { dsk_set_error (error, "error expanding file %s to %u bytes: %s", basename, mmapped_size, strerror (errno)); close (fd); return NULL; } /* mmap */ mmapped = mmap (NULL, mmapped_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (mmapped == MAP_FAILED) { dsk_set_error (error, "mmap of %u bytes failed: %s", mmapped_size, strerror (errno)); return DSK_FALSE; } rv = DSK_NEW (TrivialTableCheckpoint); rv->base = table_checkpoint_trivial__vfuncs; rv->fd = fd; rv->mmapped = mmapped; rv->mmapped_size = mmapped_size; rv->cur_size = (12 + cp_data_len + 3) / 4 * 4; ((uint32_t *) (mmapped))[0] = UINT32_TO_LE (TRIVIAL_CP_MAGIC); ((uint32_t *) (mmapped))[1] = UINT32_TO_LE (1); /* version */ ((uint32_t *) (mmapped))[2] = UINT32_TO_LE (cp_data_len); /* version */ memcpy (mmapped + 12, cp_data, cp_data_len); if (cp_data_len % 4 != 0) memset (mmapped + 12 + cp_data_len, 0, 4 - cp_data_len % 4); * ((uint32_t *) (mmapped+rv->cur_size)) = 0xffffffff; /* end-marker */ return &rv->base; }
static DskTableCheckpoint * table_checkpoint_trivial__open (DskTableCheckpointInterface *iface, DskDir *dir, const char *basename, unsigned *cp_data_len_out, uint8_t **cp_data_out, DskTableCheckpointReplayFunc func, void *func_data, DskError **error) { int fd = -1; struct stat stat_buf; void *mmapped = NULL; TrivialTableCheckpoint *rv; unsigned version, cp_data_len; unsigned at; DSK_UNUSED (iface); /* open fd */ fd = dsk_dir_openfd (dir, basename, DSK_DIR_OPENFD_WRITABLE, 0, error); if (fd < 0) return NULL; /* fstat */ if (fstat (fd, &stat_buf) < 0) { dsk_set_error (error, "fstat of %s failed: %s", basename, strerror (errno)); goto error_cleanup; } /* mmap */ mmapped = mmap (NULL, stat_buf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (mmapped == MAP_FAILED) { dsk_set_error (error, "mmap of %u bytes (file %s) failed: %s", (unsigned) stat_buf.st_size, basename, strerror (errno)); goto error_cleanup; } /* check format */ if (((uint32_t*)mmapped)[0] != UINT32_TO_LE (TRIVIAL_CP_MAGIC)) { dsk_set_error (error, "checkpoint file %s has bad magic", basename); goto error_cleanup; } version = UINT32_FROM_LE (((uint32_t*)mmapped)[1]); if (version != 1) { dsk_set_error (error, "checkpoint file %s has bad version %u", basename, version); goto error_cleanup; } cp_data_len = UINT32_FROM_LE (((uint32_t*)mmapped)[2]); /* replay */ at = 12 + (cp_data_len + 3) / 4 * 4; if (at + 4 > stat_buf.st_size) { dsk_set_error (error, "checkpoint data length (%u) too big for file's length (%u)", cp_data_len, (unsigned) stat_buf.st_size); goto error_cleanup; } while (* (uint32_t*) (mmapped+at) != 0xffffffff) { uint32_t key_len, value_len; uint32_t kv_len, kvp_len; if (at + 8 > stat_buf.st_size) { dsk_set_error (error, "checkpoint entry header too long"); goto error_cleanup; } key_len = UINT32_FROM_LE (((uint32_t *) (mmapped+at))[0]); value_len = UINT32_FROM_LE (((uint32_t *) (mmapped+at))[1]); kv_len = key_len + value_len; if (kv_len < key_len) { dsk_set_error (error, "key+value length greater than 4G"); goto error_cleanup; } kvp_len = (kv_len + 3) / 4 * 4; if (at + 8 + kvp_len + 4 > stat_buf.st_size) { dsk_set_error (error, "checkpoint entry data too long"); goto error_cleanup; } if (!func (key_len, mmapped + at + 8, value_len, mmapped + at + 8 + key_len, func_data, error)) { if (error && !*error) dsk_set_error (error, "replay handler returned false but didn't set error"); goto error_cleanup; } at += 8 + kvp_len; } /* copy cp_data */ if (cp_data_len_out != NULL) *cp_data_len_out = cp_data_len; if (cp_data_out != NULL) { *cp_data_out = dsk_malloc (cp_data_len); memcpy (*cp_data_out, mmapped + 12, cp_data_len); } rv = DSK_NEW (TrivialTableCheckpoint); rv->base = table_checkpoint_trivial__vfuncs; rv->fd = fd; rv->mmapped = mmapped; rv->mmapped_size = stat_buf.st_size; rv->cur_size = at; return &rv->base; error_cleanup: if (mmapped != NULL && munmap ((void*) mmapped, stat_buf.st_size) < 0) dsk_warning ("error calling munmap(): %s", strerror (errno)); if (fd >= 0) close (fd); return NULL; }
DskIOResult dsk_websocket_receive (DskWebsocket *websocket, unsigned *length_out, uint8_t **data_out, DskError **error) { uint8_t header[9]; uint64_t length; restart: maybe_discard_data (websocket); if (websocket->incoming.size < 9) return DSK_IO_RESULT_AGAIN; dsk_buffer_peek (&websocket->incoming, 9, header); length = dsk_uint64be_parse (header + 1); if (length > websocket->max_length) { switch (websocket->too_long_mode) { case DSK_WEBSOCKET_MODE_DROP: websocket->to_discard = length + 9; goto restart; case DSK_WEBSOCKET_MODE_SHUTDOWN: do_deferred_shutdown (websocket); return DSK_IO_RESULT_ERROR; case DSK_WEBSOCKET_MODE_RETURN_ERROR: websocket->to_discard = length + 9; maybe_discard_data (websocket); dsk_set_error (error, "packet too long (%"PRIu64" bytes)", length); return DSK_IO_RESULT_ERROR; } } switch (header[0]) { case 0x00: /* uh oh - shutdown packet */ dsk_buffer_discard (&websocket->incoming, 9 + length); do_deferred_shutdown (websocket); return DSK_IO_RESULT_EOF; case 0xff: if (websocket->incoming.size - 9 < length) return DSK_IO_RESULT_AGAIN; *length_out = length; *data_out = dsk_malloc (length); dsk_buffer_discard (&websocket->incoming, 9); *data_out = dsk_malloc (length); dsk_buffer_read (&websocket->incoming, length, *data_out); update_hooks_with_buffer (websocket); return DSK_IO_RESULT_SUCCESS; default: /* error */ switch (websocket->bad_packet_type_mode) { case DSK_WEBSOCKET_MODE_SHUTDOWN: do_deferred_shutdown (websocket); break; case DSK_WEBSOCKET_MODE_RETURN_ERROR: dsk_set_error (error, "packet had bad type: 0x%02x", header[0]); websocket->to_discard = length + 9; maybe_discard_data (websocket); return DSK_IO_RESULT_ERROR; case DSK_WEBSOCKET_MODE_DROP: websocket->to_discard = length + 9; goto restart; } } dsk_assert_not_reached (); return DSK_IO_RESULT_ERROR; }
/* TODO: rewrite this thing! */ dsk_boolean dsk_url_scan (const char *url_string, DskUrlScanned *out, DskError **error) { int num_slashes; const char *at = url_string; DskUrlInterpretation interpretation = DSK_URL_INTERPRETATION_UNKNOWN; const char *query_start; const char *frag_start; const char *end_string; out->scheme_start = at; /* XXX: use dsk_url_scheme_parse() */ while (dsk_ascii_isalnum (*at)) at++; if (out->scheme_start == at) { dsk_set_error (error, "no scheme found in URL"); return DSK_FALSE; } if (*at != ':') { dsk_set_error (error, "missing : after scheme in URL"); return DSK_FALSE; } out->scheme_end = at; at++; /* skip : */ /* Parse scheme */ out->scheme = DSK_URL_SCHEME_UNKNOWN; switch (out->scheme_end - out->scheme_start) { case 3: if (dsk_ascii_strncasecmp (out->scheme_start, "ftp", 3) == 0) out->scheme = DSK_URL_SCHEME_FTP; break; case 4: if (dsk_ascii_strncasecmp (out->scheme_start, "http", 4) == 0) out->scheme = DSK_URL_SCHEME_HTTP; else if (dsk_ascii_strncasecmp (out->scheme_start, "file", 4) == 0) out->scheme = DSK_URL_SCHEME_FILE; break; case 5: if (dsk_ascii_strncasecmp (out->scheme_start, "https", 5) == 0) out->scheme = DSK_URL_SCHEME_HTTPS; break; } num_slashes = 0; while (*at == '/') { num_slashes++; at++; } if (out->scheme == DSK_URL_SCHEME_FILE) interpretation = DSK_URL_INTERPRETATION_ABSOLUTE; else switch (num_slashes) { case 0: interpretation = DSK_URL_INTERPRETATION_RELATIVE; break; case 1: interpretation = DSK_URL_INTERPRETATION_ABSOLUTE; break; case 2: /* ``schemes including a top hierarchical element for a naming * authority'' (Section 3.2) */ interpretation = DSK_URL_INTERPRETATION_REMOTE; break; case 3: /* File urls (well those are now handled above so this * is pretty dubious) */ interpretation = DSK_URL_INTERPRETATION_ABSOLUTE; break; default: /* hmm */ interpretation = DSK_URL_INTERPRETATION_ABSOLUTE; break; } out->host_start = out->host_end = NULL; out->username_start = out->username_end = NULL; out->password_start = out->password_end = NULL; out->port_start = out->port_end = NULL; out->port = 0; if (interpretation == DSK_URL_INTERPRETATION_REMOTE) { /* rfc 2396, section 3.2.2. */ const char *end_hostport; const char *at_sign; const char *colon; /* basically the syntax is: * USER@HOST:PORT/ * ^ | ^ * at_sign ^ end_hostport * colon */ end_hostport = strchr (at, '/'); if (end_hostport == NULL) end_hostport = strchr (at, 0); at_sign = memchr (at, '@', end_hostport - at); out->host_start = at_sign != NULL ? (at_sign + 1) : at; colon = memchr (out->host_start, ':', end_hostport - out->host_start); if (at_sign != NULL) { const char *password_sep = memchr (at, ':', at_sign - at); if (password_sep) { out->username_start = at; out->username_end = password_sep; out->password_start = password_sep + 1; out->password_end = at_sign; } else { out->username_start = at; out->username_end = at_sign; out->password_start = NULL; out->password_end = NULL; } /* XXX: should validate username against * GSK_URL_USERNAME_CHARSET */ } else { out->username_start = NULL; out->username_end = NULL; out->password_start = NULL; out->password_end = NULL; } out->host_end = colon != NULL ? colon : end_hostport; if (colon != NULL) { out->port_start = colon + 1; out->port_end = end_hostport; out->port = atoi (out->port_start); } at = end_hostport; } else if (interpretation == DSK_URL_INTERPRETATION_UNKNOWN) { dsk_set_error (error, "cannot guess how to interpret %.*s URL", (int)(out->scheme_end - out->scheme_start), out->scheme_start); return DSK_FALSE; } if (num_slashes > 0 && interpretation == DSK_URL_INTERPRETATION_ABSOLUTE) at--; query_start = strchr (at, '?'); frag_start = strchr (query_start != NULL ? query_start : at, '#'); end_string = strchr (at, 0); out->path_start = at; if (query_start != NULL) out->path_end = query_start; else if (frag_start != NULL) out->path_end = frag_start; else out->path_end = end_string; if (query_start != NULL) { out->query_start = query_start + 1; out->query_end = frag_start ? frag_start : end_string; } else out->query_start = out->query_end = NULL; if (frag_start != NULL) { out->fragment_start = frag_start + 1; out->fragment_end = end_string; } else out->fragment_start = out->fragment_end = NULL; #define CHECK(base, function) \ if (out->base##_start != NULL \ && !function (out->base##_start, out->base##_end)) \ { \ dsk_set_error (error, "invalid character in %s", #base);\ return DSK_FALSE; \ } CHECK (host, is_valid_hostname) CHECK (path, is_valid_generic_component) CHECK (query, is_valid_generic_component) #undef CHECK return DSK_TRUE; }