/*
* 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 <netdb.h>
#include <sys/poll.h>
#include <pwd.h>
+#include <stdint.h>
#ifdef HAVE_RESOLVER
#include <arpa/nameser.h>
#include <resolv.h>
#define RESP_STR 1
#define RESP_INT 2
#define RESP_FLOAT 3
+#define RESP_LNUM 4
struct respclass
{
int family;
int sentcreds;
} servinfo;
+/* char dc_srv_local_addr; */
+char *dc_srv_local = (void *)&dc_srv_local;
+static void message(int bits, char *format, ...)
+{
+ static int hb = -1;
+ char *v;
+ va_list args;
+
+ if(hb == -1)
+ {
+ hb = 0;
+ if((v = getenv("LIBDCUI_MSG")) != NULL)
+ hb = strtol(v, NULL, 0) & 65535;
+ }
+ if(hb & bits)
+ {
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ }
+}
+
+static char *formataddress(struct sockaddr *arg, socklen_t arglen)
+{
+ struct sockaddr_in *ipv4;
+#ifdef HAVE_IPV6
+ struct sockaddr_in6 *ipv6;
+#endif
+ static char *ret = NULL;
+ char buf[1024];
+
+ if(ret != NULL)
+ free(ret);
+ ret = NULL;
+ switch(arg->sa_family)
+ {
+ case AF_UNIX:
+ ret = sprintf2("Unix socket (%s)", ((struct sockaddr_un *)arg)->sun_path);
+ break;
+ case AF_INET:
+ ipv4 = (struct sockaddr_in *)arg;
+ if(inet_ntop(AF_INET, &ipv4->sin_addr, buf, sizeof(buf)) == NULL)
+ return(NULL);
+ ret = sprintf2("%s:%i", buf, (int)ntohs(ipv4->sin_port));
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ ipv6 = (struct sockaddr_in6 *)arg;
+ if(inet_ntop(AF_INET6, &ipv6->sin6_addr, buf, sizeof(buf)) == NULL)
+ return(NULL);
+ ret = sprintf2("[%s]:%i", buf, (int)ntohs(ipv6->sin6_port));
+ break;
+#endif
+ default:
+ errno = EPFNOSUPPORT;
+ break;
+ }
+ return(ret);
+}
static struct dc_response *makeresp(void)
{
struct dc_response *new;
if((cmd->name != NULL) && !wcscmp(cmd->name, name))
break;
}
- if(cmd == NULL)
+ if(cmd == NULL) {
+ errno = ENOSYS; /* Bleh */
return(NULL);
+ }
}
new = smalloc(sizeof(*new));
new->tag = tag++;
{
freepart = 1;
part = swprintf2(L"%i", va_arg(al, int));
+ } else if(!wcscmp(tpart, L"li")) {
+ freepart = 1;
+ part = swprintf2(L"%ji", (intmax_t)va_arg(al, dc_lnum_t));
} else if(!wcscmp(tpart, L"s")) {
freepart = 1;
part = icmbstowcs(sarg = va_arg(al, char *), NULL);
return(-1);
}
} else if(!wcscmp(tpart, L"ls")) {
+ freepart = 0;
part = va_arg(al, wchar_t *);
} else if(!wcscmp(tpart, L"ll")) {
freepart = 1;
} else {
if(buf != NULL)
free(buf);
+ errno = EINVAL;
return(-1);
}
} else {
if(ret)
{
int newfd;
-
+
+ message(2, "could not connect to %s: %s\n", formataddress(curhost->ai_addr, curhost->ai_addrlen), strerror(ret));
for(curhost = curhost->ai_next; curhost != NULL; curhost = curhost->ai_next)
{
if((newfd = socket(curhost->ai_family, curhost->ai_socktype, curhost->ai_protocol)) < 0)
dup2(newfd, fd);
close(newfd);
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+ message(4, "connecting to %s\n", formataddress(curhost->ai_addr, curhost->ai_addrlen));
if(connect(fd, (struct sockaddr *)curhost->ai_addr, curhost->ai_addrlen))
{
if(errno == EINPROGRESS)
return(0);
+ message(2, "could not connect to %s: %s\n", formataddress(curhost->ai_addr, curhost->ai_addrlen), strerror(ret));
} else {
break;
}
return(0);
}
+#if UNIX_AUTH_STYLE == 1
static void mkcreds(struct msghdr *msg)
{
struct ucred *ucred;
ucred->gid = getgid();
msg->msg_controllen = cmsg->cmsg_len;
}
+#endif
int dc_handlewrite(void)
{
msg.msg_iovlen = 1;
bufvec.iov_base = queue->buf;
bufvec.iov_len = queue->buflen;
+#if UNIX_AUTH_STYLE == 1
if((servinfo.family == PF_UNIX) && !servinfo.sentcreds)
{
mkcreds(&msg);
servinfo.sentcreds = 1;
}
+#endif
ret = sendmsg(fd, &msg, MSG_NOSIGNAL | MSG_DONTWAIT);
if(ret < 0)
{
if(getsrvrr(name, &realname, &port))
return(NULL);
+ message(4, "SRV RR resolved: %s -> %s\n", name, realname);
ret = resolvtcp(realname, port);
free(realname);
return(ret);
return(NULL);
}
-static struct addrinfo *defaulthost(void)
+static struct addrinfo *getlocalai(void)
{
struct addrinfo *ret;
struct passwd *pwd;
char *tmp;
- char dn[1024];
-
- if(((tmp = getenv("DCSERVER")) != NULL) && *tmp)
- return(resolvhost(tmp));
+
ret = NULL;
if((getuid() != 0) && ((pwd = getpwuid(getuid())) != NULL))
{
tmp = sprintf2("/tmp/doldacond-%s", pwd->pw_name);
- ret = gaicat(ret, unixgai(SOCK_STREAM, tmp));
+ ret = unixgai(SOCK_STREAM, tmp);
free(tmp);
}
ret = gaicat(ret, unixgai(SOCK_STREAM, "/var/run/doldacond.sock"));
+ return(ret);
+}
+
+static struct addrinfo *defaulthost(void)
+{
+ struct addrinfo *ret;
+ char *tmp;
+ char dn[1024];
+
+ if(((tmp = getenv("DCSERVER")) != NULL) && *tmp) {
+ message(4, "using DCSERVER: %s\n", tmp);
+ return(resolvhost(tmp));
+ }
+ ret = getlocalai();
ret = gaicat(ret, resolvtcp("localhost", 1500));
if(!getdomainname(dn, sizeof(dn)) && *dn && strcmp(dn, "(none)"))
ret = gaicat(ret, resolvsrv(dn));
return(ret);
}
-int dc_connect(char *host)
+static int dc_connectai(struct addrinfo *hosts, struct qcmd **cnctcmd)
{
struct qcmd *qcmd;
int errnobak;
state = -1;
if(hostlist != NULL)
freeaddrinfo(hostlist);
- if(!host || !*host)
- hostlist = defaulthost();
- else
- hostlist = resolvhost(host);
- if(hostlist == NULL)
- return(-1);
+ hostlist = hosts;
for(curhost = hostlist; curhost != NULL; curhost = curhost->ai_next)
{
if((fd = socket(curhost->ai_family, curhost->ai_socktype, curhost->ai_protocol)) < 0)
{
errnobak = errno;
+ freeaddrinfo(hostlist);
+ hostlist = NULL;
errno = errnobak;
return(-1);
}
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
+ message(4, "connecting to %s\n", formataddress(curhost->ai_addr, curhost->ai_addrlen));
if(connect(fd, (struct sockaddr *)curhost->ai_addr, curhost->ai_addrlen))
{
if(errno == EINPROGRESS)
state = 0;
break;
}
+ message(2, "could not connect to %s: %s\n", formataddress(curhost->ai_addr, curhost->ai_addrlen), strerror(errno));
close(fd);
fd = -1;
} else {
break;
}
}
- qcmd = makeqcmd(NULL);
- resetreader = 1;
+ if(state != -1)
+ {
+ qcmd = makeqcmd(NULL);
+ if(cnctcmd != NULL)
+ *cnctcmd = qcmd;
+ resetreader = 1;
+ } else {
+ free(hostlist);
+ hostlist = NULL;
+ }
return(fd);
}
+static int dc_connect2(char *host, struct qcmd **cnctcmd)
+{
+ struct addrinfo *ai;
+ struct qcmd *qcmd;
+ int ret;
+
+ if(host == dc_srv_local) {
+ message(4, "connect start: Unix\n");
+ ai = getlocalai();
+ } else if(!host || !*host) {
+ message(4, "connect start: default\n");
+ ai = defaulthost();
+ } else {
+ message(4, "connect start: host %s\n", host);
+ ai = resolvhost(host);
+ }
+ if(ai == NULL)
+ return(-1);
+ ret = dc_connectai(ai, &qcmd);
+ if((ret >= 0) && (cnctcmd != NULL))
+ *cnctcmd = qcmd;
+ return(ret);
+}
+
+int dc_connect(char *host)
+{
+ return(dc_connect2(host, NULL));
+}
+
+int dc_connectsync(char *host, struct dc_response **respbuf)
+{
+ int ret;
+ struct qcmd *cc;
+ struct dc_response *resp;
+
+ if((ret = dc_connect2(host, &cc)) < 0)
+ return(-1);
+ resp = dc_gettaggedrespsync(cc->tag);
+ if(resp == NULL) {
+ dc_disconnect();
+ return(-1);
+ }
+ if(respbuf == NULL)
+ dc_freeresp(resp);
+ else
+ *respbuf = resp;
+ return(ret);
+}
+
+int dc_connectsync2(char *host, int rev)
+{
+ int ret;
+ struct dc_response *resp;
+
+ if((ret = dc_connectsync(host, &resp)) < 0)
+ return(-1);
+ if(dc_checkprotocol(resp, rev))
+ {
+ dc_freeresp(resp);
+ dc_disconnect();
+ errno = EPROTONOSUPPORT;
+ return(-1);
+ }
+ dc_freeresp(resp);
+ return(ret);
+}
+
struct dc_intresp *dc_interpret(struct dc_response *resp)
{
int i;
iresp->argv[iresp->argc].type = cls->wordt[i];
iresp->argc++;
break;
+ case RESP_LNUM:
+ sizebuf(&(iresp->argv), &args, iresp->argc + 1, sizeof(*(iresp->argv)), 1);
+ iresp->argv[iresp->argc].val.lnum = wcstoll(resp->rlines[resp->curline].argv[i + 1], NULL, 0);
+ iresp->argv[iresp->argc].type = cls->wordt[i];
+ iresp->argc++;
+ break;
}
}
resp->curline++;
if((ires = dc_interpret(resp)) == NULL)
return(-1);
low = ires->argv[0].val.num;
- high = ires->argv[0].val.num;
+ high = ires->argv[1].val.num;
dc_freeires(ires);
if((revision < low) || (revision > high))
return(-1);
{
return(servinfo.hostname);
}
+
+int dc_getfd(void)
+{
+ return(fd);
+}