#define _GNU_SOURCE
#include <unistd.h>
#include <stdlib.h>
-#include <malloc.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip6.h>
#include <arpa/inet.h>
+#include <sys/un.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
int tract:1;
int trprog:1;
int srch:1;
+ int msg:1;
} b;
int w;
} notify;
wchar_t *username;
struct uiuser *userinfo;
+ int id;
+ wchar_t *regname;
uid_t uid;
struct notif *fnotif, *lnotif;
char *fcmdbuf;
size_t inbufsize, indata;
/* Wordset storage */
wchar_t **argv;
- int argc, args;
+ size_t argc, args;
/* WCS conversation stuff */
wchar_t *cb; /* Conversation buffer */
size_t cbsize, cbdata;
static int srcheta(struct search *srch, void *uudata);
static int srchcommit(struct search *srch, void *uudata);
static int srchres(struct search *srch, struct srchres *sr, void *uudata);
+static struct notif *newnotif(struct uidata *data, int code, ...);
+static void notifappend(struct notif *notif, ...);
struct uiuser *users = NULL;
struct uidata *actives = NULL;
-struct socket *uisocket = NULL;
+struct socket *tcpsocket = NULL;
+struct socket *unixsocket = NULL;
+static time_t starttime;
static wchar_t *quoteword(wchar_t *word)
{
if(!memcmp(&((struct sockaddr_in6 *)sk->remote)->sin6_addr, &mv4lo, sizeof(in6addr_loopback)))
valid = 1;
break;
+ case AF_UNIX:
+ valid = 1;
+ break;
default:
valid = 0;
break;
haveargs(3);
havepriv(PERM_FNETCTL);
+ for(i = 0, fn = fnetnodes; fn != NULL; i++, fn = fn->next);
+ if((confgetint("fnet", "maxnodes") > 0) && (i >= confgetint("fnet", "maxnodes"))) {
+ sq(sk, 0, L"515", L"Too many fnetnodes connected already", NULL);
+ return;
+ }
if((buf = icwcstombs(argv[2], NULL)) == NULL)
{
sq(sk, 0, L"504", L"Could not convert data to locale charset", NULL);
args = NULL;
for(i = 3; i < argc - 1; i += 2)
newwcspair(argv[i], argv[i + 1], &args);
- fn = fnetinitconnect(argv[1], buf, args);
+ fn = fnetinitconnect(argv[1], data->userinfo->name, buf, args);
err = errno;
free(buf);
if(fn == NULL)
}
linkfnetnode(fn);
fnetsetname(fn, argv[2]);
+ sq(sk, 0, L"200", L"%%i", fn->id, L"Connection under way", NULL);
putfnetnode(fn);
- sq(sk, 0, L"200", L"Connection under way", NULL);
}
static void cmd_lsnodes(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
sq(sk, 0, L"510", L"No such node", NULL);
return;
}
+ if(wpfind(fn->args, L"locked") && !((data->userinfo->perms & PERM_ADMIN) || !wcscmp(data->userinfo->name, fn->owner)))
+ {
+ sq(sk, 0, L"502", L"This node is locked and you are neither administrator nor its owner", NULL);
+ return;
+ }
killfnetnode(fn);
unlinkfnetnode(fn);
}
} else {
for(peer = fn->peers; peer != NULL; peer = peer->next)
{
- sq(sk, 2 | ((peer->next != NULL)?1:0), L"200", peer->id, peer->nick, NULL);
+ sq(sk, 2 | ((peer->next != NULL)?1:0), L"200", L"%%ls", peer->id, L"%%ls", peer->nick, NULL);
for(i = 0; i < peer->dinum; i++)
{
if(peer->peerdi[i].datum->datatype == FNPD_INT)
sq(sk, 2, peer->peerdi[i].datum->id, buf, NULL);
}
if((peer->peerdi[i].datum->datatype == FNPD_STR) && (peer->peerdi[i].data.str != NULL))
- sq(sk, 2, peer->peerdi[i].datum->id, peer->peerdi[i].data.str, NULL);
+ sq(sk, 2, peer->peerdi[i].datum->id, L"%%ls", peer->peerdi[i].data.str, NULL);
}
sq(sk, 0, NULL);
}
data->notify.b.trprog = val;
} else if(!wcscasecmp(argv[i], L"srch:act")) {
data->notify.b.srch = val;
+ } else if(!wcscasecmp(argv[i], L"msg")) {
+ data->notify.b.msg = val;
}
}
sq(sk, 0, L"200", L"Notification alteration succeeded", NULL);
sq(sk, 0, L"505", L"System error - Could not fork session", "Internal error", NULL);
return;
}
- if((filtercmd = findfile(icswcstombs(confgetstr("ui", "filtercmd"), NULL, NULL), "dcdl-filtercmd", pwent->pw_dir)) == NULL)
+ filtercmd = findfile(icswcstombs(confgetstr("ui", "filtercmd"), NULL, NULL), NULL, 0);
+ if(filtercmd == NULL)
+ filtercmd = findfile("dc-filtercmd", pwent->pw_dir, 0);
+ if(filtercmd == NULL)
{
flog(LOG_WARNING, "could not find filtercmd executable for user %s", pwent->pw_name);
sq(sk, 0, L"505", L"System error - Could not fork session", L"Could not find filtercmd executable", NULL);
havepriv(PERM_TRANS);
buf1 = swprintf2(L"%lli", bytesdownload);
buf2 = swprintf2(L"%lli", bytesupload);
- sq(sk, 0, L"200", L"%%ls", buf1, L"%%ls", buf2, NULL);
+ sq(sk, 0, L"200", L"down", L"%%ls", buf1, L"up", L"%%ls", buf2, NULL);
free(buf1);
free(buf2);
}
+static void cmd_register(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
+{
+ struct uidata *d2;
+
+ haveargs(2);
+ if(data->userinfo == NULL) {
+ sq(sk, 0, L"502", L"Must be logged in", NULL);
+ return;
+ }
+ if(argv[1][0] == L'#') {
+ sq(sk, 0, L"509", L"Name must not begin with a hash sign", NULL);
+ return;
+ }
+ for(d2 = actives; d2 != NULL; d2 = d2->next) {
+ if((d2 != data) && (d2->userinfo == data->userinfo) && d2->regname && !wcscmp(d2->regname, argv[1])) {
+ sq(sk, 0, L"516", L"Name already in use", NULL);
+ return;
+ }
+ }
+ if(data->regname != NULL)
+ free(data->regname);
+ data->regname = swcsdup(argv[1]);
+ sq(sk, 0, L"200", L"Registered", NULL);
+}
+
+static void cmd_sendmsg(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
+{
+ int i, rcptid;
+ struct uidata *rcpt;
+ wchar_t *myname;
+ struct notif *notif;
+
+ haveargs(2);
+ if(data->userinfo == NULL) {
+ sq(sk, 0, L"502", L"Must be logged in", NULL);
+ return;
+ }
+ if(argv[1][0] == L'#') {
+ rcptid = wcstol(argv[1] + 1, NULL, 0);
+ for(rcpt = actives; rcpt != NULL; rcpt = rcpt->next) {
+ if((rcpt->userinfo == data->userinfo) && (rcpt->id == rcptid))
+ break;
+ }
+ } else {
+ for(rcpt = actives; rcpt != NULL; rcpt = rcpt->next) {
+ if((rcpt->userinfo == data->userinfo) && rcpt->regname && !wcscmp(rcpt->regname, argv[1]))
+ break;
+ }
+ }
+ if(rcpt == NULL) {
+ sq(sk, 0, L"517", L"No such recipient", NULL);
+ return;
+ }
+ if(!rcpt->notify.b.msg) {
+ sq(sk, 0, L"518", L"Recipient not listening for messages", NULL);
+ return;
+ }
+ if(data->regname != NULL)
+ myname = swcsdup(data->regname);
+ else
+ myname = swprintf2(L"#%i", data->id);
+ notif = newnotif(rcpt, 640, NOTIF_STR, myname, NOTIF_END);
+ for(i = 2; i < argc; i++)
+ notifappend(notif, NOTIF_STR, argv[i], NOTIF_END);
+ sq(sk, 0, L"200", L"Message sent", NULL);
+}
+
+static void cmd_uptime(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
+{
+ sq(sk, 0, L"200", L"%%i", time(NULL) - starttime, NULL);
+}
+
#undef haveargs
#undef havepriv
{L"lstrarg", cmd_lstrarg},
{L"hashstatus", cmd_hashstatus},
{L"transstatus", cmd_transstatus},
+ {L"register", cmd_register},
+ {L"sendmsg", cmd_sendmsg},
+ {L"uptime", cmd_uptime},
{NULL, NULL}
};
}
if(data->auth != NULL)
authputhandle(data->auth);
+ if(data->regname != NULL)
+ free(data->regname);
if(data->username != NULL)
{
if(data->userinfo != NULL)
static struct uidata *newuidata(struct socket *sk)
{
struct uidata *data;
+ static int curid = 0;
data = smalloc(sizeof(*data));
memset(data, 0, sizeof(*data));
+ data->id = curid++;
data->sk = sk;
getsock(sk);
data->inbuf = smalloc(1024);
}
}
-#ifdef HAVE_IPV6
-static struct sockaddr *getnameforport(int port, socklen_t *len)
+static struct sockaddr_un *makeunixname(void)
{
- static struct sockaddr_in6 addr;
+ static struct sockaddr_un buf;
+ char *val;
+ struct passwd *pwd;
+ uid_t uid;
- memset(&addr, 0, sizeof(addr));
- addr.sin6_family = AF_INET6;
- addr.sin6_port = htons(port);
- addr.sin6_addr = in6addr_any;
- if(len != NULL)
- *len = sizeof(addr);
- return((struct sockaddr *)&addr);
-}
-#else
-static struct sockaddr *getnameforport(int port, socklen_t *len)
-{
- static struct sockaddr_in addr;
+ memset(&buf, 0, sizeof(buf));
+ buf.sun_family = PF_UNIX;
+ if((val = icswcstombs(confgetstr("ui", "unixsock"), NULL, NULL)) == NULL) {
+ flog(LOG_WARNING, "could not map Unix socket name into local charset: %s", strerror(errno));
+ return(NULL);
+ }
+ if(!strcmp(val, "none"))
+ return(NULL);
+ if(!strcmp(val, "default"))
+ {
+ if((uid = getuid()) == 0)
+ {
+ strcpy(buf.sun_path, "/var/run/doldacond.sock");
+ return(&buf);
+ } else {
+ if((pwd = getpwuid(uid)) == NULL)
+ {
+ flog(LOG_ERR, "could not get passwd entry for current user: %s", strerror(errno));
+ return(NULL);
+ }
+ strcpy(buf.sun_path, "/tmp/doldacond-");
+ strcat(buf.sun_path, pwd->pw_name);
+ return(&buf);
+ }
+ }
+ if(strchr(val, '/'))
+ {
+ strcpy(buf.sun_path, val);
+ return(&buf);
+ }
+ flog(LOG_WARNING, "invalid Unix socket name: %s", val);
+ return(NULL);
+}
+
+static int tcpportupdate(struct configvar *var, void *uudata)
+{
+ struct socket *newsock;
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(port);
- if(len != NULL)
- *len = sizeof(addr);
- return((struct sockaddr *)&addr);
+ newsock = NULL;
+ if((var->val.num != -1) && ((newsock = netcstcplisten(var->val.num, 1, uiaccept, NULL)) == NULL))
+ {
+ flog(LOG_WARNING, "could not create new TCP UI socket, reverting to old: %s", strerror(errno));
+ return(0);
+ }
+ if(tcpsocket != NULL)
+ {
+ putsock(tcpsocket);
+ tcpsocket = NULL;
+ }
+ tcpsocket = newsock;
+ return(0);
}
-#endif
-static int portupdate(struct configvar *var, void *uudata)
+static int unixsockupdate(struct configvar *var, void *uudata)
{
- struct sockaddr *addr;
- socklen_t addrlen;
struct socket *newsock;
+ struct sockaddr_un *un;
- addr = getnameforport(var->val.num, &addrlen);
- if((uisocket = netcslistenlocal(SOCK_STREAM, addr, addrlen, uiaccept, NULL)) == NULL)
+ newsock = NULL;
+ if(((un = makeunixname()) != NULL) && ((newsock = netcslistenlocal(SOCK_STREAM, (struct sockaddr *)un, sizeof(*un), uiaccept, NULL)) == NULL))
{
- flog(LOG_WARNING, "could not create new UI socket, reverting to old: %s", strerror(errno));
+ flog(LOG_WARNING, "could not create new Unix UI socket, reverting to old: %s", strerror(errno));
return(0);
}
- if(uisocket != NULL)
- putsock(uisocket);
- uisocket = newsock;
+ if(unixsocket != NULL)
+ {
+ putsock(unixsocket);
+ unixsocket = NULL;
+ }
+ unixsocket = newsock;
return(0);
}
static int init(int hup)
{
- struct sockaddr *addr;
- socklen_t addrlen;
struct uiuser *user, *next;
+ struct sockaddr_un *un;
if(hup)
{
}
if(!hup)
{
- if(uisocket != NULL)
- putsock(uisocket);
- addr = getnameforport(confgetint("ui", "port"), &addrlen);
- if((uisocket = netcslistenlocal(SOCK_STREAM, addr, addrlen, uiaccept, NULL)) == NULL)
+ starttime = time(NULL);
+ if((confgetint("ui", "port") != -1) && ((tcpsocket = netcstcplisten(confgetint("ui", "port"), 1, uiaccept, NULL)) == NULL))
+ {
+ flog(LOG_CRIT, "could not create TCP UI socket: %s", strerror(errno));
+ return(1);
+ }
+ CBREG(confgetvar("ui", "port"), conf_update, tcpportupdate, NULL, NULL);
+ if(((un = makeunixname()) != NULL) && ((unixsocket = netcslistenlocal(SOCK_STREAM, (struct sockaddr *)un, sizeof(*un), uiaccept, NULL)) == NULL))
{
- flog(LOG_CRIT, "could not create UI socket: %s", strerror(errno));
+ flog(LOG_CRIT, "could not create Unix UI socket: %s", strerror(errno));
return(1);
}
- CBREG(confgetvar("ui", "port"), conf_update, portupdate, NULL, NULL);
+ CBREG(confgetvar("ui", "unixsock"), conf_update, unixsockupdate, NULL, NULL);
GCBREG(newfncb, newfnetnode, NULL);
GCBREG(newtransfercb, newtransfernotify, NULL);
}
{
while(users != NULL)
freeuser(users);
+ if(tcpsocket != NULL)
+ putsock(tcpsocket);
+ if(unixsocket != NULL)
+ putsock(unixsocket);
}
static struct configvar myvars[] =
{
+ /** If true, UI connections will only be accepted from localhost
+ * addresses (127.0.0.1, ::1 or ::ffff:127.0.0.1). Unless you are
+ * completely sure that you know what you are doing, never turn
+ * this off when auth.authless is on. */
{CONF_VAR_BOOL, "onlylocal", {.num = 1}},
+ /** The TCP port number on which to accept UI client connections,
+ * or -1 to not listen on TCP. */
{CONF_VAR_INT, "port", {.num = 1500}},
+ /**
+ * Controls the the name to use for the Unix socket on which to
+ * accept UI client connections. If the name contains a slash, it
+ * is treated as a file name to bind on. If the name is "default",
+ * the file name will be "/var/run/doldacond.sock" if doldacond
+ * runs with UID == 0, or "/tmp/doldacond-NAME" otherwise, where
+ * NAME is the user name of the UID which doldacond runs as. If
+ * the name is "none", no Unix socket will be used. Otherwise, an
+ * error is signaled.
+ */
+ {CONF_VAR_STRING, "unixsock", {.str = L"default"}},
+ /** The TOS value to use for UI connections (see the TOS VALUES
+ * section). */
{CONF_VAR_INT, "uitos", {.num = SOCK_TOS_MINDELAY}},
+ /** The name of the filtercmd script (see the FILES section for
+ * lookup information). */
{CONF_VAR_STRING, "filtercmd", {.str = L"dc-filtercmd"}},
{CONF_VAR_END}
};