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; }
/* Connection thread. Here we take clients off the connection queue and check * the contents provided. We set up the parser then hand off to the specific * request handler. */ static void _handle_connection(void) { http_parser_t *parser; const char *rawuri; client_queue_t *node; while (1) { node = _get_connection(); if (node) { client_t *client = node->client; /* Check for special shoutcast compatability processing */ if (node->shoutcast) { _handle_shoutcast_compatible (node); continue; } /* process normal HTTP headers */ parser = httpp_create_parser(); httpp_initialize(parser, NULL); client->parser = parser; if (httpp_parse (parser, client->refbuf->data, node->offset)) { char *uri; /* we may have more than just headers, so prepare for it */ if (node->stream_offset == node->offset) client->refbuf->len = 0; else { char *ptr = client->refbuf->data; client->refbuf->len = node->offset - node->stream_offset; memmove (ptr, ptr + node->stream_offset, client->refbuf->len); } rawuri = httpp_getvar(parser, HTTPP_VAR_URI); /* assign a port-based shoutcast mountpoint if required */ if (node->shoutcast_mount && strcmp (rawuri, "/admin.cgi") == 0) httpp_set_query_param (client->parser, "mount", node->shoutcast_mount); free (node->shoutcast_mount); free (node); if (strcmp("ICE", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) && strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) { ERROR0("Bad HTTP protocol detected"); client_destroy (client); continue; } uri = util_normalise_uri(rawuri); if (uri == NULL) { client_destroy (client); continue; } if (parser->req_type == httpp_req_source || parser->req_type == httpp_req_put) { _handle_source_request (client, uri); } else if (parser->req_type == httpp_req_stats) { _handle_stats_request (client, uri); } else if (parser->req_type == httpp_req_get) { _handle_get_request (client, uri); } else { ERROR0("Wrong request type from client"); client_send_400 (client, "unknown request"); } free(uri); } else { free (node); ERROR0("HTTP request parsing failed"); client_destroy (client); } continue; } break; } }
static void _handle_shoutcast_compatible (client_queue_t *node) { char *http_compliant; int http_compliant_len = 0; http_parser_t *parser; ice_config_t *config = config_get_config (); char *shoutcast_mount; client_t *client = node->client; if (node->shoutcast_mount) shoutcast_mount = node->shoutcast_mount; else shoutcast_mount = config->shoutcast_mount; if (node->shoutcast == 1) { char *source_password, *ptr, *headers; mount_proxy *mountinfo = config_find_mount (config, shoutcast_mount, MOUNT_TYPE_NORMAL); if (mountinfo && mountinfo->password) source_password = strdup (mountinfo->password); else { if (config->source_password) source_password = strdup (config->source_password); else source_password = NULL; } config_release_config(); /* Get rid of trailing \r\n or \n after password */ ptr = strstr (client->refbuf->data, "\r\r\n"); if (ptr) headers = ptr+3; else { ptr = strstr (client->refbuf->data, "\r\n"); if (ptr) headers = ptr+2; else { ptr = strstr (client->refbuf->data, "\n"); if (ptr) headers = ptr+1; } } if (ptr == NULL) { client_destroy (client); free (source_password); free (node->shoutcast_mount); free (node); return; } *ptr = '\0'; if (source_password && strcmp (client->refbuf->data, source_password) == 0) { client->respcode = 200; /* send this non-blocking but if there is only a partial write * then leave to header timeout */ sock_write (client->con->sock, "OK2\r\nicy-caps:11\r\n\r\n"); node->offset -= (headers - client->refbuf->data); memmove (client->refbuf->data, headers, node->offset+1); node->shoutcast = 2; /* we've checked the password, now send it back for reading headers */ _add_request_queue (node); free (source_password); return; } else INFO1 ("password does not match \"%s\"", client->refbuf->data); client_destroy (client); free (source_password); free (node->shoutcast_mount); free (node); return; } /* actually make a copy as we are dropping the config lock */ shoutcast_mount = strdup (shoutcast_mount); config_release_config(); /* Here we create a valid HTTP request based of the information that was passed in via the non-HTTP style protocol above. This means we can use some of our existing code to handle this case */ http_compliant_len = 20 + strlen (shoutcast_mount) + node->offset; http_compliant = (char *)calloc(1, http_compliant_len); snprintf (http_compliant, http_compliant_len, "SOURCE %s HTTP/1.0\r\n%s", shoutcast_mount, client->refbuf->data); parser = httpp_create_parser(); httpp_initialize(parser, NULL); if (httpp_parse (parser, http_compliant, strlen(http_compliant))) { /* we may have more than just headers, so prepare for it */ if (node->stream_offset == node->offset) client->refbuf->len = 0; else { char *ptr = client->refbuf->data; client->refbuf->len = node->offset - node->stream_offset; memmove (ptr, ptr + node->stream_offset, client->refbuf->len); } client->parser = parser; source_startup (client, shoutcast_mount, SHOUTCAST_SOURCE_AUTH); } else { httpp_destroy (parser); client_destroy (client); } free (http_compliant); free (shoutcast_mount); free (node->shoutcast_mount); free (node); return; }
/* Connection thread. Here we take clients off the connection queue and check * the contents provided. We set up the parser then hand off to the specific * request handler. */ static void *_handle_connection(void *arg) { http_parser_t *parser; char *rawuri, *uri; while (global.running == ICE_RUNNING) { kitsune_update("connection_handle"); /**DSU updatepoint */ client_queue_t *node = _get_connection(); if (node) { client_t *client = node->client; /* Check for special shoutcast compatability processing */ if (node->shoutcast) { _handle_shoutcast_compatible (node); continue; } /* process normal HTTP headers */ parser = httpp_create_parser(); httpp_initialize(parser, NULL); client->parser = parser; if (httpp_parse (parser, client->refbuf->data, node->offset)) { /* we may have more than just headers, so prepare for it */ if (node->stream_offset == node->offset) client->refbuf->len = 0; else { char *ptr = client->refbuf->data; client->refbuf->len = node->offset - node->stream_offset; memmove (ptr, ptr + node->stream_offset, client->refbuf->len); } free (node); if (strcmp("ICE", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) && strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) { ERROR0("Bad HTTP protocol detected"); client_destroy (client); continue; } rawuri = httpp_getvar(parser, HTTPP_VAR_URI); uri = util_normalise_uri(rawuri); if (uri == NULL) { client_destroy (client); continue; } if (parser->req_type == httpp_req_source) { _handle_source_request (client, uri, ICECAST_SOURCE_AUTH); } else if (parser->req_type == httpp_req_stats) { _handle_stats_request (client, uri); } else if (parser->req_type == httpp_req_get) { _handle_get_request (client, uri); } else { ERROR0("Wrong request type from client"); client_send_400 (client, "unknown request"); } free(uri); } else { free (node); ERROR0("HTTP request parsing failed"); client_destroy (client); } continue; } thread_sleep (50000); } DEBUG0 ("Connection thread done"); return NULL; }
/* we don't need to clean up on err, as we'll go through the node struct and clean all we have inside */ static int _connection_process (connection_queue_t *node) { refbuf_t *header; http_parser_t *parser = NULL; int hdrsize = 0; int shoutcast = 0; int err; char *shoutcast_mount = NULL; mount_proxy *mountinfo; ice_config_t *config; listener_t *listener; if (!node->refbuf) node->refbuf = refbuf_new (PER_CLIENT_REFBUF_SIZE); header = node->refbuf; { /* this code tests for shoutcastness */ config = config_get_config(); listener = config_get_listen_sock (config, node->con); if (listener) { WARN("listner"); if (listener->shoutcast_compat) shoutcast = 1; if (listener->ssl && ssl_ok) connection_uses_ssl (node->con); if (listener->shoutcast_mount) { shoutcast_mount = strdup (listener->shoutcast_mount); } else { shoutcast_mount = config->shoutcast_mount; } } WARN("shoutcast %d, mount %s", shoutcast, shoutcast_mount); mountinfo = config_find_mount (config, shoutcast_mount); config_release_config(); } if (shoutcast && !header->sync_point) { /* stage2 is actually handled by generic code */ err = _handle_shoutcast_stage1 (node, shoutcast_mount, mountinfo); if (err < 0) return err; } hdrsize = util_read_header (node->con, header, HEADER_READ_ENTIRE); if (hdrsize < 0) { ERROR ("Header read failed"); return hdrsize; } /* process normal HTTP headers */ if (node->parser) { parser = node->parser; } else { parser = node->parser = httpp_create_parser(); httpp_initialize(parser, NULL); } err = httpp_parse (parser, header->data, hdrsize); if (err == 0) { ERROR0("HTTP request parsing failed"); return -EINVAL; } /* XXX what happens when error in http ??? is err set ? */ if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE)) { ERROR("Error(%s)", httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE)); return err; } if (header->sync_point && (parser->req_type == httpp_req_source || parser->req_type == httpp_req_post)) { hdrsize = util_read_header (node->con, header, HEADER_READ_ENTIRE); if (hdrsize < 0) { INFO ("Header read failed"); return hdrsize; } } if (! node->client) { err = connection_client_setup (node); if (err < 0) return err; header->len -= hdrsize; if (header->len) { memmove(header->data, header->data + hdrsize, header->len); client_set_queue (node->client, header); } refbuf_release(header); } stats_event_inc (NULL, "connections"); WARN("shoutcast = %d", shoutcast); return _handle_client (node->client); }
static void *_handle_connection(void *arg) { char header[4096]; connection_t *con; http_parser_t *parser; char *rawuri, *uri; client_t *client; while (global.running == ICE_RUNNING) { /* grab a connection and set the socket to blocking */ while ((con = _get_connection())) { /* Handle meta-connections */ if(con->event_number > 0) { switch(con->event_number) { case EVENT_CONFIG_READ: event_config_read(con->event); break; default: ERROR1("Unknown event number: %d", con->event_number); break; } free(con); continue; } stats_event_inc(NULL, "connections"); sock_set_blocking(con->sock, SOCK_BLOCK); /* fill header with the http header */ memset(header, 0, sizeof (header)); if (util_read_header(con->sock, header, sizeof (header)) == 0) { /* either we didn't get a complete header, or we timed out */ connection_close(con); continue; } parser = httpp_create_parser(); httpp_initialize(parser, NULL); if (httpp_parse(parser, header, strlen(header))) { /* handle the connection or something */ if (strcmp("ICE", httpp_getvar(parser, HTTPP_VAR_PROTOCOL)) && strcmp("HTTP", httpp_getvar(parser, HTTPP_VAR_PROTOCOL))) { ERROR0("Bad HTTP protocol detected"); connection_close(con); httpp_destroy(parser); continue; } rawuri = httpp_getvar(parser, HTTPP_VAR_URI); uri = util_normalise_uri(rawuri); if(!uri) { client = client_create(con, parser); client_send_404(client, "The path you requested was invalid"); continue; } if (parser->req_type == httpp_req_source) { _handle_source_request(con, parser, uri); } else if (parser->req_type == httpp_req_stats) { _handle_stats_request(con, parser, uri); } else if (parser->req_type == httpp_req_get) { _handle_get_request(con, parser, uri); } else { ERROR0("Wrong request type from client"); connection_close(con); httpp_destroy(parser); } free(uri); continue; } else if(httpp_parse_icy(parser, header, strlen(header))) { /* TODO: Map incoming icy connections to /icy_0, etc. */ char mount[20]; unsigned i = 0; strcpy(mount, "/"); avl_tree_rlock(global.source_tree); while (source_find_mount (mount) != NULL) { snprintf (mount, sizeof (mount), "/icy_%u", i++); } avl_tree_unlock(global.source_tree); _handle_source_request(con, parser, mount); continue; } else { ERROR0("HTTP request parsing failed"); connection_close(con); httpp_destroy(parser); continue; } } thread_sleep (100000); } DEBUG0 ("Connection thread done"); return NULL; }