/** * nm_netlink_route6_add: * @route: the route to add * @dest: the route destination address * @dest_prefix: the CIDR prefix of @dest * @gateway: the gateway through which to reach @dest, if any * @flags: flags to pass to rtnl_route_add(), eg %NLM_F_REPLACE * * Adds an IPv6 route with the given parameters. * * Returns: zero if succeeded or the netlink error otherwise. **/ int nm_netlink_route6_add (struct rtnl_route *route, const struct in6_addr *dst, int prefix, const struct in6_addr *gw, int flags) { return _route_add (route, AF_INET6, dst, prefix, gw, flags); }
/** * nm_netlink_route4_add: * @route: the route to add * @dest: the route destination address in network byte order * @dest_prefix: the CIDR prefix of @dest * @gateway: the gateway through which to reach @dest, if any, in network byte order * @flags: flags to pass to rtnl_route_add(), eg %NLM_F_REPLACE * * Adds an IPv4 route with the given parameters. * * Returns: zero if succeeded or the netlink error otherwise. **/ int nm_netlink_route4_add (struct rtnl_route *route, guint32 *dst, int prefix, guint32 *gw, int flags) { return _route_add (route, AF_INET, dst, prefix, gw, flags); }
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; }
static void _router_process_bind(component_t comp, nad_t nad) { int attr, multi, n; jid_t name; alias_t alias; char *user, *c; attr = nad_find_attr(nad, 0, -1, "name", NULL); if(attr < 0 || (name = jid_new(NAD_AVAL(nad, attr), NAD_AVAL_L(nad, attr))) == NULL) { log_debug(ZONE, "no or invalid 'name' on bind packet, bouncing"); nad_set_attr(nad, 0, -1, "error", "400", 3); sx_nad_write(comp->s, nad); return; } user = strdup(comp->s->auth_id); c = strchr(user, '@'); if(c != NULL) *c = '\0'; if(strcmp(user, name->domain) != 0 && !aci_check(comp->r->aci, "bind", user)) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to bind '%s', but their username (%s) is not permitted to bind other names", comp->ip, comp->port, name->domain, user); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "403", 3); sx_nad_write(comp->s, nad); jid_free(name); free(user); return; } multi = nad_find_attr(nad, 0, -1, "multi", NULL); if(xhash_get(comp->r->routes, name->domain) != NULL && multi < 0) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to bind '%s', but it's already bound", comp->ip, comp->port, name->domain); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "409", 3); sx_nad_write(comp->s, nad); jid_free(name); free(user); return; } for(alias = comp->r->aliases; alias != NULL; alias = alias->next) if(strcmp(alias->name, name->domain) == 0) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to bind '%s', but that name is aliased", comp->ip, comp->port); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "409", 3); sx_nad_write(comp->s, nad); jid_free(name); free(user); return; } /* default route */ if(nad_find_elem(nad, 0, NAD_ENS(nad, 0), "default", 1) >= 0) { if(!aci_check(comp->r->aci, "default-route", user)) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to bind '%s' as the default route, but their username (%s) is not permitted to set a default route", comp->ip, comp->port, name->domain, user); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "403", 3); sx_nad_write(comp->s, nad); jid_free(name); free(user); return; } if(comp->r->default_route != NULL) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to bind '%s' as the default route, but one already exists", comp->ip, comp->port, name->domain); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "409", 3); sx_nad_write(comp->s, nad); jid_free(name); return; } log_write(comp->r->log, LOG_NOTICE, "[%s] set as default route", name->domain); comp->r->default_route = strdup(name->domain); } /* log sinks */ if(nad_find_elem(nad, 0, NAD_ENS(nad, 0), "log", 1) >= 0) { if(!aci_check(comp->r->aci, "log", user)) { log_write(comp->r->log, LOG_NOTICE, "[%s, port=%d] tried to bind '%s' as a log sink, but their username (%s) is not permitted to do this", comp->ip, comp->port, name->domain, user); nad_set_attr(nad, 0, -1, "name", NULL, 0); nad_set_attr(nad, 0, -1, "error", "403", 3); sx_nad_write(comp->s, nad); jid_free(name); free(user); return; } log_write(comp->r->log, LOG_NOTICE, "[%s] set as log sink", name->domain); xhash_put(comp->r->log_sinks, pstrdup(xhash_pool(comp->r->log_sinks), name->domain), (void *) comp); } free(user); n = _route_add(comp->r->routes, name->domain, comp, multi<0?route_SINGLE:route_MULTI_TO); xhash_put(comp->routes, pstrdup(xhash_pool(comp->routes), name->domain), (void *) comp); if(n>1) log_write(comp->r->log, LOG_NOTICE, "[%s]:%d online (bound to %s, port %d)", name->domain, n, comp->ip, comp->port); else log_write(comp->r->log, LOG_NOTICE, "[%s] online (bound to %s, port %d)", name->domain, comp->ip, comp->port); nad_set_attr(nad, 0, -1, "name", NULL, 0); sx_nad_write(comp->s, nad); /* advertise name */ _router_advertise(comp->r, name->domain, comp, 0); /* tell the new component about everyone else */ xhash_walk(comp->r->routes, _router_advertise_reverse, (void *) comp); /* bind aliases */ for(alias = comp->r->aliases; alias != NULL; alias = alias->next) { if(strcmp(alias->target, name->domain) == 0) { _route_add(comp->r->routes, name->domain, comp, route_MULTI_TO); 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, name->domain, comp->ip, comp->port); /* advertise name */ _router_advertise(comp->r, alias->name, comp, 0); } } /* done with this */ jid_free(name); }