static int _handle_shoutcast_stage1 (connection_queue_t *node, char *shoutcast_mount, mount_proxy *mountinfo) { refbuf_t *refbuf = node->refbuf; char *source_password; int err, passlen; WARN ("IN"); if (mountinfo && mountinfo->password) { source_password = strdup (mountinfo->password); } else { ice_config_t *config = config_get_config (); source_password = strdup (config->source_password); config_release_config(); } passlen = util_read_header (node->con, refbuf, HEADER_READ_LINE); if (passlen <= 0) { WARN ("HEADER READ FAILED"); err = passlen; goto out_FAIL; } if (memmem (refbuf->data, passlen, source_password, strlen(source_password)) == NULL) { INFO ("password does not match (%ld) \"%s\" (%d) \"%s\"", strlen(source_password), source_password, passlen, refbuf->data); err = -ENOENT; goto out_FAIL; } /* send this non-blocking but if there is only a partial write * then leave to header timeout */ sock_write (node->con->sock, "OK2\r\nicy-caps:11\r\n\r\n"); WARN ("writting to client: OK2\r\nicy-caps:11\r\n\r\n"); refbuf->sync_point = snprintf (refbuf->data, refbuf->len, "POST %s HTTP/1.0\r\n", shoutcast_mount); /* we've checked the password, now send it back for reading headers */ free (source_password); return 0; out_FAIL: free (source_password); return err; }
static http_parser_t *get_relay_response (connection_t *con, const char *mount, const char *server, int ask_for_metadata, const char *auth_header) { ice_config_t *config = config_get_config (); char *server_id = strdup (config->server_id); http_parser_t *parser = NULL; char response [4096]; config_release_config (); /* At this point we may not know if we are relaying an mp3 or vorbis * stream, but only send the icy-metadata header if the relay details * state so (the typical case). It's harmless in the vorbis case. If * we don't send in this header then relay will not have mp3 metadata. */ sock_write (con->sock, "GET %s HTTP/1.0\r\n" "User-Agent: %s\r\n" "Host: %s\r\n" "%s" "%s" "\r\n", mount, server_id, server, ask_for_metadata ? "Icy-MetaData: 1\r\n" : "", auth_header ? auth_header : ""); free (server_id); memset (response, 0, sizeof(response)); if (util_read_header (con->sock, response, 4096, READ_ENTIRE_HEADER) == 0) { INFO0 ("Header read failure"); return NULL; } parser = httpp_create_parser(); httpp_initialize (parser, NULL); if (! httpp_parse_response (parser, response, strlen(response), mount)) { INFO0 ("problem parsing response from relay"); httpp_destroy (parser); return NULL; } return parser; }
/* Actually open the connection and do some http parsing, handle any 302 * responses within here. */ static client_t *open_relay_connection (relay_server *relay) { int redirects = 0; char *server_id = NULL; ice_config_t *config; http_parser_t *parser = NULL; connection_t *con=NULL; char *server = strdup (relay->server); char *mount = strdup (relay->mount); int port = relay->port; char *auth_header; char header[4096]; config = config_get_config (); server_id = strdup (config->server_id); config_release_config (); /* build any authentication header before connecting */ if (relay->username && relay->password) { char *esc_authorisation; unsigned len = strlen(relay->username) + strlen(relay->password) + 2; auth_header = malloc (len); snprintf (auth_header, len, "%s:%s", relay->username, relay->password); esc_authorisation = util_base64_encode(auth_header, len); free(auth_header); len = strlen (esc_authorisation) + 24; auth_header = malloc (len); snprintf (auth_header, len, "Authorization: Basic %s\r\n", esc_authorisation); free(esc_authorisation); } else auth_header = strdup (""); while (redirects < 10) { sock_t streamsock; ICECAST_LOG_INFO("connecting to %s:%d", server, port); streamsock = sock_connect_wto_bind (server, port, relay->bind, 10); if (streamsock == SOCK_ERROR) { ICECAST_LOG_WARN("Failed to connect to %s:%d", server, port); break; } con = connection_create (streamsock, -1, strdup (server)); /* At this point we may not know if we are relaying an mp3 or vorbis * stream, but only send the icy-metadata header if the relay details * state so (the typical case). It's harmless in the vorbis case. If * we don't send in this header then relay will not have mp3 metadata. */ sock_write(streamsock, "GET %s HTTP/1.0\r\n" "User-Agent: %s\r\n" "Host: %s\r\n" "%s" "%s" "\r\n", mount, server_id, server, relay->mp3metadata?"Icy-MetaData: 1\r\n":"", auth_header); memset (header, 0, sizeof(header)); if (util_read_header (con->sock, header, 4096, READ_ENTIRE_HEADER) == 0) { ICECAST_LOG_ERROR("Header read failed for %s (%s:%d%s)", relay->localmount, server, port, mount); break; } parser = httpp_create_parser(); httpp_initialize (parser, NULL); if (! httpp_parse_response (parser, header, strlen(header), relay->localmount)) { ICECAST_LOG_ERROR("Error parsing relay request for %s (%s:%d%s)", relay->localmount, server, port, mount); break; } if (strcmp (httpp_getvar (parser, HTTPP_VAR_ERROR_CODE), "302") == 0) { /* better retry the connection again but with different details */ const char *uri, *mountpoint; int len; uri = httpp_getvar (parser, "location"); ICECAST_LOG_INFO("redirect received %s", uri); if (strncmp (uri, "http://", 7) != 0) break; uri += 7; mountpoint = strchr (uri, '/'); free (mount); if (mountpoint) mount = strdup (mountpoint); else mount = strdup ("/"); len = strcspn (uri, ":/"); port = 80; if (uri [len] == ':') port = atoi (uri+len+1); free (server); server = calloc (1, len+1); strncpy (server, uri, len); connection_close (con); httpp_destroy (parser); con = NULL; parser = NULL; } else { client_t *client = NULL; if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE)) { ICECAST_LOG_ERROR("Error from relay request: %s (%s)", relay->localmount, httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE)); break; } global_lock (); if (client_create (&client, con, parser) < 0) { global_unlock (); /* make sure only the client_destory frees these */ con = NULL; parser = NULL; client_destroy (client); break; } global_unlock (); sock_set_blocking (streamsock, 0); client_set_queue (client, NULL); free (server); free (mount); free (server_id); free (auth_header); return client; } redirects++; } /* failed, better clean up */ free (server); free (mount); free (server_id); free (auth_header); if (con) connection_close (con); if (parser) httpp_destroy (parser); 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; }