int sock_try_connection (int sock, const char *hostname, const unsigned port) { struct sockaddr_in sin, server; char ip[MAX_ADDR_LEN]; if (!hostname || !hostname[0] || port == 0) return -1; memset(&sin, 0, sizeof(struct sockaddr_in)); memset(&server, 0, sizeof(struct sockaddr_in)); if (!resolver_getip(hostname, ip, MAX_ADDR_LEN)) { sock_close (sock); return -1; } if (inet_aton(ip, (struct in_addr *)&sin.sin_addr) == 0) { sock_close(sock); return -1; } memcpy(&server.sin_addr, &sin.sin_addr, sizeof(struct sockaddr_in)); server.sin_family = AF_INET; server.sin_port = htons(port); return connect(sock, (struct sockaddr *)&server, sizeof(server)); }
/* sock_get_localip ** ** gets the local ip address for the machine ** the ip it returns *should* be on the internet. ** in any case, it's as close as we can hope to get ** unless someone has better ideas on how to do this */ char *sock_get_localip(char *buff, int len) { char temp[1024]; if (gethostname(temp, sizeof(temp)) != 0) return NULL; if (resolver_getip(temp, buff, len)) return buff; return NULL; }
/* sock_get_server_socket ** ** create a socket for incoming requests on a specified port and ** interface. if interface is null, listen on all interfaces. ** returns the socket, or SOCK_ERROR on failure */ sock_t sock_get_server_socket(int port, const char *sinterface) { struct sockaddr_in sa; int error, opt; sock_t sock; char ip[MAX_ADDR_LEN]; if (port < 0) return SOCK_ERROR; /* defaults */ memset(&sa, 0, sizeof(sa)); /* set the interface to bind to if specified */ if (sinterface != NULL) { if (!resolver_getip(sinterface, ip, sizeof (ip))) return SOCK_ERROR; if (!inet_aton(ip, &sa.sin_addr)) { return SOCK_ERROR; } else { sa.sin_family = AF_INET; sa.sin_port = htons((short)port); } } else { sa.sin_addr.s_addr = INADDR_ANY; sa.sin_family = AF_INET; sa.sin_port = htons((short)port); } /* get a socket */ sock = sock_open (AF_INET, SOCK_STREAM, 0); if (sock == -1) return SOCK_ERROR; sock_set_cloexec (sock); /* reuse it if we can */ opt = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(int)); /* bind socket to port */ error = bind(sock, (struct sockaddr *)&sa, sizeof (struct sockaddr_in)); if (error == -1) return SOCK_ERROR; return sock; }
/* sock_get_server_socket ** ** create a socket for incoming requests on a specified port and ** interface. if interface is null, listen on all interfaces. ** returns the socket, or SOCK_ERROR on failure */ sock_t sock_get_server_socket(const int port, char *sinterface) { #ifdef HAVE_INET_PTON struct sockaddr_storage sa; #else struct sockaddr_in sa; #endif int family, len, error, opt; sock_t sock; char ip[MAX_ADDR_LEN]; if (port < 0) return SOCK_ERROR; /* defaults */ memset(&sa, 0, sizeof(sa)); family = AF_INET; len = sizeof(struct sockaddr_in); /* set the interface to bind to if specified */ if (sinterface != NULL) { if (!resolver_getip(sinterface, ip, sizeof (ip))) return SOCK_ERROR; #ifdef HAVE_INET_PTON if (inet_pton(AF_INET, ip, &((struct sockaddr_in*)&sa)->sin_addr) > 0) { ((struct sockaddr_in*)&sa)->sin_family = AF_INET; ((struct sockaddr_in*)&sa)->sin_port = htons(port); } else if (inet_pton(AF_INET6, ip, &((struct sockaddr_in6*)&sa)->sin6_addr) > 0) { family = AF_INET6; len = sizeof (struct sockaddr_in6); ((struct sockaddr_in6*)&sa)->sin6_family = AF_INET6; ((struct sockaddr_in6*)&sa)->sin6_port = htons(port); } else { return SOCK_ERROR; } #else if (!inet_aton(ip, &sa.sin_addr)) { return SOCK_ERROR; } else { sa.sin_family = AF_INET; sa.sin_port = htons(port); } #endif } else { ((struct sockaddr_in*)&sa)->sin_addr.s_addr = INADDR_ANY; ((struct sockaddr_in*)&sa)->sin_family = AF_INET; ((struct sockaddr_in*)&sa)->sin_port = htons(port); } /* get a socket */ sock = socket(family, SOCK_STREAM, 0); if (sock == -1) return SOCK_ERROR; /* reuse it if we can */ opt = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(int)); /* bind socket to port */ error = bind(sock, (struct sockaddr *)&sa, len); if (error == -1) return SOCK_ERROR; return sock; }
/* The main loop for each instance. Gets data passed to it from the stream * manager (which gets it from the input module), and streams it to the * specified server */ void *ices_instance_stream(void *arg) { int ret, shouterr; ref_buffer *buffer; char *connip; stream_description *sdsc = arg; instance_t *stream = sdsc->stream; input_module_t *inmod = sdsc->input; int reencoding = (inmod->type == ICES_INPUT_VORBIS) && stream->encode; int encoding = (inmod->type == ICES_INPUT_PCM) && stream->encode; char *stream_name = NULL, *stream_genre = NULL, *stream_description = NULL; char *user = NULL; vorbis_comment_init(&sdsc->vc); sdsc->shout = shout_new(); /* we only support the ice protocol and vorbis streams currently */ shout_set_format(sdsc->shout, SHOUT_FORMAT_VORBIS); //shout_set_protocol(sdsc->shout, SHOUT_PROTOCOL_ICE); shout_set_protocol(sdsc->shout, SHOUT_PROTOCOL_HTTP); signal(SIGPIPE, signal_hup_handler); connip = malloc(16); if(!resolver_getip(stream->hostname, connip, 16)) { LOG_ERROR1("Could not resolve hostname \"%s\"", stream->hostname); free(connip); stream->died = 1; return NULL; } if (!(shout_set_host(sdsc->shout, connip)) == SHOUTERR_SUCCESS) { LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout)); free(connip); stream->died = 1; return NULL; } shout_set_port(sdsc->shout, stream->port); if (!(shout_set_password(sdsc->shout, stream->password)) == SHOUTERR_SUCCESS) { LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout)); free(connip); stream->died = 1; return NULL; } if (stream->user) user = stream->user; else user = "******"; if(shout_set_user(sdsc->shout, user) != SHOUTERR_SUCCESS) { LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout)); free(connip); stream->died = 1; return NULL; } if (!(shout_set_agent(sdsc->shout, VERSIONSTRING)) == SHOUTERR_SUCCESS) { LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout)); free(connip); stream->died = 1; return NULL; } if (!(shout_set_mount(sdsc->shout, stream->mount)) == SHOUTERR_SUCCESS) { LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout)); free(connip); stream->died = 1; return NULL; } /* set the metadata for the stream */ if(stream->stream_name) stream_name = stream->stream_name; else if (ices_config->stream_name) stream_name = ices_config->stream_name; if(stream->stream_description) stream_description = stream->stream_description; else if (ices_config->stream_description) stream_description = ices_config->stream_description; if(stream->stream_genre) stream_genre = stream->stream_genre; else if (ices_config->stream_genre) stream_genre = ices_config->stream_genre; if(stream_name) if (!(shout_set_name(sdsc->shout, stream_name)) == SHOUTERR_SUCCESS) { LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout)); free(connip); stream->died = 1; return NULL; } if (stream_genre) if (!(shout_set_genre(sdsc->shout, stream_genre)) == SHOUTERR_SUCCESS) { LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout)); free(connip); stream->died = 1; return NULL; } if (stream_description) if (!(shout_set_description(sdsc->shout, stream_description)) == SHOUTERR_SUCCESS) { LOG_ERROR1("libshout error: %s\n", shout_get_error(sdsc->shout)); free(connip); stream->died = 1; return NULL; } if(stream->downmix && encoding && stream->channels == 1) { stream->channels = 1; sdsc->downmix = downmix_initialise(); } if(stream->resampleinrate && stream->resampleoutrate && encoding) { stream->samplerate = stream->resampleoutrate; sdsc->resamp = resample_initialise(stream->channels, stream->resampleinrate, stream->resampleoutrate); } if(encoding) { if(inmod->metadata_update) inmod->metadata_update(inmod->internal, &sdsc->vc); sdsc->enc = encode_initialise(stream->channels, stream->samplerate, stream->managed, stream->min_br, stream->nom_br, stream->max_br, stream->quality, stream->serial++, &sdsc->vc); if(!sdsc->enc) { LOG_ERROR0("Failed to configure encoder"); stream->died = 1; return NULL; /* FIXME: probably leaking some memory here */ } } else if(reencoding) sdsc->reenc = reencode_init(stream); if(stream->savefilename != NULL) { stream->savefile = fopen(stream->savefilename, "wb"); if(!stream->savefile) LOG_ERROR2("Failed to open stream save file %s: %s", stream->savefilename, strerror(errno)); else LOG_INFO1("Saving stream to file %s", stream->savefilename); } if((shouterr = shout_open(sdsc->shout)) == SHOUTERR_SUCCESS) { LOG_INFO3("Connected to server: %s:%d%s", shout_get_host(sdsc->shout), shout_get_port(sdsc->shout), shout_get_mount(sdsc->shout)); while(1) { if(stream->buffer_failures > MAX_ERRORS) { LOG_WARN0("Too many errors, shutting down"); break; } buffer = stream_wait_for_data(stream); /* buffer being NULL means that either a fatal error occured, * or we've been told to shut down */ if(!buffer) break; /* If data is NULL or length is 0, we should just skip this one. * Probably, we've been signalled to shut down, and that'll be * caught next iteration. Add to the error count just in case, * so that we eventually break out anyway */ if(!buffer->buf || !buffer->len) { LOG_WARN0("Bad buffer dequeued!"); stream->buffer_failures++; continue; } if(stream->wait_for_critical) { LOG_INFO0("Trying restart on new substream"); stream->wait_for_critical = 0; } ret = process_and_send_buffer(sdsc, buffer); /* No data produced, do nothing */ if(ret == -1) ; /* Fatal error */ else if(ret == -2) { LOG_ERROR0("Serious error, waiting to restart on " "next substream. Stream temporarily suspended."); /* Set to wait until a critical buffer comes through (start of * a new substream, typically), and flush existing queue. */ thread_mutex_lock(&ices_config->flush_lock); stream->wait_for_critical = 1; input_flush_queue(stream->queue, 0); thread_mutex_unlock(&ices_config->flush_lock); } /* Non-fatal shout error */ else if(ret == 0) { LOG_ERROR2("Send error: %s (%s)", shout_get_error(sdsc->shout), strerror(errno)); if(shout_get_errno(sdsc->shout) == SHOUTERR_SOCKET) { int i=0; /* While we're trying to reconnect, don't receive data * to this instance, or we'll overflow once reconnect * succeeds */ thread_mutex_lock(&ices_config->flush_lock); stream->skip = 1; /* Also, flush the current queue */ input_flush_queue(stream->queue, 1); thread_mutex_unlock(&ices_config->flush_lock); while((i < stream->reconnect_attempts || stream->reconnect_attempts==-1) && !ices_config->shutdown) { i++; LOG_WARN0("Trying reconnect after server socket error"); shout_close(sdsc->shout); if((shouterr = shout_open(sdsc->shout)) == SHOUTERR_SUCCESS) { LOG_INFO3("Connected to server: %s:%d%s", shout_get_host(sdsc->shout), shout_get_port(sdsc->shout), shout_get_mount(sdsc->shout)); /* This stream can't restart until the next * logical stream comes along, since the * server won't have any cached headers for * this source/connection. So, don't continue * yet. */ thread_mutex_lock(&ices_config->flush_lock); stream->wait_for_critical = 1; input_flush_queue(stream->queue, 0); thread_mutex_unlock(&ices_config->flush_lock); break; } else { LOG_ERROR3("Failed to reconnect to %s:%d (%s)", shout_get_host(sdsc->shout),shout_get_port(sdsc->shout), shout_get_error(sdsc->shout)); if(i==stream->reconnect_attempts) { LOG_ERROR0("Reconnect failed too many times, " "giving up."); /* We want to die now */ stream->buffer_failures = MAX_ERRORS+1; } else /* Don't try again too soon */ sleep(stream->reconnect_delay); } } stream->skip = 0; } stream->buffer_failures++; } stream_release_buffer(buffer); } } else { LOG_ERROR3("Failed initial connect to %s:%d (%s)", shout_get_host(sdsc->shout),shout_get_port(sdsc->shout), shout_get_error(sdsc->shout)); } shout_close(sdsc->shout); if(stream->savefile != NULL) fclose(stream->savefile); shout_free(sdsc->shout); encode_clear(sdsc->enc); reencode_clear(sdsc->reenc); downmix_clear(sdsc->downmix); resample_clear(sdsc->resamp); vorbis_comment_clear(&sdsc->vc); stream->died = 1; return NULL; }