static double client_read_double(const remote_process_client *client) { double data; client_read_bytes(client, &data, 8); FIX_BYTE_ORDER(8); return data; }
static int64 client_read_int64(const remote_process_client *client) { int64 data; client_read_bytes(client, &data, 8); FIX_BYTE_ORDER(8); return data; }
static int32 client_read_int32(const remote_process_client *client) { int32 data; client_read_bytes(client, &data, 4); FIX_BYTE_ORDER(4); return data; }
static refbuf_t *ebml_get_buffer (source_t *source) { ebml_source_state_t *ebml_source_state = source->format->_state; format_plugin_t *format = source->format; char *data = NULL; int bytes = 0; refbuf_t *refbuf; int ret; while (1) { if ((bytes = ebml_read_space(ebml_source_state->ebml)) > 0) { refbuf = refbuf_new(bytes); ebml_read(ebml_source_state->ebml, refbuf->data, bytes); if (ebml_source_state->header == NULL) { ebml_source_state->header = refbuf; continue; } if (ebml_last_was_sync(ebml_source_state->ebml)) { refbuf->flags |= SOURCE_BLOCK_SYNC; } if (refbuf->len > 0) { source->client->queue_pos += refbuf->len; } return refbuf; } else { data = ebml_write_buffer(ebml_source_state->ebml, EBML_SLICE_SIZE); bytes = client_read_bytes (source->client, data, EBML_SLICE_SIZE); if (bytes <= 0) { ebml_wrote (ebml_source_state->ebml, 0); return NULL; } format->read_bytes += bytes; ret = ebml_wrote (ebml_source_state->ebml, bytes); if (ret != bytes) { ERROR0 ("Problem processing stream"); source->flags &= ~SOURCE_RUNNING; return NULL; } } } }
static refbuf_t *ebml_get_buffer(source_t *source) { ebml_source_state_t *ebml_source_state = source->format->_state; format_plugin_t *format = source->format; char *data = NULL; int bytes = 0; refbuf_t *refbuf; int ret; while (1) { if ((bytes = ebml_read_space(ebml_source_state->ebml)) > 0) { refbuf = refbuf_new(bytes); ebml_read(ebml_source_state->ebml, refbuf->data, bytes); if (ebml_source_state->header == NULL) { ebml_source_state->header = refbuf; continue; } if (ebml_last_was_sync(ebml_source_state->ebml)) { refbuf->sync_point = 1; } return refbuf; } else { data = ebml_write_buffer(ebml_source_state->ebml, EBML_SLICE_SIZE); bytes = client_read_bytes (source->client, data, EBML_SLICE_SIZE); if (bytes <= 0) { ebml_wrote (ebml_source_state->ebml, 0); return NULL; } format->read_bytes += bytes; ret = ebml_wrote (ebml_source_state->ebml, bytes); if (ret != bytes) { ICECAST_LOG_ERROR("Problem processing stream"); source->running = 0; return NULL; } } } }
static char *client_read_string(const remote_process_client *client) { int32 length = READ_INT32(); char *str; if (length < 0) { exit(10014); } str = (char*)malloc(length + 1); if (NULL == str) { /* Out of memory, probably incorrect length */ exit(10014); } client_read_bytes(client, str, (size_t)length); str[length] = 0; return str; }
/* This does the actual reading, making sure the read data is packaged in * blocks of 1400 bytes (near the common MTU size). This is because many * incoming streams come in small packets which could waste a lot of * bandwidth with many listeners due to headers and such like. */ static int complete_read (source_t *source) { int bytes; format_plugin_t *format = source->format; mp3_state *source_mp3 = format->_state; char *buf; refbuf_t *refbuf; #define REFBUF_SIZE 1400 if (source_mp3->read_data == NULL) { source_mp3->read_data = refbuf_new (REFBUF_SIZE); source_mp3->read_count = 0; } buf = source_mp3->read_data->data + source_mp3->read_count; bytes = client_read_bytes (source->client, buf, REFBUF_SIZE-source_mp3->read_count); if (bytes < 0) { if (source->client->con->error) { refbuf_release (source_mp3->read_data); source_mp3->read_data = NULL; } return 0; } source_mp3->read_count += bytes; refbuf = source_mp3->read_data; refbuf->len = source_mp3->read_count; format->read_bytes += bytes; if (source_mp3->read_count < REFBUF_SIZE) { if (source_mp3->read_count == 0) { refbuf_release (source_mp3->read_data); source_mp3->read_data = NULL; } return 0; } return 1; }
static void client_read_cell_visibilities(remote_process_client *client, struct ct_mutable_world *world) { int width, height, x0, y0, x1, y1; if (!client->cell_visibilities_read) { client->cell_visibilities_read = 1; client->cell_visibilities = (ct_cell_visibilities_t *)malloc(sizeof(ct_cell_visibilities_t)); memset(client->cell_visibilities, 0, sizeof(*client->cell_visibilities)); width = world->width; height = world->height; /* I won't actually read sizes here, instead I'll just verify their correctness */ if (READ_INT32() != width) { exit(10018); } if (READ_INT32() != height) { exit(10019); } if (READ_INT32() != _STANCE_COUNT_) { exit(10020); } for (x0 = 0; x0 < width; ++x0) { for (y0 = 0; y0 < height; ++y0) { for (x1 = 0; x1 < width; ++x1) { for (y1 = 0; y1 < height; ++y1) { int i; ct_bool vis[_STANCE_COUNT_]; client_read_bytes(client, vis, _STANCE_COUNT_); for (i = 0; i < _STANCE_COUNT_; ++i) { (*client->cell_visibilities)[i][y0][x0][y1][x1] = vis[i]; } } } } } } world->cell_visibilities = (ct_cell_visibilities_t *)malloc(sizeof(ct_cell_visibilities_t)); memcpy(world->cell_visibilities, client->cell_visibilities, sizeof(*world->cell_visibilities)); }
static int http_client_request (client_t *client) { refbuf_t *refbuf = client->shared_data; int remaining = PER_CLIENT_REFBUF_SIZE - 1 - refbuf->len, ret = -1; if (remaining && client->connection.discon_time > client->worker->current_time.tv_sec) { char *buf = refbuf->data + refbuf->len; ret = client_read_bytes (client, buf, remaining); if (ret > 0) { char *ptr; buf [ret] = '\0'; refbuf->len += ret; if (memcmp (refbuf->data, "<policy-file-request/>", 23) == 0) { fbinfo fb; memset (&fb, 0, sizeof(fb)); fb.mount = "/flashpolicy"; fb.flags = FS_USE_ADMIN; fb.type = FORMAT_TYPE_UNDEFINED; client->respcode = 200; refbuf_release (refbuf); client->shared_data = NULL; client->check_buffer = format_generic_write_to_client; return fserve_setup_client_fb (client, &fb); } /* find a blank line */ do { buf = refbuf->data; ptr = strstr (buf, "\r\n\r\n"); if (ptr) { ptr += 4; break; } ptr = strstr (buf, "\n\n"); if (ptr) { ptr += 2; break; } ptr = strstr (buf, "\r\r\n\r\r\n"); if (ptr) { ptr += 6; break; } client->schedule_ms = client->worker->time_ms + 100; return 0; } while (0); client->refbuf = client->shared_data; client->shared_data = NULL; client->connection.discon_time = 0; client->parser = httpp_create_parser(); httpp_initialize (client->parser, NULL); if (httpp_parse (client->parser, refbuf->data, refbuf->len)) { if (useragents.filename) { const char *agent = httpp_getvar (client->parser, "user-agent"); if (agent && search_cached_pattern (&useragents, agent) > 0) { INFO2 ("dropping client at %s because useragent is %s", client->connection.ip, agent); return -1; } } /* headers now parsed, make sure any sent content is next */ if (strcmp("ICE", httpp_getvar (client->parser, HTTPP_VAR_PROTOCOL)) && strcmp("HTTP", httpp_getvar (client->parser, HTTPP_VAR_PROTOCOL))) { ERROR0("Bad HTTP protocol detected"); return -1; } auth_check_http (client); switch (client->parser->req_type) { case httpp_req_get: refbuf->len = PER_CLIENT_REFBUF_SIZE; client->ops = &http_req_get_ops; break; case httpp_req_source: client->pos = ptr - refbuf->data; client->ops = &http_req_source_ops; break; case httpp_req_stats: refbuf->len = PER_CLIENT_REFBUF_SIZE; client->ops = &http_req_stats_ops; break; case httpp_req_options: return client_send_options (client); default: WARN1("unhandled request type from %s", client->connection.ip); return client_send_501 (client); } client->counter = 0; return client->ops->process(client); } /* invalid http request */ return -1; } if (ret && client->connection.error == 0) { /* scale up the retry time, very short initially, usual case */ uint64_t diff = client->worker->time_ms - client->counter; diff >>= 1; if (diff > 200) diff = 200; client->schedule_ms = client->worker->time_ms + 6 + diff; return 0; }
/* shoutcast source clients are handled specially because the protocol is limited. It is * essentially a password followed by a series of headers, each on a separate line. In here * we get the password and build a http request like a native source client would do */ static int shoutcast_source_client (client_t *client) { do { connection_t *con = &client->connection; if (con->error || con->discon_time <= client->worker->current_time.tv_sec) break; if (client->shared_data) /* need to get password first */ { refbuf_t *refbuf = client->shared_data; int remaining = PER_CLIENT_REFBUF_SIZE - 2 - refbuf->len, ret, len; char *buf = refbuf->data + refbuf->len; char *esc_header; refbuf_t *r, *resp; char header [128]; if (remaining == 0) break; ret = client_read_bytes (client, buf, remaining); if (ret == 0 || con->error) break; if (ret < 0) return 0; buf [ret] = '\0'; len = strcspn (refbuf->data, "\r\n"); if (refbuf->data [len] == '\0') /* no EOL yet */ return 0; refbuf->data [len] = '\0'; snprintf (header, sizeof(header), "source:%s", refbuf->data); esc_header = util_base64_encode (header); len += 1 + strspn (refbuf->data+len+1, "\r\n"); r = refbuf_new (PER_CLIENT_REFBUF_SIZE); snprintf (r->data, PER_CLIENT_REFBUF_SIZE, "SOURCE %s HTTP/1.0\r\n" "Authorization: Basic %s\r\n%s", client->server_conn->shoutcast_mount, esc_header, refbuf->data+len); r->len = strlen (r->data); free (esc_header); client->respcode = 200; resp = refbuf_new (30); snprintf (resp->data, 30, "OK2\r\nicy-caps:11\r\n\r\n"); resp->len = strlen (resp->data); resp->associated = r; client->refbuf = resp; refbuf_release (refbuf); client->shared_data = NULL; INFO1 ("emulation on %s", client->server_conn->shoutcast_mount); } format_generic_write_to_client (client); if (client->pos == client->refbuf->len) { refbuf_t *r = client->refbuf; client->shared_data = r->associated; client->refbuf = NULL; r->associated = NULL; refbuf_release (r); client->ops = &http_request_ops; client->pos = 0; } client->schedule_ms = client->worker->time_ms + 100; return 0; } while (0); refbuf_release (client->shared_data); client->shared_data = NULL; return -1; }
/* run along queue checking for any data that has come in or a timeout */ static void process_request_queue (void) { client_queue_t **node_ref = (client_queue_t **)&_req_queue; ice_config_t *config = config_get_config (); int timeout = config->header_timeout; config_release_config(); while (*node_ref) { client_queue_t *node = *node_ref; client_t *client = node->client; int len = PER_CLIENT_REFBUF_SIZE - 1 - node->offset; char *buf = client->refbuf->data + node->offset; if (len > 0) { if (client->con->con_time + timeout <= time(NULL)) len = 0; else len = client_read_bytes (client, buf, len); } if (len > 0) { int pass_it = 1; char *ptr; /* handle \n, \r\n and nsvcap which for some strange reason has * EOL as \r\r\n */ node->offset += len; client->refbuf->data [node->offset] = '\000'; do { if (node->shoutcast == 1) { /* password line */ if (strstr (client->refbuf->data, "\r\r\n") != NULL) break; if (strstr (client->refbuf->data, "\r\n") != NULL) break; if (strstr (client->refbuf->data, "\n") != NULL) break; } /* stream_offset refers to the start of any data sent after the * http style headers, we don't want to lose those */ ptr = strstr (client->refbuf->data, "\r\r\n\r\r\n"); if (ptr) { node->stream_offset = (ptr+6) - client->refbuf->data; break; } ptr = strstr (client->refbuf->data, "\r\n\r\n"); if (ptr) { node->stream_offset = (ptr+4) - client->refbuf->data; break; } ptr = strstr (client->refbuf->data, "\n\n"); if (ptr) { node->stream_offset = (ptr+2) - client->refbuf->data; break; } pass_it = 0; } while (0); if (pass_it) { if ((client_queue_t **)_req_queue_tail == &(node->next)) _req_queue_tail = (volatile client_queue_t **)node_ref; *node_ref = node->next; node->next = NULL; _add_connection (node); continue; } } else { if (len == 0 || client->con->error) { if ((client_queue_t **)_req_queue_tail == &node->next) _req_queue_tail = (volatile client_queue_t **)node_ref; *node_ref = node->next; client_destroy (client); free (node); continue; } } node_ref = &node->next; } _handle_connection(); }
/* main plugin handler for getting a buffer for the queue. In here we * just add an incoming page to the codecs and process it until either * more data is needed or we prodice a buffer for the queue. */ static refbuf_t *ogg_get_buffer (source_t *source) { ogg_state_t *ogg_info = source->format->_state; format_plugin_t *format = source->format; char *data = NULL; int bytes = 0; while (1) { while (1) { ogg_page page; refbuf_t *refbuf = NULL; ogg_codec_t *codec = ogg_info->current; /* if a codec has just been given a page then process it */ if (codec && codec->process) { refbuf = codec->process (ogg_info, codec); if (refbuf) return complete_buffer (source, refbuf); ogg_info->current = NULL; } if (ogg_sync_pageout (&ogg_info->oy, &page) > 0) { if (ogg_page_bos (&page)) { process_initial_page (source->format, &page); } else { ogg_info->bos_completed = 1; refbuf = process_ogg_page (ogg_info, &page); } if (ogg_info->error) { ERROR0 ("Problem processing stream"); source->flags &= ~SOURCE_RUNNING; return NULL; } if (refbuf) return complete_buffer (source, refbuf); continue; } /* need more stream data */ break; } /* we need more data to continue getting pages */ data = ogg_sync_buffer (&ogg_info->oy, 4096); bytes = client_read_bytes (source->client, data, 4096); if (bytes <= 0) { ogg_sync_wrote (&ogg_info->oy, 0); return NULL; } format->read_bytes += bytes; rate_add (format->in_bitrate, bytes, source->client->worker->current_time.tv_sec); ogg_sync_wrote (&ogg_info->oy, bytes); } }