/*
* This file is part of DisOrder.
- * Copyright (C) 2004-2009 Richard Kettlewell
+ * Copyright (C) 2004-2012 Richard Kettlewell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
struct listener {
const char *name;
int pf;
+ int privileged;
};
struct conn;
void *body_u;
/** @brief Accumulating body */
struct vector body[1];
+
+ /** @brief Nonzero if an active RTP request exists */
+ int rtp_requested;
+
+ /** @brief RTP destination (if @ref rtp_requested is nonzero) */
+ struct sockaddr_storage rtp_destination;
};
/** @brief Linked list of connections */
static const char *noyes[] = { "no", "yes" };
-/** @brief Remove a connection from the connection list */
+/** @brief Remove a connection from the connection list
+ *
+ * This is a good place for cleaning things up when connections are closed for
+ * any reason.
+ */
static void remove_connection(struct conn *c) {
struct conn **cc;
+ if(c->rtp_requested) {
+ rtp_request_cancel(&c->rtp_destination);
+ c->rtp_requested = 0;
+ }
for(cc = &connections; *cc && *cc != c; cc = &(*cc)->next)
;
if(*cc)
D(("S%x writer completed", c->tag));
} else {
if(errno_value != EPIPE)
- error(errno_value, "S%x write error on socket", c->tag);
+ disorder_error(errno_value, "S%x write error on socket", c->tag);
if(c->r) {
D(("cancel reader"));
ev_reader_cancel(c->r);
struct conn *c = u;
D(("server reader_error S%x %d", c->tag, errno_value));
- error(errno_value, "S%x read error on socket", c->tag);
+ disorder_error(errno_value, "S%x read error on socket", c->tag);
if(c->w)
ev_writer_close(c->w);
c->w = 0;
static int c_disable(struct conn *c, char **vec, int nvec) {
if(nvec == 0)
- disable_playing(c->who);
+ disable_playing(c->who, c->ev);
else if(nvec == 1 && !strcmp(vec[0], "now"))
- disable_playing(c->who);
+ disable_playing(c->who, c->ev);
else {
sink_writes(ev_writer_sink(c->w), "550 invalid argument\n");
return 1; /* completed */
sink_printf(ev_writer_sink(c->w), "550 No such ID\n");
return 1;
}
- info("added %s as %s after %s", track, q->id, afterme);
+ disorder_info("added %s as %s after %s", track, q->id, afterme);
afterme = q->id;
}
queue_write();
* prepare the same track twice so there's no point. */
if(qhead.next != &qhead) {
prepare(c->ev, qhead.next);
- info("prepared %s", qhead.next->id);
+ disorder_info("prepared %s", qhead.next->id);
}
/* If the queue was empty but we are for some reason paused then
* unpause. */
return 1;
}
if(!right_removable(c->rights, c->who, q)) {
- error(0, "%s attempted remove but lacks required rights", c->who);
+ disorder_error(0, "%s attempted remove but lacks required rights", c->who);
sink_writes(ev_writer_sink(c->w),
"510 Not authorized to remove that track\n");
return 1;
* playing track then you will get 550 if you weren't authorized to scratch
* the currently playing track. */
if(!right_scratchable(c->rights, c->who, playing)) {
- error(0, "%s attempted scratch but lacks required rights", c->who);
+ disorder_error(0, "%s attempted scratch but lacks required rights", c->who);
sink_writes(ev_writer_sink(c->w),
"510 Not authorized to scratch that track\n");
return 1;
static int c_shutdown(struct conn *c,
char attribute((unused)) **vec,
int attribute((unused)) nvec) {
- info("S%x shut down by %s", c->tag, c->who);
+ disorder_info("S%x shut down by %s", c->tag, c->who);
sink_writes(ev_writer_sink(c->w), "250 shutting down\n");
ev_writer_flush(c->w);
quit(c->ev);
static int c_reconfigure(struct conn *c,
char attribute((unused)) **vec,
int attribute((unused)) nvec) {
- info("S%x reconfigure by %s", c->tag, c->who);
+ disorder_info("S%x reconfigure by %s", c->tag, c->who);
if(reconfigure(c->ev, 1))
sink_writes(ev_writer_sink(c->w), "550 error reading new config\n");
else
}
}
/* Report what was requested */
- info("S%x rescan by %s (%s %s)", c->tag, c->who,
- flag_wait ? "wait" : "",
- flag_fresh ? "fresh" : "");
+ disorder_info("S%x rescan by %s (%s %s)", c->tag, c->who,
+ flag_wait ? "wait" : "",
+ flag_fresh ? "fresh" : "");
if(trackdb_rescan_underway()) {
if(flag_fresh) {
/* We want a fresh rescan but there is already one underway. Arrange a
/* get connection data */
l = sizeof u;
if(getpeername(c->fd, &u.sa, &l) < 0) {
- error(errno, "S%x error calling getpeername", c->tag);
+ disorder_error(errno, "S%x error calling getpeername", c->tag);
return 0;
}
if(c->l->pf != PF_UNIX) {
if((n = getnameinfo(&u.sa, l,
host, sizeof host, 0, 0, NI_NUMERICHOST))) {
- error(0, "S%x error calling getnameinfo: %s", c->tag, gai_strerror(n));
+ disorder_error(0, "S%x error calling getnameinfo: %s",
+ c->tag, gai_strerror(n));
return 0;
}
return xstrdup(host);
k = trackdb_getuserinfo(vec[0]);
/* reject nonexistent users */
if(!k) {
- error(0, "S%x unknown user '%s' from %s", c->tag, vec[0], host);
+ disorder_error(0, "S%x unknown user '%s' from %s", c->tag, vec[0], host);
sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
return 1;
}
/* reject unconfirmed users */
if(kvp_get(k, "confirmation")) {
- error(0, "S%x unconfirmed user '%s' from %s", c->tag, vec[0], host);
+ disorder_error(0, "S%x unconfirmed user '%s' from %s",
+ c->tag, vec[0], host);
sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
return 1;
}
password = kvp_get(k, "password");
if(!password) password = "";
if(parse_rights(kvp_get(k, "rights"), &rights, 1)) {
- error(0, "error parsing rights for %s", vec[0]);
+ disorder_error(0, "error parsing rights for %s", vec[0]);
sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
return 1;
}
/* check whether the response is right */
res = authhash(c->nonce, sizeof c->nonce, password,
config->authorization_algorithm);
- if(wideopen || (res && !strcmp(res, vec[1]))) {
+ if(wideopen || c->l->privileged || (res && !strcmp(res, vec[1]))) {
c->who = vec[0];
c->rights = rights;
/* currently we only bother logging remote connections */
if(strcmp(host, "local"))
- info("S%x %s connected from %s", c->tag, vec[0], host);
+ disorder_info("S%x %s connected from %s", c->tag, vec[0], host);
else
c->rights |= RIGHT__LOCAL;
sink_writes(ev_writer_sink(c->w), "230 OK\n");
return 1;
}
/* oops, response was wrong */
- info("S%x authentication failure for %s from %s", c->tag, vec[0], host);
+ disorder_info("S%x authentication failure for %s from %s",
+ c->tag, vec[0], host);
sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
return 1;
}
if((l = trackdb_get(playing->track, "_length"))
&& (length = atol(l))) {
xtime(&when);
- when += length - playing->sofar + config->gap;
+ when += length - playing->sofar;
}
} else
/* Nothing is playing but playing is enabled, so whatever is
if(when) {
if((l = trackdb_get(q->track, "_length"))
&& (length = atol(l)))
- when += length + config->gap;
+ when += length;
else
when = 0;
}
static int c_random_disable(struct conn *c,
char attribute((unused)) **vec,
int attribute((unused)) nvec) {
- disable_random(c->who);
+ disable_random(c->who, c->ev);
sink_writes(ev_writer_sink(c->w), "250 OK\n");
return 1; /* completed */
}
}
rights = set ? RIGHT_VOLUME : RIGHT_READ;
if(!(c->rights & rights)) {
- error(0, "%s attempted to set volume but lacks required rights", c->who);
+ disorder_error(0, "%s attempted to set volume but lacks required rights",
+ c->who);
sink_writes(ev_writer_sink(c->w), "510 Prohibited\n");
return 1;
}
return 1;
}
if(!has_move_rights(c, &q, 1)) {
- error(0, "%s attempted move but lacks required rights", c->who);
+ disorder_error(0, "%s attempted move but lacks required rights", c->who);
sink_writes(ev_writer_sink(c->w),
"510 Not authorized to move that track\n");
return 1;
return 1;
}
if(!has_move_rights(c, qs, nvec)) {
- error(0, "%s attempted moveafter but lacks required rights", c->who);
+ disorder_error(0, "%s attempted moveafter but lacks required rights",
+ c->who);
sink_writes(ev_writer_sink(c->w),
"510 Not authorized to move those tracks\n");
return 1;
sink_writes(ev_writer_sink(c->w), "550 cannot set internal global preferences\n");
return 1;
}
- trackdb_set_global(vec[0], vec[1], c->who);
- sink_printf(ev_writer_sink(c->w), "250 OK\n");
+ /* We special-case the 'magic' preferences here. */
+ if(!strcmp(vec[0], "playing")) {
+ (flag_enabled(vec[1]) ? enable_playing : disable_playing)(c->who, c->ev);
+ sink_printf(ev_writer_sink(c->w), "250 OK\n");
+ } else if(!strcmp(vec[0], "random-play")) {
+ (flag_enabled(vec[1]) ? enable_random : disable_random)(c->who, c->ev);
+ sink_printf(ev_writer_sink(c->w), "250 OK\n");
+ } else {
+ if(!trackdb_set_global(vec[0], vec[1], c->who))
+ sink_printf(ev_writer_sink(c->w), "250 OK\n");
+ else
+ sink_writes(ev_writer_sink(c->w), "550 not found\n");
+ }
return 1;
}
static int c_new(struct conn *c,
char **vec,
int nvec) {
- int max, n;
+ int max;
char **tracks;
if(nvec > 0)
max = config->new_max;
tracks = trackdb_new(0, max);
sink_printf(ev_writer_sink(c->w), "253 New track list follows\n");
- n = 0;
while(*tracks) {
sink_printf(ev_writer_sink(c->w), "%s%s\n",
**tracks == '.' ? "." : "", *tracks);
if(api == &uaudio_rtp) {
char **addr;
- netaddress_format(&config->broadcast, NULL, &addr);
- sink_printf(ev_writer_sink(c->w), "252 %s %s\n",
- quoteutf8(addr[1]),
- quoteutf8(addr[2]));
+ if(!strcmp(config->rtp_mode, "request"))
+ sink_printf(ev_writer_sink(c->w), "252 - -\n");
+ else {
+ netaddress_format(&config->broadcast, NULL, &addr);
+ sink_printf(ev_writer_sink(c->w), "252 %s %s\n",
+ quoteutf8(addr[1]),
+ quoteutf8(addr[2]));
+ }
} else
sink_writes(ev_writer_sink(c->w), "550 No RTP\n");
return 1;
}
+static int c_rtp_cancel(struct conn *c,
+ char attribute((unused)) **vec,
+ int attribute((unused)) nvec) {
+ if(!c->rtp_requested) {
+ sink_writes(ev_writer_sink(c->w), "550 No active RTP stream\n");
+ return 1;
+ }
+ rtp_request_cancel(&c->rtp_destination);
+ c->rtp_requested = 0;
+ sink_writes(ev_writer_sink(c->w), "250 Cancelled RTP stream\n");
+ return 1;
+}
+
+static int c_rtp_request(struct conn *c,
+ char **vec,
+ int attribute((unused)) nvec) {
+ static const struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ .ai_protocol = IPPROTO_UDP,
+ .ai_flags = AI_NUMERICHOST|AI_NUMERICSERV,
+ };
+ struct addrinfo *res;
+ int rc = getaddrinfo(vec[0], vec[1], &hints, &res);
+ if(rc) {
+ disorder_error(0, "%s port %s: %s",
+ vec[0], vec[1], gai_strerror(rc));
+ sink_writes(ev_writer_sink(c->w), "550 Invalid address\n");
+ return 1;
+ }
+ disorder_info("%s requested RTP stream to %s %s", c->who, vec[0], vec[1]);
+ /* TODO might be useful to tighten this up to restrict clients to targetting
+ * themselves only */
+ if(c->rtp_requested) {
+ rtp_request_cancel(&c->rtp_destination);
+ c->rtp_requested = 0;
+ }
+ memcpy(&c->rtp_destination, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ rtp_request(&c->rtp_destination);
+ c->rtp_requested = 1;
+ sink_writes(ev_writer_sink(c->w), "250 Initiated RTP stream\n");
+ // TODO teardown on connection close
+ return 1;
+}
+
static int c_cookie(struct conn *c,
char **vec,
int attribute((unused)) nvec) {
c->cookie = vec[0];
c->rights = rights;
if(strcmp(host, "local"))
- info("S%x %s connected with cookie from %s", c->tag, user, host);
+ disorder_info("S%x %s connected with cookie from %s", c->tag, user, host);
else
c->rights |= RIGHT__LOCAL;
/* Response contains username so client knows who they are acting as */
revoke_cookie(c->cookie);
sink_writes(ev_writer_sink(c->w), "250 OK\n");
} else
- sink_writes(ev_writer_sink(c->w), "550 Did not log in with cookie\n");
+ sink_writes(ev_writer_sink(c->w), "510 Did not log in with cookie\n");
return 1;
}
const char *rights;
if(!config->remote_userman && !(c->rights & RIGHT__LOCAL)) {
- error(0, "S%x: remote adduser", c->tag);
- sink_writes(ev_writer_sink(c->w), "550 Remote user management is disabled\n");
+ disorder_error(0, "S%x: remote adduser", c->tag);
+ sink_writes(ev_writer_sink(c->w), "510 Remote user management is disabled\n");
return 1;
}
if(nvec > 2) {
struct conn *d;
if(!config->remote_userman && !(c->rights & RIGHT__LOCAL)) {
- error(0, "S%x: remote deluser", c->tag);
- sink_writes(ev_writer_sink(c->w), "550 Remote user management is disabled\n");
+ disorder_error(0, "S%x: remote deluser", c->tag);
+ sink_writes(ev_writer_sink(c->w), "510 Remote user management is disabled\n");
return 1;
}
if(trackdb_deluser(vec[0])) {
struct conn *d;
if(!config->remote_userman && !(c->rights & RIGHT__LOCAL)) {
- error(0, "S%x: remote edituser", c->tag);
- sink_writes(ev_writer_sink(c->w), "550 Remote user management is disabled\n");
+ disorder_error(0, "S%x: remote edituser", c->tag);
+ sink_writes(ev_writer_sink(c->w), "510 Remote user management is disabled\n");
return 1;
}
/* RIGHT_ADMIN can do anything; otherwise you can only set your own email
}
sink_writes(ev_writer_sink(c->w), "250 OK\n");
} else {
- error(0, "%s attempted edituser but lacks required rights", c->who);
+ disorder_error(0, "%s attempted edituser but lacks required rights",
+ c->who);
sink_writes(ev_writer_sink(c->w), "510 Restricted to administrators\n");
}
return 1;
if(!config->remote_userman
&& !(c->rights & RIGHT__LOCAL)
&& strcmp(vec[1], "rights")) {
- error(0, "S%x: remote userinfo %s %s", c->tag, vec[0], vec[1]);
- sink_writes(ev_writer_sink(c->w), "550 Remote user management is disabled\n");
+ disorder_error(0, "S%x: remote userinfo %s %s", c->tag, vec[0], vec[1]);
+ sink_writes(ev_writer_sink(c->w), "510 Remote user management is disabled\n");
return 1;
}
/* RIGHT_ADMIN allows anything; otherwise you can only get your own email
else
sink_writes(ev_writer_sink(c->w), "550 No such user\n");
} else {
- error(0, "%s attempted userinfo but lacks required rights", c->who);
+ disorder_error(0, "%s attempted userinfo but lacks required rights",
+ c->who);
sink_writes(ev_writer_sink(c->w), "510 Restricted to administrators\n");
}
return 1;
* letters and digits, minimizing the chance of the URL being mispasted. */
gcry_randomize(nonce, sizeof nonce, GCRY_STRONG_RANDOM);
if(basen(nonce, CONFIRM_SIZE, nonce_str, sizeof nonce_str, 62)) {
- error(0, "buffer too small encoding confirmation string");
+ disorder_error(0, "buffer too small encoding confirmation string");
sink_writes(ev_writer_sink(c->w), "550 Cannot create user\n");
}
byte_xasprintf(&cs, "%s/%s", vec[0], nonce_str);
}
user = xstrndup(vec[0], sep - vec[0]);
if(trackdb_confirm(user, vec[0], &rights))
- sink_writes(ev_writer_sink(c->w), "550 Incorrect confirmation string\n");
+ sink_writes(ev_writer_sink(c->w), "510 Incorrect confirmation string\n");
else {
c->who = user;
c->cookie = 0;
c->rights = rights;
if(strcmp(host, "local"))
- info("S%x %s confirmed from %s", c->tag, user, host);
+ disorder_info("S%x %s confirmed from %s", c->tag, user, host);
else
c->rights |= RIGHT__LOCAL;
/* Response contains username so client knows who they are acting as */
if(!status) {
sink_writes(ev_writer_sink(c->w), "250 OK\n");
} else {
- error(0, "reminder subprocess %s", wstat(status));
+ disorder_error(0, "reminder subprocess %s", wstat(status));
sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
}
/* Re-enable this connection */
static hash *last_reminder;
if(!config->mail_sender) {
- error(0, "cannot send password reminders because mail_sender not set");
+ disorder_error(0, "cannot send password reminders because mail_sender not set");
sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
return 1;
}
if(!(k = trackdb_getuserinfo(vec[0]))) {
- error(0, "reminder for user '%s' who does not exist", vec[0]);
+ disorder_error(0, "reminder for user '%s' who does not exist", vec[0]);
sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
return 1;
}
if(!(email = kvp_get(k, "email"))
|| !email_valid(email)) {
- error(0, "user '%s' has no valid email address", vec[0]);
+ disorder_error(0, "user '%s' has no valid email address", vec[0]);
sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
return 1;
}
if(!(password = kvp_get(k, "password"))
|| !*password) {
- error(0, "user '%s' has no password", vec[0]);
+ disorder_error(0, "user '%s' has no password", vec[0]);
sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
return 1;
}
last = hash_find(last_reminder, vec[0]);
xtime(&now);
if(last && now < *last + config->reminder_interval) {
- error(0, "sent a password reminder to '%s' too recently", vec[0]);
+ disorder_error(0, "sent a password reminder to '%s' too recently", vec[0]);
sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
return 1;
}
"\n"
" %s\n", password);
if(!(text = mime_encode_text(text, &charset, &encoding)))
- fatal(0, "cannot encode email");
+ disorder_fatal(0, "cannot encode email");
byte_xasprintf((char **)&content_type, "text/plain;charset=%s",
quote822(charset, 0));
pid = sendmail_subprocess("", config->mail_sender, email,
return 1;
}
hash_add(last_reminder, vec[0], &now, HASH_INSERT_OR_REPLACE);
- info("sending a passsword reminder to user '%s'", vec[0]);
+ disorder_info("sending a passsword reminder to user '%s'", vec[0]);
/* We can only continue when the subprocess finishes */
ev_child(c->ev, pid, 0, sent_reminder, c);
return 0;
const char *who = kvp_get(actiondata, "who");
if(!who || !c->who || strcmp(who, c->who)) {
- sink_writes(ev_writer_sink(c->w), "551 Not authorized\n");
+ sink_writes(ev_writer_sink(c->w), "510 Not authorized\n");
return 1; /* completed */
}
}
case 0:
assert(!"cannot cope with success");
case EACCES:
- sink_writes(ev_writer_sink(c->w), "550 Access denied\n");
+ sink_writes(ev_writer_sink(c->w), "510 Access denied\n");
break;
case EINVAL:
sink_writes(ev_writer_sink(c->w), "550 Invalid playlist name\n");
return 1;
}
-static const struct command {
+/** @brief Server's definition of a command */
+static const struct server_command {
/** @brief Command name */
const char *name;
*/
rights_type rights;
} commands[] = {
- { "adduser", 2, 3, c_adduser, RIGHT_ADMIN|RIGHT__LOCAL },
+ { "adduser", 2, 3, c_adduser, RIGHT_ADMIN },
{ "adopt", 1, 1, c_adopt, RIGHT_PLAY },
{ "allfiles", 0, 2, c_allfiles, RIGHT_READ },
{ "confirm", 1, 1, c_confirm, 0 },
{ "cookie", 1, 1, c_cookie, 0 },
- { "deluser", 1, 1, c_deluser, RIGHT_ADMIN|RIGHT__LOCAL },
+ { "deluser", 1, 1, c_deluser, RIGHT_ADMIN },
{ "dirs", 0, 2, c_dirs, RIGHT_READ },
{ "disable", 0, 1, c_disable, RIGHT_GLOBAL_PREFS },
{ "edituser", 3, 3, c_edituser, RIGHT_ADMIN|RIGHT_USERINFO },
{ "random-enabled", 0, 0, c_random_enabled, RIGHT_READ },
{ "recent", 0, 0, c_recent, RIGHT_READ },
{ "reconfigure", 0, 0, c_reconfigure, RIGHT_ADMIN },
- { "register", 3, 3, c_register, RIGHT_REGISTER|RIGHT__LOCAL },
+ { "register", 3, 3, c_register, RIGHT_REGISTER },
{ "reminder", 1, 1, c_reminder, RIGHT__LOCAL },
{ "remove", 1, 1, c_remove, RIGHT_REMOVE__MASK },
{ "rescan", 0, INT_MAX, c_rescan, RIGHT_RESCAN },
{ "resume", 0, 0, c_resume, RIGHT_PAUSE },
{ "revoke", 0, 0, c_revoke, RIGHT_READ },
{ "rtp-address", 0, 0, c_rtp_address, 0 },
+ { "rtp-cancel", 0, 0, c_rtp_cancel, 0 },
+ { "rtp-request", 2, 2, c_rtp_request, RIGHT_READ },
{ "schedule-add", 3, INT_MAX, c_schedule_add, RIGHT_READ },
{ "schedule-del", 1, 1, c_schedule_del, RIGHT_READ },
{ "schedule-get", 1, 1, c_schedule_get, RIGHT_READ },
else {
if(commands[n].rights
&& !(c->rights & commands[n].rights)) {
- error(0, "%s attempted %s but lacks required rights", c->who ? c->who : "NULL",
+ disorder_error(0, "%s attempted %s but lacks required rights",
+ c->who ? c->who : "NULL",
commands[n].name);
sink_writes(ev_writer_sink(c->w), "510 Prohibited\n");
return 1;
}
if(eof) {
if(bytes)
- error(0, "S%x unterminated line", c->tag);
+ disorder_error(0, "S%x unterminated line", c->tag);
D(("normal reader close"));
c->r = 0;
if(c->w) {
c->w = ev_writer_new(ev, fd, writer_error, c,
"client writer");
if(!c->w) {
- error(0, "ev_writer_new for file inbound connection (fd=%d) failed",
+ disorder_error(0, "ev_writer_new for file inbound connection (fd=%d) failed",
fd);
close(fd);
return 0;
if(!c->r)
/* Main reason for failure is the FD is too big and that will already have
* been handled */
- fatal(0, "ev_reader_new for file inbound connection (fd=%d) failed", fd);
+ disorder_fatal(0,
+ "ev_reader_new for file inbound connection (fd=%d) failed",
+ fd);
ev_tie(c->r, c->w);
c->fd = fd;
c->reader = reader_callback;
int server_start(ev_source *ev, int pf,
size_t socklen, const struct sockaddr *sa,
- const char *name) {
+ const char *name,
+ int privileged) {
int fd;
struct listener *l = xmalloc(sizeof *l);
static const int one = 1;
- D(("server_init socket %s", name));
+ D(("server_init socket %s privileged=%d", name, privileged));
+ /* Sanity check */
+ if(privileged && pf != AF_UNIX)
+ disorder_fatal(0, "cannot create a privileged listener on a non-local port");
fd = xsocket(pf, SOCK_STREAM, 0);
xsetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
if(bind(fd, sa, socklen) < 0) {
- error(errno, "error binding to %s", name);
+ disorder_error(errno, "error binding to %s", name);
return -1;
}
xlisten(fd, 128);
cloexec(fd);
l->name = name;
l->pf = pf;
+ l->privileged = privileged;
if(ev_listen(ev, fd, listen_callback, l, "server listener"))
exit(EXIT_FAILURE);
- info("listening on %s", name);
+ disorder_info("listening on %s", name);
return fd;
}