static int _router_accept_check(router_t r, mio_fd_t fd, const char *ip) { rate_t rt; if(access_check(r->access, ip) == 0) { log_write(r->log, LOG_NOTICE, "[%d] [%s] access denied by configuration", fd->fd, ip); return 1; } if(r->conn_rate_total != 0) { rt = (rate_t) xhash_get(r->conn_rates, ip); if(rt == NULL) { rt = rate_new(r->conn_rate_total, r->conn_rate_seconds, r->conn_rate_wait); xhash_put(r->conn_rates, pstrdup(xhash_pool(r->conn_rates), ip), (void *) rt); } if(rate_check(rt) == 0) { log_write(r->log, LOG_NOTICE, "[%d] [%s] is being rate limited", fd->fd, ip); return 1; } rate_add(rt, 1); } return 0; }
void global_add_bitrates (struct rate_calc *rate, unsigned long value, uint64_t milli) { float avg; thread_spin_lock (&global.spinlock); rate_add (rate, value, milli); avg = rate_avg (rate); if (global.max_rate) { float ratio = avg / global.max_rate; if (ratio > 0.99) throttle_sends = 3; else if (ratio > 0.9) throttle_sends = 2; else if (ratio > 0.8) throttle_sends = 1; else if (throttle_sends > 0) throttle_sends--; } thread_spin_unlock (&global.spinlock); }
// Rate limit check: Prevent denial-of-service due to excessive database queries // Make sure owner is responsible for the query! int sm_storage_rate_limit(sm_t sm, const char *owner) { rate_t rt; user_t user; sess_t sess; item_t item; if (sm->query_rate_total == 0 || owner == NULL) return FALSE; user = xhash_get(sm->users, owner); if (user != NULL) { rt = (rate_t) xhash_get(sm->query_rates, owner); if (rt == NULL) { rt = rate_new(sm->query_rate_total, sm->query_rate_seconds, sm->query_rate_wait); xhash_put(sm->query_rates, pstrdup(xhash_pool(sm->query_rates), owner), (void *) rt); pool_cleanup(xhash_pool(sm->query_rates), (void (*)(void *)) rate_free, rt); } if(rate_check(rt) == 0) { log_write(sm->log, LOG_WARNING, "[%s] is being disconnected, too many database queries within %d seconds", owner, sm->query_rate_seconds); user = xhash_get(sm->users, owner); for (sess = user->sessions; sess != NULL; sess = sess->next) { sm_c2s_action(sess, "ended", NULL); } if(xhash_iter_first(user->roster)) do { xhash_iter_get(user->roster, NULL, NULL, (void *) &item); if(item->to) { pkt_router(pkt_create(user->sm, "presence", "unavailable", jid_full(item->jid), jid_full(user->jid))); } } while(xhash_iter_next(user->roster)); return TRUE; } else { rate_add(rt, 1); } } else { log_debug(ZONE, "Error: could not get user data for %s", owner); } return FALSE; }
/* send routine for files sent at a target bitrate, eg fallback files. */ static int throttled_file_send (client_t *client) { int bytes; fh_node *fh = client->shared_data; time_t now; worker_t *worker = client->worker; unsigned long secs; unsigned int rate = 0; unsigned int limit = fh->finfo.limit; if (fserve_running == 0 || client->connection.error) return -1; now = worker->current_time.tv_sec; secs = now - client->timer_start; client->schedule_ms = worker->time_ms; if (fh->finfo.fallback) return fserve_move_listener (client); if (fserve_change_worker (client)) // allow for balancing return 1; if (client->flags & CLIENT_WANTS_FLV) /* increase limit for flv clients as wrapping takes more space */ limit = (unsigned long)(limit * 1.01); rate = secs ? (client->counter+1400)/secs : limit * 2; // DEBUG3 ("counter %lld, duration %ld, limit %u", client->counter, secs, rate); if (rate > limit) { if (limit >= 1400) client->schedule_ms += 1000/(limit/1400); else client->schedule_ms += 50; // should not happen but guard against it rate_add (fh->out_bitrate, 0, worker->time_ms); global_add_bitrates (global.out_bitrate, 0, worker->time_ms); if (client->counter > 8192) return 0; // allow an initial amount without throttling } switch (format_file_read (client, fh->format, fh->f)) { case -1: // DEBUG0 ("loop of file triggered"); client->intro_offset = 0; client->schedule_ms += client->throttle ? client->throttle : 150; return 0; case -2: // DEBUG0 ("major failure on read, better leave"); return -1; default: //DEBUG1 ("reading from offset %ld", client->intro_offset); break; } bytes = client->check_buffer (client); if (bytes < 0) bytes = 0; //DEBUG3 ("bytes %d, counter %ld, %ld", bytes, client->counter, client->worker->time_ms - (client->timer_start*1000)); rate_add (fh->out_bitrate, bytes, worker->time_ms); global_add_bitrates (global.out_bitrate, bytes, worker->time_ms); if (limit > 2800) client->schedule_ms += (1000/(limit/1400*2)); else client->schedule_ms += 50; /* progessive slowdown if max bandwidth is exceeded. */ if (throttle_sends > 1) client->schedule_ms += 300; return 0; }
static int prefile_send (client_t *client) { int loop = 8, bytes, written = 0; worker_t *worker = client->worker; while (loop) { refbuf_t *refbuf = client->refbuf; fh_node *fh = client->shared_data; loop--; if (fserve_running == 0 || client->connection.error) return -1; if (refbuf == NULL || client->pos == refbuf->len) { if (fh->finfo.fallback && (client->flags & CLIENT_AUTHENTICATED)) return fserve_move_listener (client); if (refbuf == NULL || refbuf->next == NULL) { if ((client->flags & CLIENT_AUTHENTICATED) == 0) return -1; if (file_in_use (fh->f)) // is there a file to read from { refbuf_release (client->refbuf); client->refbuf = NULL; client->pos = 0; if (fh->finfo.limit) { client->ops = &throttled_file_content_ops; rate_add (fh->out_bitrate, 0, worker->time_ms); return 0; } client->ops = &file_content_ops; return client->ops->process (client); } if (client->respcode) return -1; return client_send_404 (client, NULL); } else { refbuf_t *to_go = client->refbuf; refbuf = client->refbuf = to_go->next; to_go->next = NULL; refbuf_release (to_go); } client->pos = 0; } if (refbuf->flags & WRITE_BLOCK_GENERIC) bytes = format_generic_write_to_client (client); else bytes = client->check_buffer (client); if (bytes < 0) { client->schedule_ms = worker->time_ms + (written ? 150 : 300); return 0; } written += bytes; global_add_bitrates (global.out_bitrate, bytes, worker->time_ms); if (written > 30000) break; } return 0; }
static int _router_sx_callback(sx_t s, sx_event_t e, void *data, void *arg) { component_t comp = (component_t) arg; sx_buf_t buf = (sx_buf_t) data; int rlen, len, attr, ns, sns, n; sx_error_t *sxe; nad_t nad; struct jid_st sto, sfrom; jid_static_buf sto_buf, sfrom_buf; jid_t to, from; alias_t alias; /* init static jid */ jid_static(&sto,&sto_buf); jid_static(&sfrom,&sfrom_buf); switch(e) { case event_WANT_READ: log_debug(ZONE, "want read"); mio_read(comp->r->mio, comp->fd); break; case event_WANT_WRITE: log_debug(ZONE, "want write"); mio_write(comp->r->mio, comp->fd); break; case event_READ: log_debug(ZONE, "reading from %d", comp->fd->fd); /* check rate limits */ if(comp->rate != NULL) { if(rate_check(comp->rate) == 0) { /* inform the app if we haven't already */ if(!comp->rate_log) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] is being byte rate limited", comp->ip, comp->port); comp->rate_log = 1; } log_debug(ZONE, "%d is throttled, delaying read", comp->fd->fd); buf->len = 0; return 0; } /* find out how much we can have */ rlen = rate_left(comp->rate); if(rlen > buf->len) rlen = buf->len; } /* no limit, just read as much as we can */ else rlen = buf->len; /* do the read */ len = recv(comp->fd->fd, buf->data, rlen, 0); /* update rate limits */ if(comp->rate != NULL && len > 0) { comp->rate_log = 0; rate_add(comp->rate, len); } if(len < 0) { if(MIO_WOULDBLOCK) { buf->len = 0; return 0; } log_debug(ZONE, "read failed: %s", strerror(errno)); sx_kill(comp->s); return -1; } else if(len == 0) { /* they went away */ sx_kill(comp->s); return -1; } log_debug(ZONE, "read %d bytes", len); buf->len = len; return len; case event_WRITE: log_debug(ZONE, "writing to %d", comp->fd->fd); len = send(comp->fd->fd, buf->data, buf->len, 0); if(len >= 0) { log_debug(ZONE, "%d bytes written", len); return len; } if(MIO_WOULDBLOCK) return 0; log_debug(ZONE, "write failed: %s", strerror(errno)); sx_kill(comp->s); return -1; case event_ERROR: sxe = (sx_error_t *) data; log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] error: %s (%s)", comp->ip, comp->port, sxe->generic, sxe->specific); break; case event_STREAM: /* legacy check */ if(s->ns == NULL || strcmp("jabber:component:accept", s->ns) != 0) return 0; /* component, old skool */ comp->legacy = 1; /* enabled? */ if(comp->r->local_secret == NULL) { sx_error(s, stream_err_INVALID_NAMESPACE, "support for legacy components not available"); /* !!! correct error? */ sx_close(s); return 0; } /* sanity */ if(s->req_to == NULL) { sx_error(s, stream_err_HOST_UNKNOWN, "no 'to' attribute on stream header"); sx_close(s); return 0; } break; case event_OPEN: log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] authenticated as %s", comp->ip, comp->port, comp->s->auth_id); /* make a route for legacy components */ if(comp->legacy) { for(alias = comp->r->aliases; alias != NULL; alias = alias->next) if(strcmp(alias->name, s->req_to) == 0) { sx_error(s, stream_err_HOST_UNKNOWN, "requested name is aliased"); /* !!! correct error? */ sx_close(s); return 0; } n = _route_add(comp->r->routes, s->req_to, comp, route_MULTI_FROM); xhash_put(comp->routes, pstrdup(xhash_pool(comp->routes), s->req_to), (void *) comp); if(n>1) log_write(comp->r->log, LOG_NOTICE, "[%s]:%d online (bound to %s, port %d)", s->req_to, n, comp->ip, comp->port); else log_write(comp->r->log, LOG_NOTICE, "[%s] online (bound to %s, port %d)", s->req_to, comp->ip, comp->port); /* advertise the name */ _router_advertise(comp->r, s->req_to, comp, 0); /* this is a legacy component, so we don't tell it about other routes */ /* bind aliases */ for(alias = comp->r->aliases; alias != NULL; alias = alias->next) { if(strcmp(alias->target, s->req_to) == 0) { _route_add(comp->r->routes, alias->name, comp, route_MULTI_FROM); xhash_put(comp->routes, pstrdup(xhash_pool(comp->routes), alias->name), (void *) comp); log_write(comp->r->log, LOG_NOTICE, "[%s] online (alias of '%s', bound to %s, port %d)", alias->name, s->req_to, comp->ip, comp->port); /* advertise name */ _router_advertise(comp->r, alias->name, comp, 0); } } } break; case event_PACKET: nad = (nad_t) data; /* preauth */ if(comp->s->state == state_STREAM) { /* non-legacy components can't do anything before auth */ if(!comp->legacy) { log_debug(ZONE, "stream is preauth, dropping packet"); nad_free(nad); return 0; } /* watch for handshake requests */ if(NAD_ENAME_L(nad, 0) != 9 || strncmp("handshake", NAD_ENAME(nad, 0), NAD_ENAME_L(nad, 0)) != 0) { log_debug(ZONE, "unknown preauth packet %.*s, dropping", NAD_ENAME_L(nad, 0), NAD_ENAME(nad, 0)); nad_free(nad); return 0; } /* process incoming handshakes */ _router_process_handshake(comp, nad); return 0; } /* legacy processing */ if(comp->legacy) { log_debug(ZONE, "packet from legacy component, munging it"); attr = nad_find_attr(nad, 0, -1, "to", NULL); if(attr < 0 || (to = jid_reset(&sto, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "invalid or missing 'to' address on legacy packet, dropping it"); nad_free(nad); return 0; } attr = nad_find_attr(nad, 0, -1, "from", NULL); if(attr < 0 || (from = jid_reset(&sfrom, NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "invalid or missing 'from' address on legacy packet, dropping it"); nad_free(nad); return 0; } /* rewrite component packets into client packets */ ns = nad_find_namespace(nad, 0, "jabber:component:accept", NULL); if(ns >= 0) { if(nad->elems[0].ns == ns) nad->elems[0].ns = nad->nss[nad->elems[0].ns].next; else { for(sns = nad->elems[0].ns; sns >= 0 && nad->nss[sns].next != ns; sns = nad->nss[sns].next); nad->nss[sns].next = nad->nss[nad->nss[sns].next].next; } } ns = nad_find_namespace(nad, 0, uri_CLIENT, NULL); if(ns < 0) { ns = nad_add_namespace(nad, uri_CLIENT, NULL); nad->scope = -1; nad->nss[ns].next = nad->elems[0].ns; nad->elems[0].ns = ns; } nad->elems[0].my_ns = ns; /* wrap up the packet */ ns = nad_add_namespace(nad, uri_COMPONENT, NULL); nad_wrap_elem(nad, 0, ns, "route"); nad_set_attr(nad, 0, -1, "to", to->domain, 0); nad_set_attr(nad, 0, -1, "from", from->domain, 0); } /* top element must be router scoped */ if(NAD_ENS(nad, 0) < 0 || NAD_NURI_L(nad, NAD_ENS(nad, 0)) != strlen(uri_COMPONENT) || strncmp(uri_COMPONENT, NAD_NURI(nad, NAD_ENS(nad, 0)), strlen(uri_COMPONENT)) != 0) { log_debug(ZONE, "invalid packet namespace, dropping"); nad_free(nad); return 0; } /* bind a name to this component */ if(NAD_ENAME_L(nad, 0) == 4 && strncmp("bind", NAD_ENAME(nad, 0), 4) == 0) { _router_process_bind(comp, nad); return 0; } /* unbind a name from this component */ if(NAD_ENAME_L(nad, 0) == 6 && strncmp("unbind", NAD_ENAME(nad, 0), 6) == 0) { _router_process_unbind(comp, nad); return 0; } /* route packets */ if(NAD_ENAME_L(nad, 0) == 5 && strncmp("route", NAD_ENAME(nad, 0), 5) == 0) { _router_process_route(comp, nad); return 0; } /* throttle packets */ if(NAD_ENAME_L(nad, 0) == 8 && strncmp("throttle", NAD_ENAME(nad, 0), 8) == 0) { _router_process_throttle(comp, nad); return 0; } log_debug(ZONE, "unknown packet, dropping"); nad_free(nad); return 0; case event_CLOSED: { /* close comp->fd by putting it in closefd ... unless it is already there */ _jqueue_node_t n; for (n = comp->r->closefd->front; n != NULL; n = n->prev) if (n->data == comp->fd) break; if (!n) jqueue_push(comp->r->closefd, (void *) comp->fd, 0 /*priority*/); return 0; } } return 0; }
void global_add_bitrates (struct rate_calc *rate, unsigned long value, uint64_t milli) { rate_add (rate, value, milli); }
/* main plugin handler for getting a buffer for the queue. In here we * just add an incoming page to the codecs and process it until either * more data is needed or we prodice a buffer for the queue. */ static refbuf_t *ogg_get_buffer (source_t *source) { ogg_state_t *ogg_info = source->format->_state; format_plugin_t *format = source->format; char *data = NULL; int bytes = 0; while (1) { while (1) { ogg_page page; refbuf_t *refbuf = NULL; ogg_codec_t *codec = ogg_info->current; /* if a codec has just been given a page then process it */ if (codec && codec->process) { refbuf = codec->process (ogg_info, codec); if (refbuf) return complete_buffer (source, refbuf); ogg_info->current = NULL; } if (ogg_sync_pageout (&ogg_info->oy, &page) > 0) { if (ogg_page_bos (&page)) { process_initial_page (source->format, &page); } else { ogg_info->bos_completed = 1; refbuf = process_ogg_page (ogg_info, &page); } if (ogg_info->error) { ERROR0 ("Problem processing stream"); source->flags &= ~SOURCE_RUNNING; return NULL; } if (refbuf) return complete_buffer (source, refbuf); continue; } /* need more stream data */ break; } /* we need more data to continue getting pages */ data = ogg_sync_buffer (&ogg_info->oy, 4096); bytes = client_read_bytes (source->client, data, 4096); if (bytes <= 0) { ogg_sync_wrote (&ogg_info->oy, 0); return NULL; } format->read_bytes += bytes; rate_add (format->in_bitrate, bytes, source->client->worker->current_time.tv_sec); ogg_sync_wrote (&ogg_info->oy, bytes); } }