static xmlNodePtr _dump_stats_to_doc (xmlNodePtr root, const char *show_mount, int hidden) { avl_node *avlnode; xmlNodePtr ret = NULL; ice_config_t *config; thread_mutex_lock(&_stats_mutex); /* general stats first */ avlnode = avl_get_first(_stats.global_tree); while (avlnode) { stats_node_t *stat = avlnode->key; if (stat->hidden <= hidden) xmlNewTextChild (root, NULL, XMLSTR(stat->name), XMLSTR(stat->value)); avlnode = avl_get_next (avlnode); } /* now per mount stats */ avlnode = avl_get_first(_stats.source_tree); config = config_get_config(); __add_authstack(config->authstack, root); config_release_config(); while (avlnode) { stats_source_t *source = (stats_source_t *)avlnode->key; if (source->hidden <= hidden && (show_mount == NULL || strcmp (show_mount, source->source) == 0)) { xmlNodePtr metadata, history; source_t *source_real; mount_proxy *mountproxy; int i; avl_node *avlnode2 = avl_get_first (source->stats_tree); xmlNodePtr xmlnode = xmlNewTextChild (root, NULL, XMLSTR("source"), NULL); xmlSetProp (xmlnode, XMLSTR("mount"), XMLSTR(source->source)); if (ret == NULL) ret = xmlnode; while (avlnode2) { stats_node_t *stat = avlnode2->key; xmlNewTextChild (xmlnode, NULL, XMLSTR(stat->name), XMLSTR(stat->value)); avlnode2 = avl_get_next (avlnode2); } avl_tree_rlock(global.source_tree); source_real = source_find_mount_raw(source->source); history = playlist_render_xspf(source_real->history); if (history) xmlAddChild(xmlnode, history); metadata = xmlNewTextChild(xmlnode, NULL, XMLSTR("metadata"), NULL); if (source_real->format) { for (i = 0; i < source_real->format->vc.comments; i++) __add_metadata(metadata, source_real->format->vc.user_comments[i]); } avl_tree_unlock(global.source_tree); config = config_get_config(); mountproxy = config_find_mount(config, source->source, MOUNT_TYPE_NORMAL); __add_authstack(mountproxy->authstack, xmlnode); config_release_config(); } avlnode = avl_get_next (avlnode); } thread_mutex_unlock(&_stats_mutex); return ret; }
/* * @return A string containing human-readable status text. MUST BE free()d by caller */ char * get_status_text() { pstr_t *pstr = pstr_new(); s_config *config; t_auth_serv *auth_server; t_client *sublist, *current; int count; time_t uptime = 0; unsigned int days = 0, hours = 0, minutes = 0, seconds = 0; t_trusted_mac *p; t_offline_client *oc_list; pstr_cat(pstr, "WiFiDog status\n\n"); uptime = time(NULL) - started_time; days = (unsigned int)uptime / (24 * 60 * 60); uptime -= days * (24 * 60 * 60); hours = (unsigned int)uptime / (60 * 60); uptime -= hours * (60 * 60); minutes = (unsigned int)uptime / 60; uptime -= minutes * 60; seconds = (unsigned int)uptime; pstr_cat(pstr, "Version: " VERSION "\n"); pstr_append_sprintf(pstr, "Uptime: %ud %uh %um %us\n", days, hours, minutes, seconds); pstr_cat(pstr, "Has been restarted: "); if (restart_orig_pid) { pstr_append_sprintf(pstr, "yes (from PID %d)\n", restart_orig_pid); } else { pstr_cat(pstr, "no\n"); } pstr_append_sprintf(pstr, "Internet Connectivity: %s\n", (is_online()? "yes" : "no")); pstr_append_sprintf(pstr, "Auth server reachable: %s\n", (is_auth_online()? "yes" : "no")); pstr_append_sprintf(pstr, "Clients served this session: %lu\n\n", served_this_session); LOCK_CLIENT_LIST(); count = client_list_dup(&sublist); UNLOCK_CLIENT_LIST(); current = sublist; pstr_append_sprintf(pstr, "%d clients " "connected.\n", count); count = 1; while (current != NULL) { pstr_append_sprintf(pstr, "\nClient %d status [%d]\n", count, current->is_online); pstr_append_sprintf(pstr, " IP: %s MAC: %s\n", current->ip, current->mac); pstr_append_sprintf(pstr, " Token: %s\n", current->token); pstr_append_sprintf(pstr, " First Login: %lld\n", (long long)current->first_login); pstr_append_sprintf(pstr, " Name: %s\n", current->name != NULL?current->name:"null"); pstr_append_sprintf(pstr, " Downloaded: %llu\n Uploaded: %llu\n", current->counters.incoming, current->counters.outgoing); count++; current = current->next; } client_list_destroy(sublist); LOCK_OFFLINE_CLIENT_LIST(); pstr_append_sprintf(pstr, "%d clients " "unconnected.\n", offline_client_number()); oc_list = client_get_first_offline_client(); while(oc_list != NULL) { pstr_append_sprintf(pstr, " IP: %s MAC: %s Last Login: %lld Hit Counts: %d Client Type: %d Temp Passed: %d\n", oc_list->ip, oc_list->mac, (long long)oc_list->last_login, oc_list->hit_counts, oc_list->client_type, oc_list->temp_passed); oc_list = oc_list->next; } UNLOCK_OFFLINE_CLIENT_LIST(); config = config_get_config(); LOCK_CONFIG(); if (config->trustedmaclist != NULL) { pstr_cat(pstr, "\nTrusted MAC addresses:\n"); for (p = config->trustedmaclist; p != NULL; p = p->next) { pstr_append_sprintf(pstr, " %s\n", p->mac); } } pstr_cat(pstr, "\nAuthentication servers:\n"); for (auth_server = config->auth_servers; auth_server != NULL; auth_server = auth_server->next) { pstr_append_sprintf(pstr, " Host: %s (%s)\n", auth_server->authserv_hostname, auth_server->last_ip); } UNLOCK_CONFIG(); return pstr_to_string(pstr); }
static void *start_relay_stream (void *arg) { client_t *client = arg; relay_server *relay; source_t *src; int failed = 1, sources; global_lock(); sources = ++global.sources; stats_event_args (NULL, "sources", "%d", global.sources); global_unlock(); /* set the start time, because we want to decrease the sources on all failures */ client->connection.con_time = time (NULL); do { ice_config_t *config = config_get_config(); mount_proxy *mountinfo; relay = client->shared_data; src = relay->source; thread_rwlock_wlock (&src->lock); src->flags |= SOURCE_PAUSE_LISTENERS; if (sources > config->source_limit) { config_release_config(); WARN1 ("starting relayed mountpoint \"%s\" requires a higher sources limit", relay->localmount); break; } config_release_config(); INFO1("Starting relayed source at mountpoint \"%s\"", relay->localmount); if (open_relay (relay) < 0) break; if (connection_complete_source (src) < 0) { WARN1 ("Failed to complete initialisation on %s", relay->localmount); break; } stats_event_inc (NULL, "source_relay_connections"); source_init (src); config = config_get_config(); mountinfo = config_find_mount (config, src->mount); source_update_settings (config, src, mountinfo); INFO1 ("source %s is ready to start", src->mount); config_release_config(); failed = 0; } while (0); client->ops = &relay_client_ops; client->schedule_ms = timing_get_time(); if (failed) { /* failed to start any connection, better clean up and reset */ if (relay->on_demand == 0) { yp_remove (relay->localmount); src->yp_public = -1; } relay->in_use = NULL; INFO2 ("listener count remaining on %s is %d", src->mount, src->listeners); src->flags &= ~(SOURCE_PAUSE_LISTENERS|SOURCE_RUNNING); } thread_rwlock_unlock (&src->lock); thread_spin_lock (&relay_start_lock); relays_connecting--; thread_spin_unlock (&relay_start_lock); client->flags |= CLIENT_ACTIVE; worker_wakeup (client->worker); return NULL; }
void connection_accept_loop (void) { connection_t *con; ice_config_t *config; int duration = 300; config = config_get_config (); get_ssl_certificate (config); config_release_config (); while (global.running == ICE_RUNNING) { con = _accept_connection (duration); if (con) { client_queue_t *node; ice_config_t *config; client_t *client = NULL; listener_t *listener; global_lock(); if (client_create (&client, con, NULL) < 0) { global_unlock(); client_send_403 (client, "Icecast connection limit reached"); /* don't be too eager as this is an imposed hard limit */ thread_sleep (400000); continue; } /* setup client for reading incoming http */ client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] = '\000'; if (sock_set_blocking (client->con->sock, 0) || sock_set_nodelay (client->con->sock)) { global_unlock(); WARN0 ("failed to set tcp options on client connection, dropping"); client_destroy (client); continue; } node = calloc (1, sizeof (client_queue_t)); if (node == NULL) { global_unlock(); client_destroy (client); continue; } node->client = client; config = config_get_config(); listener = config_get_listen_sock (config, client->con); if (listener) { if (listener->shoutcast_compat) node->shoutcast = 1; if (listener->ssl && ssl_ok) connection_uses_ssl (client->con); if (listener->shoutcast_mount) node->shoutcast_mount = strdup (listener->shoutcast_mount); } global_unlock(); config_release_config(); _add_request_queue (node); stats_event_inc (NULL, "connections"); duration = 5; } else { if (_req_queue == NULL) duration = 300; /* use longer timeouts when nothing waiting */ } process_request_queue (); } /* Give all the other threads notification to shut down */ thread_cond_broadcast(&global.shutdown_cond); /* wait for all the sources to shutdown */ thread_rwlock_wlock(&_source_shutdown_rwlock); thread_rwlock_unlock(&_source_shutdown_rwlock); }
static auth_result url_remove_listener (auth_client *auth_user) { client_t *client = auth_user->client; auth_t *auth = client->auth; auth_url *url = auth->state; time_t duration = time(NULL) - client->con->con_time; char *username, *password, *mount, *server; const char *mountreq; ice_config_t *config; int port; char *userpwd = NULL, post [4096]; const char *agent; char *user_agent, *ipaddr; if (url->removeurl == NULL) return AUTH_OK; config = config_get_config (); server = util_url_escape (config->hostname); port = config->port; config_release_config (); agent = httpp_getvar (client->parser, "user-agent"); if (agent) user_agent = util_url_escape (agent); else user_agent = strdup ("-"); if (client->username) username = util_url_escape (client->username); else username = strdup (""); if (client->password) password = util_url_escape (client->password); else password = strdup (""); /* get the full uri (with query params if available) */ mountreq = httpp_getvar (client->parser, HTTPP_VAR_RAWURI); if (mountreq == NULL) mountreq = httpp_getvar (client->parser, HTTPP_VAR_URI); mount = util_url_escape (mountreq); ipaddr = util_url_escape (client->con->ip); snprintf (post, sizeof (post), "action=listener_remove&server=%s&port=%d&client=%lu&mount=%s" "&user=%s&pass=%s&duration=%lu&ip=%s&agent=%s", server, port, client->con->id, mount, username, password, (long unsigned)duration, ipaddr, user_agent); free (server); free (mount); free (username); free (password); free (ipaddr); free (user_agent); if (strchr (url->removeurl, '@') == NULL) { if (url->userpwd) curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd); else { /* auth'd requests may not have a user/pass, but may use query args */ if (client->username && client->password) { size_t len = strlen (client->username) + strlen (client->password) + 2; userpwd = malloc (len); snprintf (userpwd, len, "%s:%s", client->username, client->password); curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd); } else curl_easy_setopt (url->handle, CURLOPT_USERPWD, ""); } } else { /* url has user/pass but libcurl may need to clear any existing settings */ curl_easy_setopt (url->handle, CURLOPT_USERPWD, ""); } curl_easy_setopt (url->handle, CURLOPT_URL, url->removeurl); curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post); curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user); if (curl_easy_perform (url->handle)) WARN2 ("auth to server %s failed with %s", url->removeurl, url->errormsg); free (userpwd); return AUTH_OK; }
/* Called when activating a source. Verifies that the source count is not * exceeded and applies any initial parameters. */ int connection_complete_source (source_t *source, int response) { ice_config_t *config = config_get_config(); global_lock (); DEBUG1 ("sources count is %d", global.sources); if (global.sources < config->source_limit) { char *contenttype; mount_proxy *mountinfo; format_type_t format_type; /* setup format handler */ contenttype = httpp_getvar (source->parser, "content-type"); if (contenttype != NULL) { format_type = format_get_type (contenttype); if (format_type == FORMAT_ERROR) { global_unlock(); config_release_config(); if (response) { client_send_404 (source->client, "Content-type not supported"); source->client = NULL; } WARN1("Content-type \"%s\" not supported, dropping source", contenttype); return -1; } } else { WARN0("No content-type header, falling back to backwards compatibility mode " "for icecast 1.x relays. Assuming content is mp3."); format_type = FORMAT_TYPE_GENERIC; } if (format_get_plugin (format_type, source) < 0) { global_unlock(); config_release_config(); if (response) { client_send_404 (source->client, "internal format allocation problem"); source->client = NULL; } WARN1 ("plugin format failed for \"%s\"", source->mount); return -1; } global.sources++; stats_event_args (NULL, "sources", "%d", global.sources); global_unlock(); source->running = 1; mountinfo = config_find_mount (config, source->mount); if (mountinfo == NULL) source_update_settings (config, source, mountinfo); source_recheck_mounts (); config_release_config(); source->shutdown_rwlock = &_source_shutdown_rwlock; DEBUG0 ("source is ready to start"); return 0; } WARN1("Request to add source when maximum source limit " "reached %d", global.sources); global_unlock(); config_release_config(); if (response) { client_send_404 (source->client, "too many sources connected"); source->client = NULL; } return -1; }
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; }
int fserve_setup_client_fb (client_t *client, fbinfo *finfo) { fh_node *fh = &no_file; int ret = 0; if (finfo) { mount_proxy *minfo; if (finfo->flags & FS_FALLBACK && finfo->limit == 0) return -1; avl_tree_wlock (fh_cache); fh = find_fh (finfo); minfo = config_find_mount (config_get_config(), finfo->mount); if (fh) { thread_mutex_lock (&fh->lock); avl_tree_unlock (fh_cache); client->shared_data = NULL; if (minfo) { if (minfo->max_listeners >= 0 && fh->refcount > minfo->max_listeners) { thread_mutex_unlock (&fh->lock); config_release_config(); return client_send_403redirect (client, finfo->mount, "max listeners reached"); } if (check_duplicate_logins (finfo->mount, fh->clients, client, minfo->auth) == 0) { thread_mutex_unlock (&fh->lock); config_release_config(); return client_send_403 (client, "Account already in use"); } } config_release_config(); } else { if (minfo && minfo->max_listeners == 0) { avl_tree_unlock (fh_cache); config_release_config(); client->shared_data = NULL; return client_send_403redirect (client, finfo->mount, "max listeners reached"); } config_release_config(); fh = open_fh (finfo); if (fh == NULL) return client_send_404 (client, NULL); if (fh->finfo.limit) DEBUG2 ("request for throttled file %s (bitrate %d)", fh->finfo.mount, fh->finfo.limit*8); } if (fh->finfo.limit) { client->timer_start = client->worker->current_time.tv_sec; if (client->connection.sent_bytes == 0) client->timer_start -= 2; client->counter = 0; global_reduce_bitrate_sampling (global.out_bitrate); } } else { if (client->mount && (client->flags & CLIENT_AUTHENTICATED) && (client->respcode >= 300 || client->respcode < 200)) { fh = calloc (1, sizeof (no_file)); fh->finfo.mount = strdup (client->mount); fh->finfo.flags |= FS_DELETE; fh->refcount = 1; fh->f = SOCK_ERROR; thread_mutex_create (&fh->lock); } thread_mutex_lock (&fh->lock); } client->mount = fh->finfo.mount; if (fh->finfo.type == FORMAT_TYPE_UNDEFINED) { if (client->respcode == 0) { client->refbuf->len = 0; ret = format_general_headers (fh->format, client); } } else { if (fh->format->create_client_data && client->format_data == NULL) ret = fh->format->create_client_data (fh->format, client); if (fh->format->write_buf_to_client) client->check_buffer = fh->format->write_buf_to_client; } if (ret < 0) { thread_mutex_unlock (&fh->lock); return client_send_416 (client); } fh_add_client (fh, client); thread_mutex_unlock (&fh->lock); client->shared_data = fh; if (client->check_buffer == NULL) client->check_buffer = format_generic_write_to_client; client->ops = &buffer_content_ops; client->flags &= ~CLIENT_HAS_INTRO_CONTENT; client->flags |= CLIENT_IN_FSERVE; if (client->flags & CLIENT_ACTIVE) { client->schedule_ms = client->worker->time_ms; if (finfo && finfo->flags & FS_FALLBACK) return 0; // prevent a recursive loop return client->ops->process (client); } else { worker_t *worker = client->worker; ret = (fh->finfo.limit) ? 0 : -1; client->flags |= CLIENT_ACTIVE; worker_wakeup (worker); /* worker may of already processed client but make sure */ } return ret; }
/** Initialize the firewall rules. */ int iptables_fw_init(void) { s_config *config; char * gw_interface = NULL; char * gw_address = NULL; char * gw_iprange = NULL; int gw_port = 0; int traffic_control; int set_mss, mss_value; t_MAC *pt; t_MAC *pb; t_MAC *pa; int rc = 0; int macmechanism; LOCK_CONFIG(); config = config_get_config(); gw_interface = safe_strdup(config->gw_interface); /* must free */ gw_address = safe_strdup(config->gw_address); /* must free */ gw_iprange = safe_strdup(config->gw_iprange); /* must free */ gw_port = config->gw_port; pt = config->trustedmaclist; pb = config->blockedmaclist; pa = config->allowedmaclist; macmechanism = config->macmechanism; set_mss = config->set_mss; mss_value = config->mss_value; traffic_control = config->traffic_control; FW_MARK_BLOCKED = config->FW_MARK_BLOCKED; FW_MARK_TRUSTED = config->FW_MARK_TRUSTED; FW_MARK_AUTHENTICATED = config->FW_MARK_AUTHENTICATED; UNLOCK_CONFIG(); /* Set up packet marking methods */ rc |= _iptables_init_marks(); rc |= _iptables_check_mark_masking(); /* * ************************************** * Set up mangle table chains and rules * */ /* Create new chains in the mangle table */ rc |= iptables_do_command("-t mangle -N " CHAIN_TRUSTED); /* for marking trusted packets */ rc |= iptables_do_command("-t mangle -N " CHAIN_BLOCKED); /* for marking blocked packets */ rc |= iptables_do_command("-t mangle -N " CHAIN_INCOMING); /* for counting incoming packets */ rc |= iptables_do_command("-t mangle -N " CHAIN_OUTGOING); /* for marking authenticated packets, and for counting outgoing packets */ /* Assign jumps to these new chains */ rc |= iptables_do_command("-t mangle -I PREROUTING 1 -i %s -s %s -j " CHAIN_OUTGOING, gw_interface, gw_iprange); rc |= iptables_do_command("-t mangle -I PREROUTING 2 -i %s -s %s -j " CHAIN_BLOCKED, gw_interface, gw_iprange); rc |= iptables_do_command("-t mangle -I PREROUTING 3 -i %s -s %s -j " CHAIN_TRUSTED, gw_interface, gw_iprange); rc |= iptables_do_command("-t mangle -I POSTROUTING 1 -o %s -d %s -j " CHAIN_INCOMING, gw_interface, gw_iprange); /* Rules to mark as trusted MAC address packets in mangle PREROUTING */ for (; pt != NULL; pt = pt->next) { rc |= iptables_trust_mac(pt->mac); } /* Rules to mark as blocked MAC address packets in mangle PREROUTING */ if (MAC_BLOCK == macmechanism) { /* with the MAC_BLOCK mechanism, * MAC's on the block list are marked as blocked; * everything else passes */ for (; pb != NULL; pb = pb->next) { rc |= iptables_block_mac(pb->mac); } } else if (MAC_ALLOW == macmechanism) { /* with the MAC_ALLOW mechanism, * MAC's on the allow list pass; * everything else is to be marked as blocked */ /* So, append at end of chain a rule to mark everything blocked */ rc |= iptables_do_command("-t mangle -A " CHAIN_BLOCKED " -j MARK %s 0x%x", markop, FW_MARK_BLOCKED); /* But insert at beginning of chain rules to pass allowed MAC's */ for (; pa != NULL; pa = pa->next) { rc |= iptables_allow_mac(pa->mac); } } else { debug(LOG_ERR, "Unknown MAC mechanism: %d", macmechanism); rc = -1; } /* Set up for traffic control */ if (traffic_control) { rc |= tc_init_tc(); } /* * End of mangle table chains and rules ************************************** */ /* * ************************************** * Set up nat table chains and rules * */ /* Create new chains in nat table */ rc |= iptables_do_command("-t nat -N " CHAIN_OUTGOING); /* * nat PREROUTING chain */ /* packets coming in on gw_interface jump to CHAIN_OUTGOING */ rc |= iptables_do_command("-t nat -I PREROUTING -i %s -s %s -j " CHAIN_OUTGOING, gw_interface, gw_iprange); /* CHAIN_OUTGOING, packets marked TRUSTED ACCEPT */ rc |= iptables_do_command("-t nat -A " CHAIN_OUTGOING " -m mark --mark 0x%x%s -j ACCEPT", FW_MARK_TRUSTED, markmask); /* CHAIN_OUTGOING, packets marked AUTHENTICATED ACCEPT */ rc |= iptables_do_command("-t nat -A " CHAIN_OUTGOING " -m mark --mark 0x%x%s -j ACCEPT",FW_MARK_AUTHENTICATED, markmask); /* CHAIN_OUTGOING, append the "preauthenticated-users" ruleset */ rc |= _iptables_append_ruleset("nat", "preauthenticated-users", CHAIN_OUTGOING); /* CHAIN_OUTGOING, packets for tcp port 80, redirect to gw_port on primary address for the iface */ rc |= iptables_do_command("-t nat -A " CHAIN_OUTGOING " -p tcp --dport 80 -j DNAT --to-destination %s:%d", gw_address, gw_port); /* CHAIN_OUTGOING, other packets ACCEPT */ rc |= iptables_do_command("-t nat -A " CHAIN_OUTGOING " -j ACCEPT"); /* * End of nat table chains and rules ************************************** */ /* * ************************************** * Set up filter table chains and rules * */ /* Create new chains in the filter table */ rc |= iptables_do_command("-t filter -N " CHAIN_TO_INTERNET); rc |= iptables_do_command("-t filter -N " CHAIN_TO_ROUTER); rc |= iptables_do_command("-t filter -N " CHAIN_AUTHENTICATED); rc |= iptables_do_command("-t filter -N " CHAIN_TRUSTED); rc |= iptables_do_command("-t filter -N " CHAIN_TRUSTED_TO_ROUTER); /* * filter INPUT chain */ /* packets coming in on gw_interface jump to CHAIN_TO_ROUTER */ rc |= iptables_do_command("-t filter -I INPUT -i %s -s %s -j " CHAIN_TO_ROUTER, gw_interface, gw_iprange); /* CHAIN_TO_ROUTER packets marked BLOCKED DROP */ rc |= iptables_do_command("-t filter -A " CHAIN_TO_ROUTER " -m mark --mark 0x%x%s -j DROP", FW_MARK_BLOCKED, markmask); /* CHAIN_TO_ROUTER, invalid packets DROP */ rc |= iptables_do_command("-t filter -A " CHAIN_TO_ROUTER " -m conntrack --ctstate INVALID -j DROP"); /* CHAIN_TO_ROUTER, related and established packets ACCEPT */ rc |= iptables_do_command("-t filter -A " CHAIN_TO_ROUTER " -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT"); /* CHAIN_TO_ROUTER, bogus SYN packets DROP */ rc |= iptables_do_command("-t filter -A " CHAIN_TO_ROUTER " -p tcp --tcp-flags SYN SYN \\! --tcp-option 2 -j DROP"); /* CHAIN_TO_ROUTER, packets to HTTP listening on gw_port on router ACCEPT */ rc |= iptables_do_command("-t filter -A " CHAIN_TO_ROUTER " -p tcp --dport %d -j ACCEPT", gw_port); /* CHAIN_TO_ROUTER, packets marked TRUSTED: */ /* if trusted-users-to-router ruleset is empty: * use empty ruleset policy * else: * jump to CHAIN_TRUSTED_TO_ROUTER, and load and use users-to-router ruleset */ if (is_empty_ruleset("trusted-users-to-router")) { rc |= iptables_do_command("-t filter -A " CHAIN_TO_ROUTER " -m mark --mark 0x%x%s -j %s", FW_MARK_TRUSTED, markmask, get_empty_ruleset_policy("trusted-users-to-router")); } else { rc |= iptables_do_command("-t filter -A " CHAIN_TO_ROUTER " -m mark --mark 0x%x%s -j " CHAIN_TRUSTED_TO_ROUTER, FW_MARK_TRUSTED, markmask); /* CHAIN_TRUSTED_TO_ROUTER, related and established packets ACCEPT */ rc |= iptables_do_command("-t filter -A " CHAIN_TRUSTED_TO_ROUTER " -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT"); /* CHAIN_TRUSTED_TO_ROUTER, append the "trusted-users-to-router" ruleset */ rc |= _iptables_append_ruleset("filter", "trusted-users-to-router", CHAIN_TRUSTED_TO_ROUTER); /* CHAIN_TRUSTED_TO_ROUTER, any packets not matching that ruleset REJECT */ rc |= iptables_do_command("-t filter -A " CHAIN_TRUSTED_TO_ROUTER " -j REJECT --reject-with icmp-port-unreachable"); } /* CHAIN_TO_ROUTER, other packets: */ /* if users-to-router ruleset is empty: * use empty ruleset policy * else: * load and use users-to-router ruleset */ if (is_empty_ruleset("users-to-router")) { rc |= iptables_do_command("-t filter -A " CHAIN_TO_ROUTER " -j %s", get_empty_ruleset_policy("users-to-router")); } else { /* CHAIN_TO_ROUTER, append the "users-to-router" ruleset */ rc |= _iptables_append_ruleset("filter", "users-to-router", CHAIN_TO_ROUTER); /* everything else, REJECT */ rc |= iptables_do_command("-t filter -A " CHAIN_TO_ROUTER " -j REJECT --reject-with icmp-port-unreachable"); } /* * filter FORWARD chain */ /* packets coming in on gw_interface jump to CHAIN_TO_INTERNET */ rc |= iptables_do_command("-t filter -I FORWARD -i %s -s %s -j " CHAIN_TO_INTERNET, gw_interface, gw_iprange); /* CHAIN_TO_INTERNET packets marked BLOCKED DROP */ rc |= iptables_do_command("-t filter -A " CHAIN_TO_INTERNET " -m mark --mark 0x%x%s -j DROP", FW_MARK_BLOCKED, markmask); /* CHAIN_TO_INTERNET, invalid packets DROP */ rc |= iptables_do_command("-t filter -A " CHAIN_TO_INTERNET " -m conntrack --ctstate INVALID -j DROP"); /* CHAIN_TO_INTERNET, deal with MSS */ if (set_mss) { /* XXX this mangles, so 'should' be done in the mangle POSTROUTING chain. * However OpenWRT standard S35firewall does it in filter FORWARD, * and since we are pre-empting that chain here, we put it in */ if (mss_value > 0) { /* set specific MSS value */ rc |= iptables_do_command("-t filter -A " CHAIN_TO_INTERNET " -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss %d", mss_value); } else { /* allow MSS as large as possible */ rc |= iptables_do_command("-t filter -A " CHAIN_TO_INTERNET " -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu"); } } /* CHAIN_TO_INTERNET, packets marked TRUSTED: */ /* if trusted-users ruleset is empty: * use empty ruleset policy * else: * jump to CHAIN_TRUSTED, and load and use trusted-users ruleset */ if (is_empty_ruleset("trusted-users")) { rc |= iptables_do_command("-t filter -A " CHAIN_TO_INTERNET " -m mark --mark 0x%x%s -j %s", FW_MARK_TRUSTED, markmask, get_empty_ruleset_policy("trusted-users")); } else { rc |= iptables_do_command("-t filter -A " CHAIN_TO_INTERNET " -m mark --mark 0x%x%s -j " CHAIN_TRUSTED, FW_MARK_TRUSTED, markmask); /* CHAIN_TRUSTED, related and established packets ACCEPT */ rc |= iptables_do_command("-t filter -A " CHAIN_TRUSTED " -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT"); /* CHAIN_TRUSTED, append the "trusted-users" ruleset */ rc |= _iptables_append_ruleset("filter", "trusted-users", CHAIN_TRUSTED); /* CHAIN_TRUSTED, any packets not matching that ruleset REJECT */ rc |= iptables_do_command("-t filter -A " CHAIN_TRUSTED " -j REJECT --reject-with icmp-port-unreachable"); } /* CHAIN_TO_INTERNET, packets marked AUTHENTICATED: */ /* if authenticated-users ruleset is empty: * use empty ruleset policy * else: * jump to CHAIN_AUTHENTICATED, and load and use authenticated-users ruleset */ if (is_empty_ruleset("authenticated-users")) { rc |= iptables_do_command("-t filter -A " CHAIN_TO_INTERNET " -m mark --mark 0x%x%s -j %s", FW_MARK_AUTHENTICATED, markmask, get_empty_ruleset_policy("authenticated-users")); } else { rc |= iptables_do_command("-t filter -A " CHAIN_TO_INTERNET " -m mark --mark 0x%x%s -j " CHAIN_AUTHENTICATED, FW_MARK_AUTHENTICATED, markmask); /* CHAIN_AUTHENTICATED, related and established packets ACCEPT */ rc |= iptables_do_command("-t filter -A " CHAIN_AUTHENTICATED " -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT"); /* CHAIN_AUTHENTICATED, append the "authenticated-users" ruleset */ rc |= _iptables_append_ruleset("filter", "authenticated-users", CHAIN_AUTHENTICATED); /* CHAIN_AUTHENTICATED, any packets not matching that ruleset REJECT */ rc |= iptables_do_command("-t filter -A " CHAIN_AUTHENTICATED " -j REJECT --reject-with icmp-port-unreachable"); } /* CHAIN_TO_INTERNET, other packets: */ /* if preauthenticated-users ruleset is empty: * use empty ruleset policy * else: * load and use authenticated-users ruleset */ if (is_empty_ruleset("preauthenticated-users")) { rc |= iptables_do_command("-t filter -A " CHAIN_TO_INTERNET " -j %s ", get_empty_ruleset_policy("preauthenticated-users")); } else { rc |= _iptables_append_ruleset("filter", "preauthenticated-users", CHAIN_TO_INTERNET); } /* CHAIN_TO_INTERNET, all other packets REJECT */ rc |= iptables_do_command("-t filter -A " CHAIN_TO_INTERNET " -j REJECT --reject-with icmp-port-unreachable"); /* * End of filter table chains and rules ************************************** */ free(gw_interface); free(gw_iprange); free(gw_address); return rc; }
/**@internal * Main execution loop */ static void main_loop(void) { int result = 0; pthread_t tid; s_config *config; config = config_get_config(); /* Set the time when nodogsplash started */ if (!started_time) { debug(LOG_INFO, "Setting started_time"); started_time = time(NULL); } else if (started_time < MINIMUM_STARTED_TIME) { debug(LOG_WARNING, "Detected possible clock skew - re-setting started_time"); started_time = time(NULL); } /* If we don't have the Gateway IP address, get it. Exit on failure. */ if (!config->gw_address) { debug(LOG_DEBUG, "Finding IP address of %s", config->gw_interface); if ((config->gw_address = get_iface_ip(config->gw_interface)) == NULL) { debug(LOG_ERR, "Could not get IP address information of %s, exiting...", config->gw_interface); exit(1); } } if ((config->gw_mac = get_iface_mac(config->gw_interface)) == NULL) { debug(LOG_ERR, "Could not get MAC address information of %s, exiting...", config->gw_interface); exit(1); } debug(LOG_NOTICE, "Detected gateway %s at %s (%s)", config->gw_interface, config->gw_address, config->gw_mac); /* Initializes the web server */ if ((webserver = MHD_start_daemon( MHD_USE_EPOLL_INTERNALLY, config->gw_port, NULL, NULL, libmicrohttpd_cb, NULL, MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 120, MHD_OPTION_LISTENING_ADDRESS_REUSE, 1, MHD_OPTION_END)) == NULL) { debug(LOG_ERR, "Could not create web server: %s", strerror(errno)); exit(1); } /* TODO: set listening socket */ debug(LOG_NOTICE, "Created web server on %s:%d", config->gw_address, config->gw_port); /* httpdAddCContent(webserver, "/", "", 0, NULL, http_nodogsplash_callback_index); httpdAddCWildcardContent(webserver, config->authdir, NULL, http_nodogsplash_callback_auth); httpdAddCWildcardContent(webserver, config->denydir, NULL, http_nodogsplash_callback_deny); httpdAddC404Content(webserver, http_nodogsplash_callback_404); */ /* Reset the firewall (cleans it, in case we are restarting after nodogsplash crash) */ fw_destroy(); /* Then initialize it */ debug(LOG_NOTICE, "Initializing firewall rules"); if (fw_init() != 0) { debug(LOG_ERR, "Error initializing firewall rules! Cleaning up"); fw_destroy(); debug(LOG_ERR, "Exiting because of error initializing firewall rules"); exit(1); } /* Start client statistics and timeout clean-up thread */ result = pthread_create(&tid_client_check, NULL, thread_client_timeout_check, NULL); if (result != 0) { debug(LOG_ERR, "FATAL: Failed to create thread_client_timeout_check - exiting"); termination_handler(0); } pthread_detach(tid_client_check); /* Start control thread */ result = pthread_create(&tid, NULL, thread_ndsctl, (void *)(config->ndsctl_sock)); if (result != 0) { debug(LOG_ERR, "FATAL: Failed to create thread_ndsctl - exiting"); termination_handler(1); } result = pthread_join(tid, NULL); if (result) { debug(LOG_INFO, "Failed to wait for nodogsplash thread."); } MHD_stop_daemon(webserver); termination_handler(result); }
/* client has requested a file, so check for it and send the file. Do not * refer to the client_t afterwards. return 0 for success, -1 on error. */ int fserve_client_create (client_t *httpclient, const char *path) { struct stat file_buf; char *fullpath; int m3u_requested = 0, m3u_file_available = 1; int xspf_requested = 0, xspf_file_available = 1; int ret = -1; ice_config_t *config; fbinfo finfo; char fsize[20]; fullpath = util_get_path_from_normalised_uri (path, 0); DEBUG2 ("checking for file %s (%s)", path, fullpath); if (strcmp (util_get_extension (fullpath), "m3u") == 0) m3u_requested = 1; if (strcmp (util_get_extension (fullpath), "xspf") == 0) xspf_requested = 1; /* check for the actual file */ if (stat (fullpath, &file_buf) != 0) { /* the m3u can be generated, but send an m3u file if available */ if (m3u_requested == 0 && xspf_requested == 0) { if (redirect_client (path, httpclient) == 0) { if ((httpclient->flags & CLIENT_SKIP_ACCESSLOG) == 0) WARN2 ("req for file \"%s\" %s", fullpath, strerror (errno)); ret = client_send_404 (httpclient, "The file you requested could not be found"); } free (fullpath); return ret; } m3u_file_available = 0; xspf_file_available = 0; } client_set_queue (httpclient, NULL); httpclient->refbuf = refbuf_new (4096); if (m3u_requested && m3u_file_available == 0) { const char *host = httpp_getvar (httpclient->parser, "host"), *args = httpp_getvar (httpclient->parser, HTTPP_VAR_QUERYARGS), *at = "", *user = "", *pass =""; char *sourceuri = strdup (path); char *dot = strrchr (sourceuri, '.'); char *protocol = not_ssl_connection (&httpclient->connection) ? "http" : "https"; const char *agent = httpp_getvar (httpclient->parser, "user-agent"); int x; char scratch[1000]; if (agent) { if (strstr (agent, "QTS") || strstr (agent, "QuickTime")) protocol = "icy"; } /* at least a couple of players (fb2k/winamp) are reported to send a * host header but without the port number. So if we are missing the * port then lets treat it as if no host line was sent */ if (host && strchr (host, ':') == NULL) host = NULL; *dot = 0; if (httpclient->username && httpclient->password) { at = "@"; user = httpclient->username; pass = httpclient->password; } httpclient->respcode = 200; if (host == NULL) { config = config_get_config(); x = snprintf (scratch, sizeof scratch, "%s://%s%s%s%s%s:%d%s%s\r\n", protocol, user, at[0]?":":"", pass, at, config->hostname, config->port, sourceuri, args?args:""); config_release_config(); } else { x = snprintf (scratch, sizeof scratch, "%s://%s%s%s%s%s%s%s\r\n", protocol, user, at[0]?":":"", pass, at, host, sourceuri, args?args:""); } snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.0 200 OK\r\n" "Content-Length: %d\r\n" "%s\r\n" "Content-Type: audio/x-mpegurl\r\n\r\n%s", x, client_keepalive_header (httpclient), scratch); httpclient->refbuf->len = strlen (httpclient->refbuf->data); free (sourceuri); free (fullpath); return fserve_setup_client_fb (httpclient, NULL); } if (xspf_requested && xspf_file_available == 0) { xmlDocPtr doc; char *reference = strdup (path); char *eol = strrchr (reference, '.'); if (eol) *eol = '\0'; doc = stats_get_xml (0, reference); free (reference); free (fullpath); return admin_send_response (doc, httpclient, XSLT, "xspf.xsl"); } /* on demand file serving check */ config = config_get_config(); if (config->fileserve == 0) { config_release_config(); DEBUG1 ("on demand file \"%s\" refused", fullpath); free (fullpath); return client_send_404 (httpclient, "The file you requested could not be found"); } config_release_config(); if (S_ISREG (file_buf.st_mode) == 0) { WARN1 ("found requested file but there is no handler for it: %s", fullpath); free (fullpath); return client_send_404 (httpclient, "The file you requested could not be found"); } free (fullpath); finfo.flags = 0; finfo.mount = (char *)path; finfo.fallback = NULL; finfo.limit = 0; finfo.type = FORMAT_TYPE_UNDEFINED; snprintf (fsize, 20, "%" PRId64, (int64_t)file_buf.st_size); httpp_setvar (httpclient->parser, "__FILESIZE", fsize); stats_event_inc (NULL, "file_connections"); return fserve_setup_client_fb (httpclient, &finfo); }
int format_general_headers (format_plugin_t *plugin, client_t *client) { unsigned remaining = 4096 - client->refbuf->len; char *ptr = client->refbuf->data + client->refbuf->len; int bytes; int bitrate_filtered = 0; avl_node *node; ice_config_t *config; if (client->respcode == 0) { const char *useragent = httpp_getvar (client->parser, "user-agent"); const char *protocol = "HTTP/1.0"; const char *contenttypehdr = "Content-Type"; const char *contenttype = plugin->contenttype; if (useragent) { const char *resp = httpp_get_query_param (client->parser, "_hdr"); int fmtcode = 0; #define FMT_RETURN_ICY 1 #define FMT_LOWERCASE_TYPE 2 #define FMT_FORCE_AAC 4 if (resp) fmtcode = atoi (resp); else { if (strstr (useragent, "shoutcastsource")) /* hack for mpc */ fmtcode = FMT_RETURN_ICY; if (strstr (useragent, "Windows-Media-Player")) /* hack for wmp*/ fmtcode = FMT_RETURN_ICY; if (strstr (useragent, "Shoutcast Server")) /* hack for sc_serv */ fmtcode = FMT_LOWERCASE_TYPE; // if (strstr (useragent, "Sonos")) // contenttypehdr = "content-type"; if (plugin->type == FORMAT_TYPE_AAC) { if (strstr (useragent, "BlackBerry")) fmtcode = FMT_FORCE_AAC; } } if (fmtcode & FMT_RETURN_ICY) protocol = "ICY"; if (fmtcode & FMT_LOWERCASE_TYPE) contenttypehdr = "content-type"; if (fmtcode & FMT_FORCE_AAC) // ie for avoiding audio/aacp contenttype = "audio/aac"; } bytes = snprintf (ptr, remaining, "%s 200 OK\r\n" "%s: %s\r\n", protocol, contenttypehdr, contenttype); remaining -= bytes; ptr += bytes; client->respcode = 200; } if (plugin->parser) { /* iterate through source http headers and send to client */ avl_tree_rlock (plugin->parser->vars); node = avl_get_first (plugin->parser->vars); while (node) { int next = 1; http_var_t *var = (http_var_t *)node->key; bytes = 0; if (!strcasecmp (var->name, "ice-audio-info")) { /* convert ice-audio-info to icy-br */ char *brfield = NULL; unsigned int bitrate; if (bitrate_filtered == 0) brfield = strstr (var->value, "bitrate="); if (brfield && sscanf (brfield, "bitrate=%u", &bitrate)) { bytes = snprintf (ptr, remaining, "icy-br:%u\r\n", bitrate); next = 0; bitrate_filtered = 1; } else /* show ice-audio_info header as well because of relays */ bytes = snprintf (ptr, remaining, "%s: %s\r\n", var->name, var->value); } else { if (strcasecmp (var->name, "ice-password") && strcasecmp (var->name, "icy-metaint")) { if (!strncasecmp ("ice-", var->name, 4)) { if (!strcasecmp ("ice-public", var->name)) bytes = snprintf (ptr, remaining, "icy-pub:%s\r\n", var->value); else if (!strcasecmp ("ice-bitrate", var->name)) bytes = snprintf (ptr, remaining, "icy-br:%s\r\n", var->value); else bytes = snprintf (ptr, remaining, "icy%s:%s\r\n", var->name + 3, var->value); } else if (!strncasecmp ("icy-", var->name, 4)) { bytes = snprintf (ptr, remaining, "icy%s:%s\r\n", var->name + 3, var->value); } } } remaining -= bytes; ptr += bytes; if (next) node = avl_get_next (node); } avl_tree_unlock (plugin->parser->vars); } config = config_get_config(); bytes = snprintf (ptr, remaining, "Server: %s\r\n", config->server_id); config_release_config(); remaining -= bytes; ptr += bytes; /* prevent proxy servers from caching */ bytes = snprintf (ptr, remaining, "Cache-Control: no-cache\r\n"); remaining -= bytes; ptr += bytes; bytes = snprintf (ptr, remaining, "\r\n"); remaining -= bytes; ptr += bytes; client->refbuf->len = 4096 - remaining; client->refbuf->flags |= WRITE_BLOCK_GENERIC; return 0; }
static int command_manageauth (client_t *client, source_t *source, int response) { xmlDocPtr doc; xmlNodePtr node, srcnode, msgnode; const char *action = NULL; const char *username = NULL; const char *message = NULL; int ret = AUTH_OK; ice_config_t *config = config_get_config (); mount_proxy *mountinfo = config_find_mount (config, source->mount); do { if (mountinfo == NULL || mountinfo->auth == NULL) { WARN1 ("manage auth request for %s but no facility available", source->mount); break; } COMMAND_OPTIONAL (client, "action", action); COMMAND_OPTIONAL (client, "username", username); if (action == NULL) action = "list"; if (!strcmp(action, "add")) { const char *password = NULL; COMMAND_OPTIONAL (client, "password", password); if (username == NULL || password == NULL) { WARN1 ("manage auth request add for %s but no user/pass", source->mount); break; } ret = mountinfo->auth->adduser(mountinfo->auth, username, password); if (ret == AUTH_FAILED) { message = "User add failed - check the icecast error log"; } if (ret == AUTH_USERADDED) { message = "User added"; } if (ret == AUTH_USEREXISTS) { message = "User already exists - not added"; } } if (!strcmp(action, "delete")) { if (username == NULL) { WARN1 ("manage auth request delete for %s but no username", source->mount); break; } ret = mountinfo->auth->deleteuser(mountinfo->auth, username); if (ret == AUTH_FAILED) { message = "User delete failed - check the icecast error log"; } if (ret == AUTH_USERDELETED) { message = "User deleted"; } } doc = xmlNewDoc(XMLSTR "1.0"); node = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL); srcnode = xmlNewChild(node, NULL, XMLSTR("source"), NULL); xmlSetProp(srcnode, XMLSTR "mount", XMLSTR(source->mount)); thread_mutex_unlock (&source->lock); if (message) { msgnode = xmlNewChild(node, NULL, XMLSTR("iceresponse"), NULL); xmlNewChild(msgnode, NULL, XMLSTR "message", XMLSTR(message)); } xmlDocSetRootElement(doc, node); if (mountinfo && mountinfo->auth && mountinfo->auth->listuser) mountinfo->auth->listuser (mountinfo->auth, srcnode); config_release_config (); return admin_send_response (doc, client, response, "manageauth.xsl"); } while (0); thread_mutex_unlock (&source->lock); config_release_config (); return client_send_400 (client, "missing parameter"); }
/* build an XML doc containing information about currently running sources. * If a mountpoint is passed then that source will not be added to the XML * doc even if the source is running */ xmlDocPtr admin_build_sourcelist (const char *mount) { avl_node *node; source_t *source; xmlNodePtr xmlnode, srcnode; xmlDocPtr doc; char buf[22]; time_t now = time(NULL); doc = xmlNewDoc(XMLSTR("1.0")); xmlnode = xmlNewDocNode(doc, NULL, XMLSTR("icestats"), NULL); xmlDocSetRootElement(doc, xmlnode); if (mount) { xmlNewChild(xmlnode, NULL, XMLSTR("current_source"), XMLSTR(mount)); } node = avl_get_first(global.source_tree); while(node) { source = (source_t *)node->key; if (mount && strcmp (mount, source->mount) == 0) { node = avl_get_next (node); continue; } thread_mutex_lock (&source->lock); if (source_available (source)) { ice_config_t *config; mount_proxy *mountinfo; srcnode = xmlNewChild (xmlnode, NULL, XMLSTR("source"), NULL); xmlSetProp (srcnode, XMLSTR("mount"), XMLSTR(source->mount)); snprintf (buf, sizeof(buf), "%lu", source->listeners); xmlNewChild (srcnode, NULL, XMLSTR("listeners"), XMLSTR(buf)); config = config_get_config(); mountinfo = config_find_mount (config, source->mount); if (mountinfo) { if (mountinfo->auth) { xmlNewChild (srcnode, NULL, XMLSTR("authenticator"), XMLSTR(mountinfo->auth->type)); } if (mountinfo->fallback_mount) xmlNewChild (srcnode, NULL, XMLSTR("fallback"), XMLSTR(mountinfo->fallback_mount)); } config_release_config(); if (source_running (source)) { if (source->client) { snprintf (buf, sizeof(buf), "%lu", (unsigned long)(now - source->client->connection.con_time)); xmlNewChild (srcnode, NULL, XMLSTR("Connected"), XMLSTR(buf)); } xmlNewChild (srcnode, NULL, XMLSTR("content-type"), XMLSTR(source->format->contenttype)); } } thread_mutex_unlock (&source->lock); node = avl_get_next(node); } return(doc); }
int format_general_headers (format_plugin_t *plugin, client_t *client) { unsigned remaining = 4096 - client->refbuf->len; char *ptr = client->refbuf->data + client->refbuf->len; int bytes = 0; int bitrate_filtered = 0; avl_node *node; ice_config_t *config; uint64_t length = 0; /* hack for flash player, it wants a length. */ if (httpp_getvar (client->parser, "x-flash-version")) length = 221183499; else { // flash may not send above header, so check for swf in referer const char *referer = httpp_getvar (client->parser, "referer"); if (referer) { int len = strcspn (referer, "?"); if (len >= 4 && strncmp (referer+len-4, ".swf", 4) == 0) length = 221183499; } } if (client->respcode == 0) { const char *useragent = httpp_getvar (client->parser, "user-agent"); const char *protocol = "HTTP/1.0"; const char *contenttypehdr = "Content-Type"; const char *contenttype = plugin->contenttype; const char *range = httpp_getvar (client->parser, "range"); const char *fs = httpp_getvar (client->parser, "__FILESIZE"); if (useragent) { const char *resp = httpp_get_query_param (client->parser, "_hdr"); int fmtcode = 0; #define FMT_RETURN_ICY 1 #define FMT_LOWERCASE_TYPE 2 #define FMT_FORCE_AAC 4 do { if (resp) { fmtcode = atoi (resp); break; } if (fs) break; // ignore following settings for files. if (strstr (useragent, "shoutcastsource")) /* hack for mpc */ fmtcode = FMT_RETURN_ICY; if (strstr (useragent, "Windows-Media-Player")) /* hack for wmp*/ fmtcode = FMT_RETURN_ICY; if (strstr (useragent, "RealMedia")) /* hack for rp (mainly mobile) */ fmtcode = FMT_RETURN_ICY; if (strstr (useragent, "Shoutcast Server")) /* hack for sc_serv */ fmtcode = FMT_LOWERCASE_TYPE; // if (strstr (useragent, "Sonos")) // contenttypehdr = "content-type"; if (plugin->type == FORMAT_TYPE_AAC && strstr (useragent, "AppleWebKit")) fmtcode |= FMT_FORCE_AAC; if (strstr (useragent, "BlackBerry")) { fmtcode |= FMT_RETURN_ICY; if (plugin->type == FORMAT_TYPE_AAC) fmtcode |= FMT_FORCE_AAC; } } while (0); if (fmtcode & FMT_RETURN_ICY) protocol = "ICY"; if (fmtcode & FMT_LOWERCASE_TYPE) contenttypehdr = "content-type"; if (fmtcode & FMT_FORCE_AAC) // ie for avoiding audio/aacp contenttype = "audio/aac"; } if (fs) { uint64_t pos1 = 0, pos2 = length ? length : 50000000, max = pos2; char buf[30]; sscanf (fs, "%" SCNuMAX, &max); if (range) { if (strncmp (range, "bytes=-", 7) == 0) { sscanf (range, "bytes=-%" SCNuMAX, &pos2); pos1 = max-pos2; pos2 = max-1; } else { if (sscanf (range, "bytes=%" SCNuMAX "-%" SCNuMAX, &pos1, &pos2) < 2) pos2 = max-1; } if (pos2 >= max || pos1 >= pos2) { DEBUG2 ("client range invalid (%" PRIu64 ", %" PRIu64 ")", pos1, pos2); return -1; } length = pos2 - pos1 + 1; snprintf (buf, 30, "%" PRIu64, length); httpp_setvar (client->parser, "__LENGTH", buf); client->respcode = 206; client->intro_offset = pos1; bytes = snprintf (ptr, remaining, "%s 206 Partial Content\r\n" "%s: %s\r\n" "Content-Length: %" PRIu64 "\r\n" "Content-Range: bytes %" PRIu64 "-%" PRIu64 "/%" PRIu64 "\r\n", protocol, contenttypehdr, contenttype ? contenttype : "application/octet-stream", length, pos1, pos2, max); } else { client->respcode = 200; bytes = snprintf (ptr, remaining, "%s 200 OK\r\n" "Content-Length: %" PRIu64 "\r\n" "%s: %s\r\n", protocol, max, contenttypehdr, contenttype); } } else { client->respcode = 200; bytes = snprintf (ptr, remaining, "%s 200 OK\r\nAccept-Ranges: none\r\n" "%s: %s\r\n", protocol, contenttypehdr, contenttype); } remaining -= bytes; ptr += bytes; } if (plugin && plugin->parser) { /* iterate through source http headers and send to client */ avl_tree_rlock (plugin->parser->vars); node = avl_get_first (plugin->parser->vars); while (node) { int next = 1; http_var_t *var = (http_var_t *)node->key; bytes = 0; if (!strcasecmp (var->name, "ice-audio-info")) { /* convert ice-audio-info to icy-br */ char *brfield = NULL; unsigned int bitrate; if (bitrate_filtered == 0) brfield = strstr (var->value, "bitrate="); if (brfield && sscanf (brfield, "bitrate=%u", &bitrate)) { bytes = snprintf (ptr, remaining, "icy-br:%u\r\n", bitrate); next = 0; bitrate_filtered = 1; } else /* show ice-audio_info header as well because of relays */ bytes = snprintf (ptr, remaining, "%s: %s\r\n", var->name, var->value); } else { if (strcasecmp (var->name, "ice-password") && strcasecmp (var->name, "icy-metaint")) { if (!strncasecmp ("ice-", var->name, 4)) { if (!strcasecmp ("ice-public", var->name)) bytes = snprintf (ptr, remaining, "icy-pub:%s\r\n", var->value); else if (!strcasecmp ("ice-bitrate", var->name)) bytes = snprintf (ptr, remaining, "icy-br:%s\r\n", var->value); else bytes = snprintf (ptr, remaining, "icy%s:%s\r\n", var->name + 3, var->value); } else if (!strncasecmp ("icy-", var->name, 4)) { bytes = snprintf (ptr, remaining, "icy%s:%s\r\n", var->name + 3, var->value); } } } remaining -= bytes; ptr += bytes; if (next) node = avl_get_next (node); } avl_tree_unlock (plugin->parser->vars); } config = config_get_config(); bytes = snprintf (ptr, remaining, "Server: %s\r\n", config->server_id); config_release_config(); remaining -= bytes; ptr += bytes; /* prevent proxy servers from caching */ bytes = snprintf (ptr, remaining, "Cache-Control: no-cache, no-store\r\nPragma: no-cache\r\n" "Access-Control-Allow-Origin: *\r\n" "Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type\r\n" "Access-Control-Allow-Methods: GET, OPTIONS, HEAD\r\n" "Connection: close\r\n" "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"); remaining -= bytes; ptr += bytes; bytes = snprintf (ptr, remaining, "\r\n"); remaining -= bytes; ptr += bytes; client->refbuf->len = 4096 - remaining; client->refbuf->flags |= WRITE_BLOCK_GENERIC; return 0; }
/** Remove the firewall rules * This is used when we do a clean shutdown of nodogsplash, * and when it starts, to make sure there are no rules left over from a crash */ int iptables_fw_destroy(void) { fw_quiet = 1; s_config *config; int traffic_control; LOCK_CONFIG(); config = config_get_config(); traffic_control = config->traffic_control; UNLOCK_CONFIG(); if (traffic_control) { debug(LOG_DEBUG, "Destroying our tc hooks"); tc_destroy_tc(); } debug(LOG_DEBUG, "Destroying our iptables entries"); /* * * Everything in the mangle table * */ debug(LOG_DEBUG, "Destroying chains in the MANGLE table"); iptables_fw_destroy_mention("mangle", "PREROUTING", CHAIN_TRUSTED); iptables_fw_destroy_mention("mangle", "PREROUTING", CHAIN_BLOCKED); iptables_fw_destroy_mention("mangle", "PREROUTING", CHAIN_ALLOWED); iptables_fw_destroy_mention("mangle", "PREROUTING", CHAIN_OUTGOING); iptables_fw_destroy_mention("mangle", "POSTROUTING", CHAIN_INCOMING); iptables_do_command("-t mangle -F " CHAIN_TRUSTED); iptables_do_command("-t mangle -F " CHAIN_BLOCKED); iptables_do_command("-t mangle -F " CHAIN_ALLOWED); iptables_do_command("-t mangle -F " CHAIN_OUTGOING); iptables_do_command("-t mangle -F " CHAIN_INCOMING); iptables_do_command("-t mangle -X " CHAIN_TRUSTED); iptables_do_command("-t mangle -X " CHAIN_BLOCKED); iptables_do_command("-t mangle -X " CHAIN_ALLOWED); iptables_do_command("-t mangle -X " CHAIN_OUTGOING); iptables_do_command("-t mangle -X " CHAIN_INCOMING); /* * * Everything in the nat table * */ debug(LOG_DEBUG, "Destroying chains in the NAT table"); iptables_fw_destroy_mention("nat", "PREROUTING", CHAIN_OUTGOING); iptables_do_command("-t nat -F " CHAIN_OUTGOING); iptables_do_command("-t nat -X " CHAIN_OUTGOING); /* * * Everything in the filter table * */ debug(LOG_DEBUG, "Destroying chains in the FILTER table"); iptables_fw_destroy_mention("filter", "INPUT", CHAIN_TO_ROUTER); iptables_fw_destroy_mention("filter", "FORWARD", CHAIN_TO_INTERNET); iptables_do_command("-t filter -F " CHAIN_TO_ROUTER); iptables_do_command("-t filter -F " CHAIN_TO_INTERNET); iptables_do_command("-t filter -F " CHAIN_AUTHENTICATED); iptables_do_command("-t filter -F " CHAIN_TRUSTED); iptables_do_command("-t filter -F " CHAIN_TRUSTED_TO_ROUTER); iptables_do_command("-t filter -X " CHAIN_TO_ROUTER); iptables_do_command("-t filter -X " CHAIN_TO_INTERNET); iptables_do_command("-t filter -X " CHAIN_AUTHENTICATED); iptables_do_command("-t filter -X " CHAIN_TRUSTED); iptables_do_command("-t filter -X " CHAIN_TRUSTED_TO_ROUTER); return 0; }
void connection_accept_loop(void) { connection_t *con; if (!kitsune_is_updating()) /**DSU control */ tid = thread_create("connection thread", _handle_connection, NULL, THREAD_ATTACHED); while (global.running == ICE_RUNNING) { kitsune_update("connection_accept"); /**DSU updatepoint */ con = _accept_connection(); if (con) { client_queue_t *node; ice_config_t *config; int i; client_t *client = NULL; global_lock(); if (client_create (&client, con, NULL) < 0) { global_unlock(); client_send_404 (client, "Icecast connection limit reached"); continue; } global_unlock(); /* setup client for reading incoming http */ client->refbuf->data [PER_CLIENT_REFBUF_SIZE-1] = '\000'; node = calloc (1, sizeof (client_queue_t)); if (node == NULL) { client_destroy (client); continue; } node->client = client; /* Check for special shoutcast compatability processing */ config = config_get_config(); for (i = 0; i < global.server_sockets; i++) { if (global.serversock[i] == con->serversock) { if (config->listeners[i].shoutcast_compat) node->shoutcast = 1; } } config_release_config(); sock_set_blocking (client->con->sock, SOCK_NONBLOCK); sock_set_nodelay (client->con->sock); _add_request_queue (node); stats_event_inc (NULL, "connections"); } process_request_queue (); } /* Give all the other threads notification to shut down */ thread_cond_broadcast(&global.shutdown_cond); if (tid) thread_join (tid); /* wait for all the sources to shutdown */ thread_rwlock_wlock(&_source_shutdown_rwlock); thread_rwlock_unlock(&_source_shutdown_rwlock); }
/** Update the counters of all the clients in the client list */ int iptables_fw_counters_update(void) { FILE *output; char *script, ip[INET6_ADDRSTRLEN], target[MAX_BUF]; int rc; int af; s_config *config; unsigned long long int counter; t_client *p1; struct sockaddr_storage tempaddr; config = config_get_config(); af = config->ip6 ? AF_INET6 : AF_INET; /* Look for outgoing traffic */ safe_asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " CHAIN_OUTGOING); output = popen(script, "r"); free(script); if (!output) { debug(LOG_ERR, "popen(): %s", strerror(errno)); return -1; } /* skip the first two lines */ while (('\n' != fgetc(output)) && !feof(output)) {} while (('\n' != fgetc(output)) && !feof(output)) {} while ( !(feof(output) )) { rc = fscanf(output, "%*s %llu %s %*s %*s %*s %*s %15[0-9.]", &counter,target,ip); /* eat rest of line */ while (('\n' != fgetc(output)) && !feof(output)) {} if (3 == rc && !strcmp(target,"MARK")) { /* Sanity*/ if (!inet_pton(af, ip, &tempaddr)) { debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip); continue; } debug(LOG_DEBUG, "Read outgoing traffic for %s: Bytes=%llu", ip, counter); LOCK_CLIENT_LIST(); if ((p1 = client_list_find_by_ip(ip))) { if ((p1->counters.outgoing - p1->counters.outgoing_history) < counter) { p1->counters.outgoing = p1->counters.outgoing_history + counter; p1->counters.last_updated = time(NULL); debug(LOG_DEBUG, "%s - Updated counter.outgoing to %llu bytes. Updated last_updated to %d", ip, counter, p1->counters.last_updated); } } else { debug(LOG_WARNING, "Could not find %s in client list", ip); } UNLOCK_CLIENT_LIST(); } } pclose(output); /* Look for incoming traffic */ safe_asprintf(&script, "%s %s", "iptables", "-v -n -x -t mangle -L " CHAIN_INCOMING); output = popen(script, "r"); free(script); if (!output) { debug(LOG_ERR, "popen(): %s", strerror(errno)); return -1; } /* skip the first two lines */ while (('\n' != fgetc(output)) && !feof(output)) {} while (('\n' != fgetc(output)) && !feof(output)) {} while ( !(feof(output) )) { rc = fscanf(output, "%*s %llu %s %*s %*s %*s %*s %*s %15[0-9.]", &counter,target,ip); /* eat rest of line */ while (('\n' != fgetc(output)) && !feof(output)) {} if (3 == rc && !strcmp(target,"ACCEPT")) { /* Sanity*/ if (!inet_pton(af, ip, &tempaddr)) { debug(LOG_WARNING, "I was supposed to read an IP address but instead got [%s] - ignoring it", ip); continue; } debug(LOG_DEBUG, "Read incoming traffic for %s: Bytes=%llu", ip, counter); LOCK_CLIENT_LIST(); if ((p1 = client_list_find_by_ip(ip))) { if ((p1->counters.incoming - p1->counters.incoming_history) < counter) { p1->counters.incoming = p1->counters.incoming_history + counter; debug(LOG_DEBUG, "%s - Updated counter.incoming to %llu bytes", ip, counter); } } else { debug(LOG_WARNING, "Could not find %s in client list", ip); } UNLOCK_CLIENT_LIST(); } } pclose(output); return 0; }
static void _handle_get_request (client_t *client, char *passed_uri) { int fileserve; int port; int i; char *serverhost = NULL; int serverport = 0; aliases *alias; ice_config_t *config; char *uri = passed_uri; config = config_get_config(); fileserve = config->fileserve; port = config->port; for(i = 0; i < global.server_sockets; i++) { if(global.serversock[i] == client->con->serversock) { serverhost = config->listeners[i].bind_address; serverport = config->listeners[i].port; break; } } alias = config->aliases; /* there are several types of HTTP GET clients ** media clients, which are looking for a source (eg, URI = /stream.ogg) ** stats clients, which are looking for /admin/stats.xml ** and directory server authorizers, which are looking for /GUID-xxxxxxxx ** (where xxxxxx is the GUID in question) - this isn't implemented yet. ** we need to handle the latter two before the former, as the latter two ** aren't subject to the limits. */ /* TODO: add GUID-xxxxxx */ /* Handle aliases */ while(alias) { if(strcmp(uri, alias->source) == 0 && (alias->port == -1 || alias->port == serverport) && (alias->bind_address == NULL || (serverhost != NULL && strcmp(alias->bind_address, serverhost) == 0))) { uri = strdup (alias->destination); DEBUG2 ("alias has made %s into %s", passed_uri, uri); break; } alias = alias->next; } config_release_config(); stats_event_inc(NULL, "client_connections"); /* Dispatch all admin requests */ if ((strcmp(uri, "/admin.cgi") == 0) || (strncmp(uri, "/admin/", 7) == 0)) { admin_handle_request(client, uri); if (uri != passed_uri) free (uri); return; } /* Here we are parsing the URI request to see ** if the extension is .xsl, if so, then process ** this request as an XSLT request */ if (util_check_valid_extension (uri) == XSLT_CONTENT) { /* If the file exists, then transform it, otherwise, write a 404 */ DEBUG0("Stats request, sending XSL transformed stats"); stats_transform_xslt (client, uri); if (uri != passed_uri) free (uri); return; } add_client (uri, client); if (uri != passed_uri) free (uri); }
void config_shutdown(void) { config_get_config(); config_clear(&_current_configuration); config_release_config(); release_locks(); }
/* 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(); }
/**@internal * Main execution loop */ static void main_loop(void) { int result; pthread_t tid; s_config *config = config_get_config(); request *r; void **params; /* Set the time when wifidog started */ if (!started_time) { debug(LOG_INFO, "Setting started_time"); started_time = time(NULL); } else if (started_time < MINIMUM_STARTED_TIME) { debug(LOG_WARNING, "Detected possible clock skew - re-setting started_time"); started_time = time(NULL); } /* save the pid file if needed */ if ((!config) && (!config->pidfile)) save_pid_file(config->pidfile); /* If we don't have the Gateway IP address, get it. Can't fail. */ if (!config->gw_address) { debug(LOG_DEBUG, "Finding IP address of %s", config->gw_interface); if ((config->gw_address = get_iface_ip(config->gw_interface)) == NULL) { debug(LOG_ERR, "Could not get IP address information of %s, exiting...", config->gw_interface); exit(1); } debug(LOG_DEBUG, "%s = %s", config->gw_interface, config->gw_address); } /* If we don't have the Gateway ID, construct it from the internal MAC address. * "Can't fail" so exit() if the impossible happens. */ if (!config->gw_id) { debug(LOG_DEBUG, "Finding MAC address of %s", config->external_interface); if ((config->gw_id = get_iface_mac(config->external_interface)) == NULL) { debug(LOG_ERR, "Could not get MAC address information of %s, exiting...", config->external_interface); exit(1); } debug(LOG_DEBUG, "%s = %s", config->gw_interface, config->gw_id); } /* Initializes the web server */ debug(LOG_NOTICE, "Creating web server on %s:%d", config->gw_address, config->gw_port); if ((webserver = httpdCreate(config->gw_address, config->gw_port)) == NULL) { debug(LOG_ERR, "Could not create web server: %s", strerror(errno)); exit(1); } register_fd_cleanup_on_fork(webserver->serverSock); debug(LOG_DEBUG, "Assigning callbacks to web server"); httpdAddCContent(webserver, "/wifidog", "about", 0, NULL, http_callback_about); httpdAddCContent(webserver, "/wifidog", "status", 0, NULL, http_callback_status); httpdAddCContent(webserver, "/wifidog", "release", 0, NULL, http_callback_release); httpdAddCContent(webserver, "/wifidog", "allow", 0, NULL, http_callback_allow_redirect); httpdSetErrorFunction(webserver, 404, http_callback_404); /* Set the auth server ip address. */ set_auth_svr_lastip(config); /* Reset the firewall (if WiFiDog crashed) */ fw_destroy(); /* Then initialize it */ if (!fw_init()) { debug(LOG_ERR, "FATAL: Failed to initialize firewall"); exit(1); } /* Start control thread */ result = pthread_create(&tid, NULL, (void *)thread_wdctl, (void *)safe_strdup(config->wdctl_sock)); if (result != 0) { debug(LOG_ERR, "FATAL: Failed to create a new thread (wdctl) - exiting"); termination_handler(0); } pthread_detach(tid); debug(LOG_NOTICE, "Waiting for connections"); while (1) { r = httpdGetConnection(webserver, NULL); /* We can't convert this to a switch because there might be * values that are not -1, 0 or 1. */ if (webserver->lastError == -1) { /* Interrupted system call */ if (NULL != r) { httpdEndRequest(r); } } else if (webserver->lastError < -1) { /* * FIXME * An error occurred - should we abort? * reboot the device ? */ debug(LOG_ERR, "FATAL: httpdGetConnection returned unexpected value %d, exiting.", webserver->lastError); termination_handler(0); } else if (r != NULL) { /* * We got a connection * * We should create another thread */ debug(LOG_INFO, "Received connection from %s, spawning worker thread", r->clientAddr); /* The void**'s are a simulation of the normal C * function calling sequence. */ params = safe_malloc(2 * sizeof(void *)); *params = webserver; *(params + 1) = r; result = pthread_create(&tid, NULL, (void *)thread_httpd, (void *)params); if (result != 0) { debug(LOG_ERR, "FATAL: Failed to create a new thread (httpd) - exiting"); termination_handler(0); } pthread_detach(tid); } else { /* webserver->lastError should be 2 */ /* XXX We failed an ACL.... No handling because * we don't set any... */ } //update the auth server ip update_auth_svr_lastip(config); } /* never reached */ }
/* Called when activating a source. Verifies that the source count is not * exceeded and applies any initial parameters. */ int connection_complete_source (source_t *source, int response) { ice_config_t *config; global_lock (); DEBUG1 ("sources count is %d", global.sources); config = config_get_config(); if (global.sources < config->source_limit) { const char *contenttype; const char *expectcontinue; mount_proxy *mountinfo; format_type_t format_type; /* setup format handler */ contenttype = httpp_getvar (source->parser, "content-type"); if (contenttype != NULL) { format_type = format_get_type (contenttype); if (format_type == FORMAT_ERROR) { config_release_config(); global_unlock(); if (response) { client_send_403 (source->client, "Content-type not supported"); source->client = NULL; } WARN1("Content-type \"%s\" not supported, dropping source", contenttype); return -1; } } else { WARN0("No content-type header, falling back to backwards compatibility mode " "for icecast 1.x relays. Assuming content is mp3."); format_type = FORMAT_TYPE_GENERIC; } if (format_get_plugin (format_type, source) < 0) { global_unlock(); config_release_config(); if (response) { client_send_403 (source->client, "internal format allocation problem"); source->client = NULL; } WARN1 ("plugin format failed for \"%s\"", source->mount); return -1; } /* For PUT support we check for 100-continue and send back a 100 to stay in spec */ expectcontinue = httpp_getvar (source->parser, "expect"); if (expectcontinue != NULL) { #ifdef HAVE_STRCASESTR if (strcasestr (expectcontinue, "100-continue") != NULL) #else WARN0("OS doesn't support case insenestive substring checks..."); if (strstr (expectcontinue, "100-continue") != NULL) #endif { client_send_100 (source->client); } } global.sources++; stats_event_args (NULL, "sources", "%d", global.sources); global_unlock(); source->running = 1; mountinfo = config_find_mount (config, source->mount, MOUNT_TYPE_NORMAL); source_update_settings (config, source, mountinfo); config_release_config(); slave_rebuild_mounts(); source->shutdown_rwlock = &_source_shutdown_rwlock; DEBUG0 ("source is ready to start"); return 0; } WARN1("Request to add source when maximum source limit " "reached %d", global.sources); global_unlock(); config_release_config(); if (response) { client_send_403 (source->client, "too many sources connected"); source->client = NULL; } return -1; }
/* @internal * @brief During gateway restart, connects to the parent process via the internal socket * Downloads from it the active client list */ void get_clients_from_parent(void) { int sock; struct sockaddr_un sa_un; s_config *config = NULL; char linebuffer[MAX_BUF]; int len = 0; char *running1 = NULL; char *running2 = NULL; char *token1 = NULL; char *token2 = NULL; char onechar; char *command = NULL; char *key = NULL; char *value = NULL; t_client *client = NULL; config = config_get_config(); debug(LOG_INFO, "Connecting to parent to download clients"); /* Connect to socket */ sock = socket(AF_UNIX, SOCK_STREAM, 0); /* XXX An attempt to quieten coverity warning about the subsequent connect call: * Coverity says: "sock is apssed to parameter that cannot be negative" * Although connect expects a signed int, coverity probably tells us that it shouldn't * be negative */ if (sock < 0) { debug(LOG_ERR, "Could not open socket (%s) - client list not downloaded", strerror(errno)); return; } memset(&sa_un, 0, sizeof(sa_un)); sa_un.sun_family = AF_UNIX; strncpy(sa_un.sun_path, config->internal_sock, (sizeof(sa_un.sun_path) - 1)); if (connect(sock, (struct sockaddr *)&sa_un, strlen(sa_un.sun_path) + sizeof(sa_un.sun_family))) { debug(LOG_ERR, "Failed to connect to parent (%s) - client list not downloaded", strerror(errno)); close(sock); return; } debug(LOG_INFO, "Connected to parent. Downloading clients"); LOCK_CLIENT_LIST(); command = NULL; memset(linebuffer, 0, sizeof(linebuffer)); len = 0; client = NULL; /* Get line by line */ while (read(sock, &onechar, 1) == 1) { if (onechar == '\n') { /* End of line */ onechar = '\0'; } linebuffer[len++] = onechar; if (!onechar) { /* We have a complete entry in linebuffer - parse it */ debug(LOG_DEBUG, "Received from parent: [%s]", linebuffer); running1 = linebuffer; while ((token1 = strsep(&running1, "|")) != NULL) { if (!command) { /* The first token is the command */ command = token1; } else { /* Token1 has something like "foo=bar" */ running2 = token1; key = value = NULL; while ((token2 = strsep(&running2, "=")) != NULL) { if (!key) { key = token2; } else if (!value) { value = token2; } } } if (strcmp(command, "CLIENT") == 0) { /* This line has info about a client in the client list */ if (NULL == client) { /* Create a new client struct */ client = client_get_new(); } } /* XXX client check to shut up clang... */ if (key && value && client) { if (strcmp(command, "CLIENT") == 0) { /* Assign the key into the appropriate slot in the connection structure */ if (strcmp(key, "ip") == 0) { client->ip = safe_strdup(value); } else if (strcmp(key, "mac") == 0) { client->mac = safe_strdup(value); } else if (strcmp(key, "fw_connection_state") == 0) { client->fw_connection_state = atoi(value); } else if (strcmp(key, "fd") == 0) { client->fd = atoi(value); } else { debug(LOG_NOTICE, "I don't know how to inherit key [%s] value [%s] from parent", key, value); } } } } /* End of parsing this command */ if (client) { client_list_insert_client(client); } /* Clean up */ command = NULL; memset(linebuffer, 0, sizeof(linebuffer)); len = 0; client = NULL; } } UNLOCK_CLIENT_LIST(); debug(LOG_INFO, "Client list downloaded successfully from parent"); close(sock); }
static auth_result url_add_listener (auth_client *auth_user) { client_t *client = auth_user->client; auth_t *auth = client->auth; auth_url *url = auth->state; int res = 0, port; const char *agent; char *user_agent, *username, *password; const char *mountreq; char *mount, *ipaddr, *server; ice_config_t *config; char *userpwd = NULL, post [4096]; ssize_t post_offset; char *pass_headers, *cur_header, *next_header; const char *header_val; char *header_valesc; if (url->addurl == NULL) return AUTH_OK; config = config_get_config (); server = util_url_escape (config->hostname); port = config->port; config_release_config (); agent = httpp_getvar (client->parser, "user-agent"); if (agent) user_agent = util_url_escape (agent); else user_agent = strdup ("-"); if (client->username) username = util_url_escape (client->username); else username = strdup (""); if (client->password) password = util_url_escape (client->password); else password = strdup (""); /* get the full uri (with query params if available) */ mountreq = httpp_getvar (client->parser, HTTPP_VAR_RAWURI); if (mountreq == NULL) mountreq = httpp_getvar (client->parser, HTTPP_VAR_URI); mount = util_url_escape (mountreq); ipaddr = util_url_escape (client->con->ip); post_offset = snprintf (post, sizeof (post), "action=listener_add&server=%s&port=%d&client=%lu&mount=%s" "&user=%s&pass=%s&ip=%s&agent=%s", server, port, client->con->id, mount, username, password, ipaddr, user_agent); free (server); free (mount); free (user_agent); free (username); free (password); free (ipaddr); pass_headers = NULL; if (url->pass_headers) pass_headers = strdup (url->pass_headers); if (pass_headers) { cur_header = pass_headers; while (cur_header) { next_header = strstr (cur_header, ","); if (next_header) { *next_header=0; next_header++; } header_val = httpp_getvar (client->parser, cur_header); if (header_val) { header_valesc = util_url_escape (header_val); post_offset += snprintf (post+post_offset, sizeof (post)-post_offset, "&%s%s=%s", url->prefix_headers ? url->prefix_headers : "", cur_header, header_valesc); free (header_valesc); } cur_header = next_header; } } if (strchr (url->addurl, '@') == NULL) { if (url->userpwd) curl_easy_setopt (url->handle, CURLOPT_USERPWD, url->userpwd); else { /* auth'd requests may not have a user/pass, but may use query args */ if (client->username && client->password) { size_t len = strlen (client->username) + strlen (client->password) + 2; userpwd = malloc (len); snprintf (userpwd, len, "%s:%s", client->username, client->password); curl_easy_setopt (url->handle, CURLOPT_USERPWD, userpwd); } else curl_easy_setopt (url->handle, CURLOPT_USERPWD, ""); } } else { /* url has user/pass but libcurl may need to clear any existing settings */ curl_easy_setopt (url->handle, CURLOPT_USERPWD, ""); } curl_easy_setopt (url->handle, CURLOPT_URL, url->addurl); curl_easy_setopt (url->handle, CURLOPT_POSTFIELDS, post); curl_easy_setopt (url->handle, CURLOPT_WRITEHEADER, auth_user); url->errormsg[0] = '\0'; res = curl_easy_perform (url->handle); free (userpwd); if (res) { WARN2 ("auth to server %s failed with %s", url->addurl, url->errormsg); return AUTH_FAILED; } /* we received a response, lets see what it is */ if (client->authenticated) return AUTH_OK; INFO2 ("client auth (%s) failed with \"%s\"", url->addurl, url->errormsg); return AUTH_FAILED; }
/** Uses getopt() to parse the command line and set configuration values * also populates restartargv */ void parse_commandline(int argc, char **argv) { int c; int skiponrestart; int i; s_config *config = config_get_config(); //MAGIC 3: Our own -x, the pid, and NULL : restartargv = safe_malloc((size_t) (argc + 3) * sizeof(char *)); i = 0; restartargv[i++] = safe_strdup(argv[0]); while (-1 != (c = getopt(argc, argv, "c:hfd:sw:vx:i:a:"))) { skiponrestart = 0; switch (c) { case 'h': usage(); exit(1); break; case 'c': if (optarg) { free(config->configfile); config->configfile = safe_strdup(optarg); } break; case 'w': if (optarg) { free(config->wdctl_sock); config->wdctl_sock = safe_strdup(optarg); } break; case 'f': skiponrestart = 1; config->daemon = 0; debugconf.log_stderr = 1; break; case 'd': if (optarg) { debugconf.debuglevel = atoi(optarg); } break; case 's': debugconf.log_syslog = 1; break; case 'v': fprintf(stdout, "This is WiFiDog-FFW version " VERSION "\n"); exit(1); break; case 'x': skiponrestart = 1; if (optarg) { restart_orig_pid = atoi(optarg); } else { fprintf(stdout, "The expected PID to the -x switch was not supplied!"); exit(1); } break; case 'i': if (optarg) { free(config->internal_sock); config->internal_sock = safe_strdup(optarg); } break; case 'a': if (optarg) { free(config->arp_table_path); config->arp_table_path = safe_strdup(optarg); } else { fprintf(stdout, "You must supply the path to the ARP table with -a!"); exit(1); } break; default: usage(); exit(1); break; } if (!skiponrestart) { /* Add it to restartargv */ safe_asprintf(&(restartargv[i++]), "-%c", c); if (optarg) { restartargv[i++] = safe_strdup(optarg); } } } /* Finally, we should add the -x, pid and NULL to restartargv * HOWEVER we cannot do it here, since this is called before we fork to background * so we'll leave this job to gateway.c after forking is completed * so that the correct PID is assigned * * We add 3 nulls, and the first 2 will be overridden later */ restartargv[i++] = NULL; restartargv[i++] = NULL; restartargv[i++] = NULL; }
/** Authenticates a single client against the central server and returns when done * Alters the firewall rules depending on what the auth server says @param r httpd request struct */ void authenticate_client(request *r) { t_client *client; t_authresponse auth_response; char *mac, *token; char *urlFragment = NULL; s_config *config = NULL; t_auth_serv *auth_server = NULL; httpVar *click_ad = httpdGetVariableByName(r, "click_ad"); LOCK_CLIENT_LIST(); client = client_list_find_by_ip(r->clientAddr); if (client == NULL) { debug(LOG_ERR, "authenticate_client(): Could not find client for %s", r->clientAddr); UNLOCK_CLIENT_LIST(); return; } mac = safe_strdup(client->mac); token = safe_strdup(client->token); UNLOCK_CLIENT_LIST(); /* * At this point we've released the lock while we do an HTTP request since it could * take multiple seconds to do and the gateway would effectively be frozen if we * kept the lock. */ auth_server_request(&auth_response, REQUEST_TYPE_LOGIN, r->clientAddr, mac, token, 0, 0); LOCK_CLIENT_LIST(); /* can't trust the client to still exist after n seconds have passed */ client = client_list_find(r->clientAddr, mac); if (client == NULL) { debug(LOG_ERR, "authenticate_client(): Could not find client node for %s (%s)", r->clientAddr, mac); UNLOCK_CLIENT_LIST(); free(token); free(mac); return; } free(token); free(mac); /* Prepare some variables we'll need below */ config = config_get_config(); auth_server = get_auth_server(); switch(auth_response.authcode) { case AUTH_ERROR: /* Error talking to central server */ debug(LOG_ERR, "Got %d from central server authenticating token %s from %s at %s", auth_response, client->token, client->ip, client->mac); send_http_page(r, "Error!", "Error: We did not get a valid answer from the central server"); break; case AUTH_DENIED: /* Central server said invalid token */ debug(LOG_INFO, "Got DENIED from central server authenticating token %s from %s at %s - deleting from firewall and redirecting them to denied message", client->token, client->ip, client->mac); fw_deny(client->ip, client->mac, FW_MARK_KNOWN); safe_asprintf(&urlFragment, "%smessage=%s", auth_server->authserv_msg_script_path_fragment, GATEWAY_MESSAGE_DENIED ); http_send_redirect_to_auth(r, urlFragment, "Redirect to denied message"); free(urlFragment); break; case AUTH_VALIDATION: /* They just got validated for X minutes to check their email */ debug(LOG_INFO, "Got VALIDATION from central server authenticating token %s from %s at %s" "- adding to firewall and redirecting them to activate message", client->token, client->ip, client->mac); client->fw_connection_state = FW_MARK_PROBATION; fw_allow(client->ip, client->mac, FW_MARK_PROBATION); safe_asprintf(&urlFragment, "%smessage=%s", auth_server->authserv_msg_script_path_fragment, GATEWAY_MESSAGE_ACTIVATE_ACCOUNT ); http_send_redirect_to_auth(r, urlFragment, "Redirect to activate message"); free(urlFragment); break; case AUTH_ALLOWED: /* Logged in successfully as a regular account */ debug(LOG_INFO, "Got ALLOWED from central server authenticating token %s from %s at %s - " "adding to firewall and redirecting them to portal", client->token, client->ip, client->mac); client->fw_connection_state = FW_MARK_KNOWN; fw_allow(client->ip, client->mac, FW_MARK_KNOWN); served_this_session++; if(click_ad != NULL) { safe_asprintf(&urlFragment,"%sgw_id=%s&click_ad=%s", auth_server->authserv_portal_script_path_fragment, config->gw_id, click_ad->value); } else { safe_asprintf(&urlFragment, "%sgw_id=%s", auth_server->authserv_portal_script_path_fragment, config->gw_id ); } http_send_redirect_to_auth(r, urlFragment, "Redirect to portal"); free(urlFragment); break; case AUTH_VALIDATION_FAILED: /* Client had X minutes to validate account by email and didn't = too late */ debug(LOG_INFO, "Got VALIDATION_FAILED from central server authenticating token %s from %s at %s " "- redirecting them to failed_validation message", client->token, client->ip, client->mac); safe_asprintf(&urlFragment, "%smessage=%s", auth_server->authserv_msg_script_path_fragment, GATEWAY_MESSAGE_ACCOUNT_VALIDATION_FAILED ); http_send_redirect_to_auth(r, urlFragment, "Redirect to failed validation message"); free(urlFragment); break; default: debug(LOG_WARNING, "I don't know what the validation code %d means for token %s from %s at %s - sending error message", auth_response.authcode, client->token, client->ip, client->mac); send_http_page(r, "Internal Error", "We can not validate your request at this time"); break; } UNLOCK_CLIENT_LIST(); return; }
/** @internal * This function does the actual request. */ static void ping(void) { char request[MAX_BUF]; FILE *fh; int sockfd; unsigned long int sys_uptime = 0; unsigned int sys_memfree = 0; float sys_load = 0; t_auth_serv *auth_server = NULL; auth_server = get_auth_server(); static int authdown = 0; debug(LOG_DEBUG, "Entering ping()"); memset(request, 0, sizeof(request)); /* * The ping thread does not really try to see if the auth server is actually * working. Merely that there is a web server listening at the port. And that * is done by connect_auth_server() internally. */ sockfd = connect_auth_server(); if (sockfd == -1) { /* * No auth servers for me to talk to */ if (!authdown) { fw_set_authdown(); authdown = 1; } return; } /* * Populate uptime, memfree and load */ if ((fh = fopen("/proc/uptime", "r"))) { if (fscanf(fh, "%lu", &sys_uptime) != 1) debug(LOG_CRIT, "Failed to read uptime"); fclose(fh); } if ((fh = fopen("/proc/meminfo", "r"))) { while (!feof(fh)) { if (fscanf(fh, "MemFree: %u", &sys_memfree) == 0) { /* Not on this line */ while (!feof(fh) && fgetc(fh) != '\n') ; } else { /* Found it */ break; } } fclose(fh); } if ((fh = fopen("/proc/loadavg", "r"))) { if (fscanf(fh, "%f", &sys_load) != 1) debug(LOG_CRIT, "Failed to read loadavg"); fclose(fh); } /* * Prep & send request */ snprintf(request, sizeof(request) - 1, "GET %s%sgw_id=%s&sys_uptime=%lu&sys_memfree=%u&sys_load=%.2f&wifidog_uptime=%lu&prop=%s&network_id=%s&lat=%s&lon=%s&node_name=%s HTTP/1.0\r\n" "User-Agent: WiFiDog-ffw %s\r\n" "Host: %s\r\n" "\r\n", auth_server->authserv_path, auth_server->authserv_ping_script_path_fragment, config_get_config()->gw_id, sys_uptime, sys_memfree, sys_load, (long unsigned int)((long unsigned int)time(NULL) - (long unsigned int)started_time), config_get_config()->owner, config_get_config()->network, config_get_config()->lat, config_get_config()->lon, config_get_config()->node_name, VERSION, auth_server->authserv_hostname); char *res; #ifdef USE_CYASSL if (auth_server->authserv_use_ssl) { res = https_get(sockfd, request, auth_server->authserv_hostname); } else { res = http_get(sockfd, request); } #endif #ifndef USE_CYASSL res = http_get(sockfd, request); #endif if (NULL == res) { debug(LOG_ERR, "There was a problem pinging the auth server!"); if (!authdown) { fw_set_authdown(); authdown = 1; } } else if (strstr(res, "Pong") == 0) { debug(LOG_WARNING, "Auth server did NOT say Pong!"); if (!authdown) { fw_set_authdown(); authdown = 1; } free(res); } else { debug(LOG_DEBUG, "Auth Server Says: Pong"); if (authdown) { fw_set_authup(); authdown = 0; } free(res); } return; }
void logging_access (client_t *client) { ice_config_t *config = config_get_config(); logging_access_id (&config->access_log, client); config_release_config (); }
/* client has requested a file, so check for it and send the file. Do not * refer to the client_t afterwards. return 0 for success, -1 on error. */ int fserve_client_create (client_t *httpclient, const char *path) { int bytes; struct stat file_buf; const char *range = NULL; off_t new_content_len = 0; off_t rangenumber = 0, content_length; int rangeproblem = 0; int ret = 0; char *fullpath; int m3u_requested = 0, m3u_file_available = 1; const char * xslt_playlist_requested = NULL; int xslt_playlist_file_available = 1; ice_config_t *config; FILE *file; fullpath = util_get_path_from_normalised_uri (path); INFO2 ("checking for file %H (%H)", path, fullpath); if (strcmp (util_get_extension (fullpath), "m3u") == 0) m3u_requested = 1; if (strcmp (util_get_extension (fullpath), "xspf") == 0) xslt_playlist_requested = "xspf.xsl"; if (strcmp (util_get_extension (fullpath), "vclt") == 0) xslt_playlist_requested = "vclt.xsl"; /* check for the actual file */ if (stat (fullpath, &file_buf) != 0) { /* the m3u can be generated, but send an m3u file if available */ if (m3u_requested == 0 && xslt_playlist_requested == NULL) { WARN2 ("req for file \"%H\" %s", fullpath, strerror (errno)); client_send_404 (httpclient, "The file you requested could not be found"); free (fullpath); return -1; } m3u_file_available = 0; xslt_playlist_file_available = 0; } httpclient->refbuf->len = PER_CLIENT_REFBUF_SIZE; if (m3u_requested && m3u_file_available == 0) { const char *host = httpp_getvar (httpclient->parser, "host"); char *sourceuri = strdup (path); char *dot = strrchr(sourceuri, '.'); /* at least a couple of players (fb2k/winamp) are reported to send a * host header but without the port number. So if we are missing the * port then lets treat it as if no host line was sent */ if (host && strchr (host, ':') == NULL) host = NULL; *dot = 0; httpclient->respcode = 200; if (host == NULL) { config = config_get_config(); snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.0 200 OK\r\n" "Content-Type: audio/x-mpegurl\r\n\r\n" "http://%s%s\r\n", config->hostname, sourceuri ); config_release_config(); } else { snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.0 200 OK\r\n" "Content-Type: audio/x-mpegurl\r\n\r\n" "http://%s%s\r\n", host, sourceuri ); } httpclient->refbuf->len = strlen (httpclient->refbuf->data); fserve_add_client (httpclient, NULL); free (sourceuri); free (fullpath); return 0; } if (xslt_playlist_requested && xslt_playlist_file_available == 0) { xmlDocPtr doc; char *reference = strdup (path); char *eol = strrchr (reference, '.'); if (eol) *eol = '\0'; doc = stats_get_xml (0, reference); free (reference); admin_send_response (doc, httpclient, TRANSFORMED, xslt_playlist_requested); xmlFreeDoc(doc); return 0; } /* on demand file serving check */ config = config_get_config(); if (config->fileserve == 0) { DEBUG1 ("on demand file \"%H\" refused", fullpath); client_send_404 (httpclient, "The file you requested could not be found"); config_release_config(); free (fullpath); return -1; } config_release_config(); if (S_ISREG (file_buf.st_mode) == 0) { client_send_404 (httpclient, "The file you requested could not be found"); WARN1 ("found requested file but there is no handler for it: %H", fullpath); free (fullpath); return -1; } file = fopen (fullpath, "rb"); if (file == NULL) { WARN1 ("Problem accessing file \"%H\"", fullpath); client_send_404 (httpclient, "File not readable"); free (fullpath); return -1; } free (fullpath); content_length = file_buf.st_size; range = httpp_getvar (httpclient->parser, "range"); /* full http range handling is currently not done but we deal with the common case */ if (range != NULL) { ret = 0; if (strncasecmp (range, "bytes=", 6) == 0) ret = sscanf (range+6, "%" SCN_OFF_T "-", &rangenumber); if (ret != 1) { /* format not correct, so lets just assume we start from the beginning */ rangeproblem = 1; } if (rangenumber < 0) { rangeproblem = 1; } if (!rangeproblem) { ret = fseeko (file, rangenumber, SEEK_SET); if (ret != -1) { new_content_len = content_length - rangenumber; if (new_content_len < 0) { rangeproblem = 1; } } else { rangeproblem = 1; } if (!rangeproblem) { /* Date: is required on all HTTP1.1 responses */ char currenttime[50]; time_t now; int strflen; struct tm result; off_t endpos = rangenumber+new_content_len-1; char *type; if (endpos < 0) { endpos = 0; } time(&now); strflen = strftime(currenttime, 50, "%a, %d-%b-%Y %X GMT", gmtime_r(&now, &result)); httpclient->respcode = 206; type = fserve_content_type (path); bytes = snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.1 206 Partial Content\r\n" "Date: %s\r\n" "Accept-Ranges: bytes\r\n" "Content-Length: %" PRI_OFF_T "\r\n" "Content-Range: bytes %" PRI_OFF_T \ "-%" PRI_OFF_T "/%" PRI_OFF_T "\r\n" "Content-Type: %s\r\n\r\n", currenttime, new_content_len, rangenumber, endpos, content_length, type); free (type); } else { goto fail; } } else { goto fail; } } else { char *type = fserve_content_type(path); httpclient->respcode = 200; bytes = snprintf (httpclient->refbuf->data, BUFSIZE, "HTTP/1.0 200 OK\r\n" "Accept-Ranges: bytes\r\n" "Content-Length: %" PRI_OFF_T "\r\n" "Content-Type: %s\r\n\r\n", content_length, type); free (type); } httpclient->refbuf->len = bytes; httpclient->pos = 0; stats_event_inc (NULL, "file_connections"); fserve_add_client (httpclient, file); return 0; fail: fclose (file); httpclient->respcode = 416; sock_write (httpclient->con->sock, "HTTP/1.0 416 Request Range Not Satisfiable\r\n\r\n"); client_destroy (httpclient); return -1; }