/*
* Dolda Connect - Modular multiuser Direct Connect-style client
- * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
+ * Copyright (C) 2004 Fredrik Tolf <fredrik@dolda2000.com>
*
* 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
#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>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
+#include <stdint.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#define NOTIF_STR 2
#define NOTIF_FLOAT 3
#define NOTIF_ID 4
+#define NOTIF_OFF 5
#define NOTIF_PEND 0
#define NOTIF_WAIT 1
union
{
int n;
+ off_t o;
wchar_t *s;
double d;
} d;
struct uidata *next, *prev;
struct socket *sk;
struct qcommand *queue, *queuelast;
+ size_t queuesize;
struct authhandle *auth;
int close;
union
struct uiuser *users = NULL;
struct uidata *actives = NULL;
-struct socket *uisocket = NULL;
+struct lport *tcpsocket = NULL;
+struct lport *unixsocket = NULL;
static time_t starttime;
static wchar_t *quoteword(wchar_t *word)
{
int num, freepart;
va_list al;
- char *final;
+ char *final, *sarg;
wchar_t *buf;
wchar_t *part, *tpart;
size_t bufsize, bufdata;
va_start(al, cont);
while((part = va_arg(al, wchar_t *)) != NULL)
{
+ freepart = 0;
if(*part == L'%')
{
- /*
- * This kludge demands that all arguments that you call it
- * with are the size of an int. That happens to be the
- * case for most datatypes on most platforms and
- * compilers, but I don't know exactly which ones, and
- * also a long long is a notable candidate of an arg that
- * is not the size of an int on 32-bit archs. If it breaks
- * some existing call on your architecture, please tell
- * me.
- */
- part = vswprintf2(tpart = (part + 1), al);
- for(; *tpart != L'\0'; tpart++)
+ tpart = part + 1;
+ if(!wcscmp(tpart, L"i"))
{
- if(*tpart == L'%')
+ freepart = 1;
+ part = swprintf2(L"%i", va_arg(al, int));
+ } else if(!wcscmp(tpart, L"zi")) {
+ freepart = 1;
+ part = swprintf2(L"%zi", va_arg(al, size_t));
+ } else if(!wcscmp(tpart, L"oi")) {
+ freepart = 1;
+ part = swprintf2(L"%ji", (intmax_t)va_arg(al, off_t));
+ } else if(!wcscmp(tpart, L"s")) {
+ freepart = 1;
+ part = icmbstowcs(sarg = va_arg(al, char *), NULL);
+ if(part == NULL)
{
- if(tpart[1] == L'%')
- tpart++;
- else
- va_arg(al, int);
+ freepart = 0;
+ part = L"ERROR";
+ flog(LOG_ERR, "could not convert local string to wcs: %s", sarg);
}
+ } else if(!wcscmp(tpart, L"ls")) {
+ part = va_arg(al, wchar_t *);
+ } else if(!wcscmp(tpart, L"ll")) {
+ freepart = 1;
+ part = swprintf2(L"%lli", va_arg(al, long long));
+ } else if(!wcscmp(tpart, L"f")) {
+ freepart = 1;
+ part = swprintf2(L"%f", va_arg(al, double));
+ } else if(!wcscmp(tpart, L"x")) {
+ freepart = 1;
+ part = swprintf2(L"%x", va_arg(al, int));
+ } else {
+ flog(LOG_CRIT, "BUG: unknown type code in sq: %ls", tpart);
+ abort();
}
- freepart = 1;
- } else {
- freepart = 0;
}
if((tpart = quoteword(part)) != NULL)
{
/* Useful macros for the command functions: */
#define haveargs(n) do { if(argc < n) { sq(sk, 0, L"501", L"Wrong number of arguments", NULL); return; } } while(0)
-#define havepriv(p) do { if((data->userinfo == NULL) || ((data->userinfo->perms & (p)) != (p))) { sq(sk, 0, L"502", L"%Unauthorized request - %x needed", (p), NULL); return; } } while(0)
+#define havepriv(p) do { if((data->userinfo == NULL) || ((data->userinfo->perms & (p)) != (p))) { sq(sk, 0, L"502", L"Unauthorized request", L"needed", L"%x", (p), NULL); return; } } while(0)
static void cmd_connect(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
{
int valid;
struct in6_addr mv4lo;
+ struct sockaddr *remote;
if(confgetint("ui", "onlylocal"))
{
- switch(sk->remote->sa_family)
- {
- case AF_INET:
- valid = ((struct sockaddr_in *)sk->remote)->sin_addr.s_addr == INADDR_LOOPBACK;
- break;
- case AF_INET6:
- inet_pton(AF_INET6, "::ffff:127.0.0.1", &mv4lo);
- valid = 0;
- if(!memcmp(&((struct sockaddr_in6 *)sk->remote)->sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback)))
- valid = 1;
- if(!memcmp(&((struct sockaddr_in6 *)sk->remote)->sin6_addr, &mv4lo, sizeof(in6addr_loopback)))
+ valid = 0;
+ if(!sockpeeraddr(sk, &remote, NULL)) {
+ switch(remote->sa_family)
+ {
+ case AF_INET:
+ valid = ((struct sockaddr_in *)remote)->sin_addr.s_addr == INADDR_LOOPBACK;
+ break;
+ case AF_INET6:
+ inet_pton(AF_INET6, "::ffff:127.0.0.1", &mv4lo);
+ valid = 0;
+ if(!memcmp(&((struct sockaddr_in6 *)remote)->sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback)))
+ valid = 1;
+ if(!memcmp(&((struct sockaddr_in6 *)remote)->sin6_addr, &mv4lo, sizeof(in6addr_loopback)))
+ valid = 1;
+ break;
+ case AF_UNIX:
valid = 1;
- break;
- default:
- valid = 0;
- break;
+ break;
+ }
+ free(remote);
}
if(!valid)
{
sq(sk, 0, L"502", L"Only localhost connections allowed to this host", NULL);
- sk->close = 1;
+ closesock(sk);
data->close = 1;
return;
}
}
- sq(sk, 0, L"200", L"%Dolda Connect daemon v%s", VERSION, NULL);
+ sq(sk, 0, L"201", L"1", L"3", L"Dolda Connect daemon v" VERSION, NULL);
}
static void cmd_notfound(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
{
if((argv != NULL) && (argv[0] != NULL))
- sq(sk, 0, L"500", L"%Command not found: %ls", argv[0], NULL);
+ sq(sk, 0, L"500", L"Command not found", NULL);
else
sq(sk, 0, L"500", L"No command", NULL);
}
prev = NULL;
for(mech = mechs; mech != NULL; mech = mech->next)
{
- if(mech->enabled)
+ if(mech->enabled && authavailable(mech, sk))
{
if(prev != NULL)
sq(sk, 1, L"200", prev->name, NULL);
if(errno == ENOENT)
sq(sk, 0, L"508", L"No such authentication mechanism", NULL);
else
- sq(sk, 0, L"505", L"Could not initialize authentication system", L"%%s", strerror(errno), NULL);
+ sq(sk, 0, L"505", L"Could not initialize authentication system", L"%s", strerror(errno), NULL);
free(buf);
logout(data);
return;
}
free(buf);
- switch(authenticate(data->auth, NULL))
+ switch(authenticate(data->auth, sk, NULL))
{
case AUTH_SUCCESS:
data->userinfo = finduser(data->username);
if(data->uid == -1)
{
sq(sk, 0, L"506", L"Authentication error", NULL);
- flog(LOG_INFO, "user %ls authenticated successfully from %s, but no account existed", data->username, formataddress(sk->remote, sk->remotelen));
+ flog(LOG_INFO, "user %ls authenticated successfully from %s, but no account existed", data->username, formatsockpeer(sk));
logout(data);
} else if((data->userinfo == NULL) || (data->userinfo->perms & PERM_DISALLOW)) {
sq(sk, 0, L"506", L"Authentication error", NULL);
- flog(LOG_INFO, "user %ls authenticated successfully from %s, but was not authorized", data->username, formataddress(sk->remote, sk->remotelen));
+ flog(LOG_INFO, "user %ls authenticated successfully from %s, but was not authorized", data->username, formatsockpeer(sk));
logout(data);
} else {
sq(sk, 0, L"200", L"Welcome", NULL);
- flog(LOG_INFO, "%ls (UID %i) logged in from %s", data->username, data->uid, formataddress(sk->remote, sk->remotelen));
+ flog(LOG_INFO, "%ls (UID %i) logged in from %s", data->username, data->uid, formatsockpeer(sk));
}
break;
case AUTH_DENIED:
- sq(sk, 0, L"506", L"Authentication error", L"%%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL);
- flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formataddress(sk->remote, sk->remotelen));
+ sq(sk, 0, L"506", L"Authentication error", L"%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL);
+ flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formatsockpeer(sk));
logout(data);
break;
case AUTH_PASS:
code = 304;
break;
}
- sq(sk, 0, L"%%i", code, data->auth->text, NULL);
+ sq(sk, 0, L"%i", code, data->auth->text, NULL);
break;
case AUTH_ERR:
- sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL);
+ sq(sk, 0, L"505", L"System error", L"%s", strerror(errno), NULL);
logout(data);
break;
default:
flog(LOG_WARNING, "BUG? Non-caught return from authenticate in cmd_login");
- sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL);
+ sq(sk, 0, L"505", L"System error", L"%s", strerror(errno), NULL);
logout(data);
break;
}
sq(sk, 0, L"507", L"Data not expected", NULL);
return;
}
- switch(authenticate(data->auth, buf))
+ switch(authenticate(data->auth, sk, buf))
{
case AUTH_SUCCESS:
data->userinfo = finduser(data->username);
if(data->uid == -1)
{
sq(sk, 0, L"506", L"Authentication error", NULL);
- flog(LOG_INFO, "user %ls authenticated successfully from %s, but no account existed", data->username, formataddress(sk->remote, sk->remotelen));
+ flog(LOG_INFO, "user %ls authenticated successfully from %s, but no account existed", data->username, formatsockpeer(sk));
logout(data);
} else if((data->userinfo == NULL) || (data->userinfo->perms & PERM_DISALLOW)) {
sq(sk, 0, L"506", L"Authentication error", NULL);
- flog(LOG_INFO, "user %ls authenticated successfully from %s, but was not authorized", data->username, formataddress(sk->remote, sk->remotelen));
+ flog(LOG_INFO, "user %ls authenticated successfully from %s, but was not authorized", data->username, formatsockpeer(sk));
logout(data);
} else {
sq(sk, 0, L"200", L"Welcome", NULL);
- flog(LOG_INFO, "%ls (UID %i) logged in from %s", data->username, data->uid, formataddress(sk->remote, sk->remotelen));
+ flog(LOG_INFO, "%ls (UID %i) logged in from %s", data->username, data->uid, formatsockpeer(sk));
}
break;
case AUTH_DENIED:
- sq(sk, 0, L"506", L"Authentication error", L"%%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL);
- flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formataddress(sk->remote, sk->remotelen));
+ sq(sk, 0, L"506", L"Authentication error", L"%ls", (data->auth->text == NULL)?L"":(data->auth->text), NULL);
+ flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formatsockpeer(sk));
logout(data);
break;
case AUTH_PASS:
code = 304;
break;
}
- sq(sk, 0, L"%%i", code, data->auth->text, NULL);
+ sq(sk, 0, L"%i", code, data->auth->text, NULL);
break;
case AUTH_ERR:
- sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL);
+ sq(sk, 0, L"505", L"System error", L"%s", strerror(errno), NULL);
logout(data);
break;
default:
flog(LOG_WARNING, "BUG? Non-caught return from authenticate in cmd_pass");
- sq(sk, 0, L"505", L"System error", L"%%s", strerror(errno), NULL);
+ sq(sk, 0, L"505", L"System error", L"%s", strerror(errno), NULL);
logout(data);
break;
}
if(errno == EPROTONOSUPPORT)
sq(sk, 0, L"511", L"No such network name", NULL);
else
- sq(sk, 0, L"509", L"Could not parse the address", L"%%s", strerror(err), NULL);
+ sq(sk, 0, L"509", L"Could not parse the address", L"%s", strerror(err), NULL);
return;
}
linkfnetnode(fn);
fnetsetname(fn, argv[2]);
- sq(sk, 0, L"200", L"%%i", fn->id, L"Connection under way", NULL);
+ sq(sk, 0, L"200", L"%i", fn->id, L"Connection under way", NULL);
putfnetnode(fn);
}
}
for(fn = fnetnodes; fn != NULL; fn = fn->next)
{
- sq(sk, (fn->next != NULL)?1:0, L"200", L"%%i", fn->id, fn->fnet->name, (fn->name == NULL)?L"":fn->name, L"%%i", fn->numpeers, L"%%i", fn->state, L"%%ls", fn->pubid, NULL);
+ sq(sk, (fn->next != NULL)?1:0, L"200", L"%i", fn->id, fn->fnet->name, (fn->name == NULL)?L"":fn->name, L"%i", fn->numpeers, L"%i", fn->state, L"%ls", fn->pubid, NULL);
}
}
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);
}
sq(sk, 0, L"201", L"No data available", NULL);
} else {
for(datum = fn->peerdata; datum != NULL; datum = datum->next)
- sq(sk, (datum->next != NULL)?1:0, L"200", datum->id, L"%%i", datum->datatype, NULL);
+ sq(sk, (datum->next != NULL)?1:0, L"200", datum->id, L"%i", datum->datatype, NULL);
}
}
{
int i;
struct fnetnode *fn;
- struct fnetpeer *peer;
- wchar_t buf[40];
+ struct fnetpeer *peer, *npeer;
haveargs(2);
if((fn = findfnetnode(wcstol(argv[1], NULL, 0))) == NULL)
}
if(fn->peers == NULL)
{
- sq(sk, 0, L"201", L"No peers avaiable", NULL);
+ sq(sk, 0, L"201", L"No peers available", NULL);
} else {
- for(peer = fn->peers; peer != NULL; peer = peer->next)
+ for(peer = btreeiter(fn->peers); peer != NULL; peer = npeer)
{
- sq(sk, 2 | ((peer->next != NULL)?1:0), L"200", peer->id, peer->nick, NULL);
+ npeer = btreeiter(NULL);
+ sq(sk, 2 | ((npeer != 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, L"%%i", peer->peerdi[i].data.num, NULL);
- /* Note: A long long is not the size of an int, so
- * sq() can't handle the conversion itself. */
+ sq(sk, 2, peer->peerdi[i].datum->id, L"%i", peer->peerdi[i].data.num, NULL);
if(peer->peerdi[i].datum->datatype == FNPD_LL)
- {
- swprintf(buf, 40, L"%lli", peer->peerdi[i].data.lnum);
- sq(sk, 2, peer->peerdi[i].datum->id, buf, NULL);
- }
+ sq(sk, 2, peer->peerdi[i].datum->id, L"%ll", peer->peerdi[i].data.lnum, 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);
}
linktransfer(transfer);
}
if(argc > 4)
- transfersetsize(transfer, wcstol(argv[4], NULL, 0));
+ transfersetsize(transfer, wcstoll(argv[4], NULL, 0));
if(argc > 5)
{
for(i = 5; i < argc; i += 2)
}
}
}
- sq(sk, 0, L"200", L"%%i", transfer->id, L"Download queued", NULL);
+ sq(sk, 0, L"200", L"%i", transfer->id, L"Download queued", NULL);
transfersetactivity(transfer, L"create");
}
if((transfer->dir != TRNSD_DOWN) || (transfer->owner == data->uid))
{
if(pt != NULL)
- sq(sk, 1, L"200", L"%%i", pt->id, L"%%i", pt->dir,
- L"%%i", pt->state, pt->peerid,
+ sq(sk, 1, L"200", L"%i", pt->id, L"%i", pt->dir,
+ L"%i", pt->state, pt->peerid,
(pt->peernick == NULL)?L"":(pt->peernick),
(pt->path == NULL)?L"":(pt->path),
- L"%%i", pt->size, L"%%i", pt->curpos,
+ L"%oi", pt->size, L"%oi", pt->curpos,
(pt->hash == NULL)?L"":unparsehash(pt->hash),
NULL);
pt = transfer;
if(pt == NULL)
sq(sk, 0, L"201", L"No transfers", NULL);
else
- sq(sk, 0, L"200", L"%%i", pt->id, L"%%i", pt->dir,
- L"%%i", pt->state, pt->peerid,
+ sq(sk, 0, L"200", L"%i", pt->id, L"%i", pt->dir,
+ L"%i", pt->state, pt->peerid,
(pt->peernick == NULL)?L"":(pt->peernick),
(pt->path == NULL)?L"":(pt->path),
- L"%%i", pt->size, L"%%i", pt->curpos,
+ L"%oi", pt->size, L"%oi", pt->curpos,
(pt->hash == NULL)?L"":unparsehash(pt->hash),
NULL);
}
sq(sk, 0, L"200", L"Transfer cancelled", NULL);
}
+static void cmd_reset(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
+{
+ struct transfer *transfer;
+
+ haveargs(2);
+ havepriv(PERM_TRANS);
+ if((transfer = findtransfer(wcstol(argv[1], NULL, 0))) == NULL)
+ {
+ sq(sk, 0, L"512", L"No such transfer", NULL);
+ return;
+ }
+ if(transfer->dir == TRNSD_UP)
+ {
+ sq(sk, 0, L"512", L"Only applicable to downloads", NULL);
+ return;
+ }
+ resettransfer(transfer);
+ sq(sk, 0, L"200", L"Transfer reset", NULL);
+}
+
static void cmd_notify(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
{
int i, val;
CBREG(srch, search_eta, srcheta, NULL, NULL);
CBREG(srch, search_commit, srchcommit, NULL, NULL);
CBREG(srch, search_result, srchres, NULL, NULL);
- sq(sk, 0, L"200", L"%%i", srch->id, L"%%i", srch->eta - time(NULL), NULL);
+ sq(sk, 0, L"200", L"%i", srch->id, L"%i", srch->eta - time(NULL), NULL);
putsexpr(sexpr);
}
if(!wcscmp(srch->owner, data->username))
{
if(pt != NULL)
- sq(sk, 1, L"200", L"%%i", pt->id, L"%%i", pt->state, L"%%i", pt->eta - now, L"%%i", pt->numres, NULL);
+ sq(sk, 1, L"200", L"%i", pt->id, L"%i", pt->state, L"%i", pt->eta - now, L"%i", pt->numres, NULL);
pt = srch;
}
}
if(pt == NULL)
sq(sk, 0, L"201", L"No searches", NULL);
else
- sq(sk, 0, L"200", L"%%i", pt->id, L"%%i", pt->state, L"%%i", pt->eta - now, L"%%i", pt->numres, NULL);
+ sq(sk, 0, L"200", L"%i", pt->id, L"%i", pt->state, L"%i", pt->eta - now, L"%i", pt->numres, NULL);
}
static void cmd_lssr(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
{
struct search *srch;
struct srchres *sr;
- wchar_t buf[64];
haveargs(2);
havepriv(PERM_SRCH);
} else {
for(sr = srch->results; sr != NULL; sr = sr->next)
{
- swprintf(buf, 64, L"%f", sr->time);
- sq(sk, (sr->next != NULL)?1:0, L"200", L"%%ls", sr->filename, sr->fnet->name, L"%%ls", sr->peerid, L"%%i", sr->size, L"%%i", sr->slots, L"%%i", (sr->fn == NULL)?-1:(sr->fn->id), buf, L"%%ls", (sr->hash == NULL)?L"":unparsehash(sr->hash), NULL);
+ sq(sk, (sr->next != NULL)?1:0, L"200", L"%ls", sr->filename,
+ sr->fnet->name, L"%ls", sr->peerid, L"%oi", sr->size,
+ L"%i", sr->slots, L"%i", (sr->fn == NULL)?-1:(sr->fn->id),
+ L"%f", sr->time,
+ L"%ls", (sr->hash == NULL)?L"":unparsehash(sr->hash), NULL);
}
}
}
data->fcmdbuf = NULL;
}
data->fcmdbufsize = data->fcmdbufdata = 0;
- sq(data->sk, 0, L"505", L"An error occurred on the pipe to the filtercmd", L"%%s", strerror(err), NULL);
+ sq(data->sk, 0, L"505", L"An error occurred on the pipe to the filtercmd", L"%s", strerror(err), NULL);
return;
}
putsock(data->fcmdsk);
for(p2 = wcschr(p, L'\n'); p2 != NULL; p2 = wcschr(p, L'\n'))
{
*(p2++) = L'\0';
- sq(data->sk, (*p2 == L'\0')?0:1, L"200", L"%%ls", p, NULL);
+ sq(data->sk, (*p2 == L'\0')?0:1, L"200", L"%ls", p, NULL);
p = p2;
}
if(*p == L'\0')
if(p == wbuf)
sq(data->sk, 0, L"201", L"No data returned", NULL);
} else {
- sq(data->sk, 0, L"200", L"%%ls", p, NULL);
+ sq(data->sk, 0, L"200", L"%ls", p, NULL);
}
free(wbuf);
}
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, 0)) == NULL)
+ filtercmd = findfile("dc-filtercmd", pwent->pw_dir, 0);
+ if(filtercmd == NULL)
+ filtercmd = findfile(icswcstombs(confgetstr("ui", "filtercmd"), NULL, NULL), NULL, 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);
for(i = 0; i < cargvdata; i++)
free(cargv[i]);
free(cargv);
- sq(sk, 0, L"504", L"%Could not convert argument %i into local character set", i, L"%%s", strerror(errno), NULL);
+ sq(sk, 0, L"504", L"Could not convert argument into local character set", L"%i", i, L"%s", strerror(errno), NULL);
return;
}
addtobuf(cargv, argbuf);
if((pid = forksess(data->uid, data->auth, NULL, NULL, FD_FILE, 0, O_RDWR, "/dev/null", FD_PIPE, 1, O_RDONLY, &pipe, FD_FILE, 2, O_RDWR, "/dev/null", FD_END)) < 0)
{
flog(LOG_WARNING, "could not fork session in filtercmd: %s", strerror(errno));
- sq(sk, 0, L"505", L"System error - Could not fork session", L"%%s", strerror(errno), NULL);
+ sq(sk, 0, L"505", L"System error - Could not fork session", L"%s", strerror(errno), NULL);
return;
}
if(pid == 0)
sq(sk, 0, L"201", L"Transfer has no arguments", NULL);
} else {
for(ta = transfer->args; ta != NULL; ta = ta->next)
- sq(sk, ta->next != NULL, L"200", L"%%ls", ta->key, L"%%ls", ta->val, NULL);
+ sq(sk, ta->next != NULL, L"200", L"%ls", ta->key, L"%ls", ta->val, NULL);
}
}
hashed++;
}
}
- sq(sk, 0, L"200", L"%%i", total, L"tth", L"%%i", hashed, NULL);
+ sq(sk, 0, L"200", L"%i", total, L"tth", L"%i", hashed, NULL);
}
static void cmd_transstatus(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
{
- wchar_t *buf1, *buf2;
-
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);
- free(buf1);
- free(buf2);
+ sq(sk, 0, L"200", L"down", L"%ll", bytesdownload, L"up", L"%ll", bytesupload, NULL);
}
static void cmd_register(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
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);
+ sq(sk, 0, L"200", L"%i", time(NULL) - starttime, NULL);
+}
+
+static void cmd_hup(struct socket *sk, struct uidata *data, int argc, wchar_t **argv)
+{
+ extern volatile int reinit;
+
+ havepriv(PERM_ADMIN);
+ flog(LOG_NOTICE, "UI HUP request from %ls", data->username);
+ reinit = 1;
+ sq(sk, 0, L"200", L"Will reinit", NULL);
}
#undef haveargs
{L"download", cmd_download},
{L"lstrans", cmd_lstrans},
{L"cancel", cmd_cancel},
+ {L"reset", cmd_reset},
{L"notify", cmd_notify},
{L"sendchat", cmd_sendchat},
{L"search", cmd_search},
{L"register", cmd_register},
{L"sendmsg", cmd_sendmsg},
{L"uptime", cmd_uptime},
+ {L"hup", cmd_hup},
{NULL, NULL}
};
qcmd = data->queue;
if(qcmd != NULL)
{
+ data->queuesize--;
data->queue = qcmd->next;
if(qcmd == data->queuelast)
data->queuelast = qcmd->next;
case NOTIF_ID:
notif->argv[ca].d.n = va_arg(args, int);
break;
+ case NOTIF_OFF:
+ notif->argv[ca].d.o = va_arg(args, off_t);
+ break;
case NOTIF_STR:
- notif->argv[ca].d.s = wcsdup(va_arg(args, wchar_t *));
+ notif->argv[ca].d.s = swcsdup(va_arg(args, wchar_t *));
break;
case NOTIF_FLOAT:
notif->argv[ca].d.d = va_arg(args, double);
actives = data->next;
data->sk->readcb = NULL;
data->sk->errcb = NULL;
+ closesock(data->sk);
putsock(data->sk);
while((qcmd = unlinkqcmd(data)) != NULL)
freequeuecmd(qcmd);
data->queuelast = new;
if(data->queue == NULL)
data->queue = new;
+ data->queuesize++;
}
static struct uidata *newuidata(struct socket *sk)
break;
}
}
+ if(data->cbdata > 16384)
+ {
+ /* Kill clients that send us unreasonably long lines */
+ data->close = 1;
+ }
}
static void uierror(struct socket *sk, int err, struct uidata *data)
freeuidata(data);
}
-static void uiaccept(struct socket *sk, struct socket *newsk, void *data)
+static void uiaccept(struct lport *lp, struct socket *newsk, void *data)
{
struct uidata *uidata;
{
if(haspriv(data, PERM_SRCH) && data->notify.b.srch && !wcscmp(srch->owner, data->username))
{
- newnotif(data, 622, NOTIF_ID, srch->id, NOTIF_STR, sr->filename, NOTIF_STR, sr->fnet->name, NOTIF_STR, sr->peerid, NOTIF_INT, sr->size,
+ newnotif(data, 622, NOTIF_ID, srch->id, NOTIF_STR, sr->filename, NOTIF_STR, sr->fnet->name, NOTIF_STR, sr->peerid, NOTIF_OFF, sr->size,
NOTIF_INT, sr->slots, NOTIF_INT, (sr->fn == NULL)?-1:(sr->fn->id), NOTIF_FLOAT, sr->time, NOTIF_STR, (sr->hash == NULL)?L"":unparsehash(sr->hash), NOTIF_END);
}
}
for(data = actives; data != NULL; data = data->next)
{
if(haspriv(data, PERM_TRANS) && data->notify.b.tract && ((transfer->owner == 0) || (transfer->owner == data->uid)))
- newnotif(data, 613, NOTIF_ID, transfer->id, NOTIF_INT, transfer->size, NOTIF_END);
+ newnotif(data, 613, NOTIF_ID, transfer->id, NOTIF_OFF, transfer->size, NOTIF_END);
}
} else if(!wcscmp(attrib, L"error")) {
for(data = actives; data != NULL; data = data->next)
if(haspriv(data, PERM_TRANS) && data->notify.b.trprog && ((transfer->owner == 0) || (transfer->owner == data->uid)))
{
if((notif = findnotif(data->fnotif, 1, NOTIF_PEND, 615, transfer->id)) != NULL)
- notif->argv[1].d.n = transfer->curpos;
+ notif->argv[1].d.o = transfer->curpos;
else
- newnotif(data, 615, NOTIF_ID, transfer->id, NOTIF_INT, transfer->curpos, NOTIF_END)->rlimit = 0.5;
+ newnotif(data, 615, NOTIF_ID, transfer->id, NOTIF_OFF, transfer->curpos, NOTIF_END)->rlimit = 0.5;
}
}
return(0);
if(!hup)
{
- newuser(L"default", 0);
+ newuser(L"default", PERM_DISALLOW);
} else {
for(user = users; user != NULL; user = user->next)
{
}
}
-static int portupdate(struct configvar *var, void *uudata)
+static struct sockaddr_un *makeunixname(void)
+{
+ static struct sockaddr_un buf;
+ char *val;
+ struct passwd *pwd;
+ uid_t uid;
+
+ 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;
+ struct lport *newsock;
- if((uisocket = netcstcplisten(var->val.num, 1, uiaccept, NULL)) == NULL)
+ newsock = NULL;
+ if((var->val.num != -1) && ((newsock = netcstcplisten(var->val.num, 1, 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 TCP UI socket, reverting to old: %s", strerror(errno));
return(0);
}
- if(uisocket != NULL)
- putsock(uisocket);
- uisocket = newsock;
+ if(tcpsocket != NULL)
+ {
+ closelport(tcpsocket);
+ tcpsocket = NULL;
+ }
+ tcpsocket = newsock;
+ return(0);
+}
+
+static int unixsockupdate(struct configvar *var, void *uudata)
+{
+ struct lport *newsock;
+ struct sockaddr_un *un;
+ mode_t ou;
+
+ newsock = NULL;
+ ou = umask(0111);
+ if(((un = makeunixname()) != NULL) && ((newsock = netcslistenlocal(SOCK_STREAM, (struct sockaddr *)un, sizeof(*un), uiaccept, NULL)) == NULL))
+ {
+ umask(ou);
+ flog(LOG_WARNING, "could not create new Unix UI socket, reverting to old: %s", strerror(errno));
+ return(0);
+ }
+ umask(ou);
+ if(unixsocket != NULL)
+ {
+ closelport(unixsocket);
+ unixsocket = NULL;
+ }
+ unixsocket = newsock;
return(0);
}
static int init(int hup)
{
struct uiuser *user, *next;
+ struct sockaddr_un *un;
+ struct passwd *pwd;
+ wchar_t *wcsname;
+ mode_t ou;
if(hup)
{
if(!hup)
{
starttime = time(NULL);
- if(uisocket != NULL)
- putsock(uisocket);
- if((uisocket = netcstcplisten(confgetint("ui", "port"), 1, uiaccept, NULL)) == 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);
+ ou = umask(0111);
+ 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));
+ umask(ou);
+ flog(LOG_CRIT, "could not create Unix UI socket: %s", strerror(errno));
return(1);
}
- CBREG(confgetvar("ui", "port"), conf_update, portupdate, NULL, NULL);
+ umask(ou);
+ CBREG(confgetvar("ui", "unixsock"), conf_update, unixsockupdate, NULL, NULL);
GCBREG(newfncb, newfnetnode, NULL);
GCBREG(newtransfercb, newtransfernotify, NULL);
}
+ if(getuid() != 0)
+ {
+ for(user = users; user != NULL; user = user->next)
+ {
+ if(wcscmp(user->name, L"default"))
+ break;
+ }
+ if(!user)
+ {
+ if((pwd = getpwuid(getuid())) == NULL)
+ {
+ flog(LOG_CRIT, "could not get login info: %s", strerror(errno));
+ return(1);
+ }
+ if((wcsname = icmbstowcs(pwd->pw_name, NULL)) == NULL)
+ {
+ flog(LOG_CRIT, "could not convert user name into wcs: %s", strerror(errno));
+ return(1);
+ }
+ newuser(wcsname, ~PERM_DISALLOW);
+ free(wcsname);
+ }
+ }
return(0);
}
struct uidata *data, *next;
struct qcommand *qcmd;
struct notif *notif, *nnotif;
- wchar_t buf[64];
for(data = actives; data != NULL; data = next)
{
}
if(findnotif(notif->prev, 0, -1, notif->code, id) != NULL)
continue;
- sq(data->sk, 2, L"%%i", notif->code, NULL);
+ sq(data->sk, 2, L"%i", notif->code, NULL);
for(i = 0; i < notif->argc; i++)
{
switch(notif->argv[i].dt)
{
case NOTIF_INT:
case NOTIF_ID:
- sq(data->sk, 2, L"%%i", notif->argv[i].d.n, NULL);
+ sq(data->sk, 2, L"%i", notif->argv[i].d.n, NULL);
+ break;
+ case NOTIF_OFF:
+ sq(data->sk, 2, L"%oi", notif->argv[i].d.o, NULL);
break;
case NOTIF_STR:
if(notif->argv[i].d.s[0] == L'%')
- sq(data->sk, 2, L"%%s", notif->argv[i].d.s, NULL);
+ sq(data->sk, 2, L"%ls", notif->argv[i].d.s, NULL);
else
sq(data->sk, 2, notif->argv[i].d.s, NULL);
break;
case NOTIF_FLOAT:
- swprintf(buf, 64, L"%f", notif->argv[i].d.d);
- sq(data->sk, 2, buf, NULL);
+ sq(data->sk, 2, L"%f", notif->argv[i].d.d, NULL);
break;
}
}
freequeuecmd(qcmd);
return(1);
}
+ if(data->queuesize > 10)
+ {
+ /* Clients should not be queue up commands at all, since
+ * they should not send a new command before receiving a
+ * reply to the previous command. Therefore, we
+ * mercilessly massacre clients which are stacking up too
+ * many commands. */
+ data->close = 1;
+ }
}
return(0);
}
{
while(users != NULL)
freeuser(users);
+ if(tcpsocket != NULL)
+ closelport(tcpsocket);
+ if(unixsocket != NULL)
+ closelport(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}
};