static int ddns_write(const struct cfg_account *cfg, const char const *newwanip, struct request_buff *buff) { char buf[256]; char *b64_loginpass = NULL; size_t b64_loginpass_size; int n; /* make the update packet */ snprintf(buf, sizeof(buf), "%s:%s", cfgstr_get(&(cfg->username)), cfgstr_get(&(cfg->passwd))); if (util_base64_encode(buf, &b64_loginpass, &b64_loginpass_size) != 0) { /* publish_error_status ?? */ log_error("Unable to encode in base64"); return -1; } n = snprintf(buff->data, sizeof(buff->data), "GET /nic/update?system=dyndns" "&hostname=%s" "&myip=%s" " HTTP/1.0\r\n" "Host: " DDNS_HOST "\r\n" "Authorization: Basic %s\r\n" "User-Agent: " PACKAGE "/" VERSION "\r\n" "Connection: close\r\n" "Pragma: no-cache\r\n\r\n", cfgstr_get(&(cfg->hostname)), newwanip, b64_loginpass); if(n < 0) { log_error("Unable to write data buffer"); return -1; } buff->data_size = (size_t)n; free(b64_loginpass); 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; }
/* 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; }
/* Actually open the connection and do some http parsing, handle any 302 * responses within here. */ static int open_relay_connection (client_t *client, relay_server *relay, relay_server_master *master) { int redirects = 0; http_parser_t *parser = NULL; connection_t *con = &client->connection; char *server = strdup (master->ip); char *mount = strdup (master->mount); int port = master->port, timeout = master->timeout, ask_for_metadata = relay->mp3metadata; char *auth_header = NULL; if (relay->username && relay->password) { char *esc_authorisation; unsigned len = strlen(relay->username) + strlen(relay->password) + 2; DEBUG2 ("using username %s for %s", relay->username, relay->localmount); auth_header = malloc (len); snprintf (auth_header, len, "%s:%s", relay->username, relay->password); esc_authorisation = util_base64_encode(auth_header); 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); } while (redirects < 10) { sock_t streamsock; char *bind = NULL; /* policy decision, we assume a source bind even after redirect, possible option */ if (master->bind) bind = strdup (master->bind); if (bind) INFO4 ("connecting to %s:%d for %s, bound to %s", server, port, relay->localmount, bind); else INFO3 ("connecting to %s:%d for %s", server, port, relay->localmount); con->con_time = time (NULL); relay->in_use = master; streamsock = sock_connect_wto_bind (server, port, bind, timeout); free (bind); if (connection_init (con, streamsock, server) < 0) { WARN2 ("Failed to connect to %s:%d", server, port); break; } parser = get_relay_response (con, mount, server, ask_for_metadata, auth_header); if (parser == NULL) { ERROR4 ("Problem trying to start relay on %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"); INFO1 ("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); parser = NULL; } else { if (httpp_getvar (parser, HTTPP_VAR_ERROR_MESSAGE)) { ERROR3 ("Error from relay request on %s (%s %s)", relay->localmount, master->mount, httpp_getvar(parser, HTTPP_VAR_ERROR_MESSAGE)); client->parser = NULL; break; } sock_set_blocking (streamsock, 0); thread_rwlock_wlock (&relay->source->lock); client->parser = parser; // old parser will be free in the format clear thread_rwlock_unlock (&relay->source->lock); client->connection.discon_time = 0; client->connection.con_time = time (NULL); client_set_queue (client, NULL); free (server); free (mount); free (auth_header); return 0; } redirects++; } /* failed, better clean up */ free (server); free (mount); free (auth_header); if (parser) httpp_destroy (parser); connection_close (con); con->con_time = time (NULL); // sources count needs to drop in such cases if (relay->in_use) relay->in_use->skip = 1; return -1; }
static int update_from_master(ice_config_t *config) { char *master = NULL, *password = NULL, *username= NULL; int port; sock_t mastersock; int ret = 0; char buf[256]; do { char *authheader, *data; relay_server *new_relays = NULL, *cleanup_relays; int len, count = 1; username = strdup ("relay"); if (config->master_password) password = strdup (config->master_password); if (config->master_server) master = strdup (config->master_server); port = config->master_server_port; if (password == NULL || master == NULL || port == 0) break; ret = 1; config_release_config(); mastersock = sock_connect_wto (master, port, 0); if (mastersock == SOCK_ERROR) { WARN0("Relay slave failed to contact master server to fetch stream list"); break; } len = strlen(username) + strlen(password) + 2; authheader = malloc(len); snprintf (authheader, len, "%s:%s", username, password); data = util_base64_encode(authheader); sock_write (mastersock, "GET /admin/streamlist.txt HTTP/1.0\r\n" "Authorization: Basic %s\r\n" "\r\n", data); free(authheader); free(data); if (sock_read_line(mastersock, buf, sizeof(buf)) == 0 || strncmp (buf, "HTTP/1.0 200", 12) != 0) { sock_close (mastersock); WARN0 ("Master rejected streamlist request"); break; } while (sock_read_line(mastersock, buf, sizeof(buf))) { if (!strlen(buf)) break; } while (sock_read_line(mastersock, buf, sizeof(buf))) { relay_server *r; if (!strlen(buf)) continue; DEBUG2 ("read %d from master \"%s\"", count++, buf); r = calloc (1, sizeof (relay_server)); if (r) { r->server = xmlStrdup (master); r->port = port; r->mount = xmlStrdup (buf); r->localmount = xmlStrdup (buf); r->mp3metadata = 1; r->next = new_relays; new_relays = r; } } sock_close (mastersock); thread_mutex_lock (&(config_locks()->relay_lock)); cleanup_relays = update_relays (&global.master_relays, new_relays); relay_check_streams (global.master_relays, cleanup_relays); relay_check_streams (NULL, new_relays); thread_mutex_unlock (&(config_locks()->relay_lock)); } while(0); if (master) free (master); if (username) free (username); if (password) free (password); return ret; }