/*
* 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
#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 *tcpsocket = NULL;
-struct socket *unixsocket = NULL;
+struct lport *tcpsocket = NULL;
+struct lport *unixsocket = NULL;
static time_t starttime;
static wchar_t *quoteword(wchar_t *word)
{
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);
/* 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", L"needed", L"%x", (p), L"had", L"%x", data->userinfo->perms, 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;
- 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"201", L"1", L"2", L"Dolda Connect daemon v" 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(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));
+ flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formatsockpeer(sk));
logout(data);
break;
case AUTH_PASS:
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));
+ flog(LOG_INFO, "authentication failed for %ls from %s", data->username, formatsockpeer(sk));
logout(data);
break;
case AUTH_PASS:
{
int i;
struct fnetnode *fn;
- struct fnetpeer *peer;
+ 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", L"%ls", peer->id, L"%ls", 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)
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)
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;
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);
}
for(sr = srch->results; sr != NULL; sr = sr->next)
{
sq(sk, (sr->next != NULL)?1:0, L"200", L"%ls", sr->filename,
- sr->fnet->name, L"%ls", sr->peerid, L"%i", sr->size,
+ 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);
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
#undef havepriv
{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);
static int tcpportupdate(struct configvar *var, void *uudata)
{
- struct socket *newsock;
+ struct lport *newsock;
newsock = NULL;
if((var->val.num != -1) && ((newsock = netcstcplisten(var->val.num, 1, uiaccept, NULL)) == NULL))
}
if(tcpsocket != NULL)
{
- putsock(tcpsocket);
+ closelport(tcpsocket);
tcpsocket = NULL;
}
tcpsocket = newsock;
static int unixsockupdate(struct configvar *var, void *uudata)
{
- struct socket *newsock;
+ 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)
{
- putsock(unixsocket);
+ closelport(unixsocket);
unixsocket = NULL;
}
unixsocket = newsock;
struct sockaddr_un *un;
struct passwd *pwd;
wchar_t *wcsname;
+ mode_t ou;
if(hup)
{
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))
{
+ umask(ou);
flog(LOG_CRIT, "could not create Unix UI socket: %s", strerror(errno));
return(1);
}
+ umask(ou);
CBREG(confgetvar("ui", "unixsock"), conf_update, unixsockupdate, NULL, NULL);
GCBREG(newfncb, newfnetnode, NULL);
GCBREG(newtransfercb, newtransfernotify, NULL);
case NOTIF_ID:
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"%ls", notif->argv[i].d.s, NULL);
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)
- putsock(tcpsocket);
+ closelport(tcpsocket);
if(unixsocket != NULL)
- putsock(unixsocket);
+ closelport(unixsocket);
}
static struct configvar myvars[] =