#include <netinet/in.h>
#include <netdb.h>
#include <sys/signal.h>
-#include <printf.h>
+#include <sys/stat.h> /* For rebindunix() */
#ifdef HAVE_LINUX_SOCKIOS_H
#include <linux/sockios.h>
#endif
static struct configvar myvars[] =
{
- /* 0 = Direct mode, 1 = Passive mode, 2 = SOCKS proxy */
+ /** The network mode to use. Currently supported values are 0 for
+ * active mode and 1 for passive mode. In the future, SOCKS5 proxy
+ * support may be added. */
{CONF_VAR_INT, "mode", {.num = 0}},
+ /** Set the SO_REUSEADDR socket option on listening sockets, so
+ * that dead TCP connections waiting for timeout are ignored. */
{CONF_VAR_BOOL, "reuseaddr", {.num = 0}},
- /* Only for direct mode */
+ /** Overrides the IPv4 address reported to other clients in active
+ * mode. Useful for servers behind NAT routers. If both this and
+ * net.publicif are unspecified the address of the hub connection
+ * is used. */
{CONF_VAR_IPV4, "visibleipv4", {.ipv4 = {0}}},
+ /** Specifies an interface name from which to fetch the IPv4
+ * address reported to other clients in active mode. If both this
+ * and net.visibleipv4 are unspecified the address of the hub
+ * connection is used. */
{CONF_VAR_STRING, "publicif", {.str = L""}},
/* Diffserv should be supported on IPv4, too, but I don't know the
* API to do that. */
+ /** The Diffserv value to use on IPv6 connections when the
+ * minimize cost TOS value is used (see the TOS VALUES
+ * section). */
{CONF_VAR_INT, "diffserv-mincost", {.num = 0}},
+ /** The Diffserv value to use on IPv6 connections when the
+ * maximize reliability TOS value is used (see the TOS VALUES
+ * section). */
{CONF_VAR_INT, "diffserv-maxrel", {.num = 0}},
+ /** The Diffserv value to use on IPv6 connections when the
+ * maximize throughput TOS value is used (see the TOS VALUES
+ * section). */
{CONF_VAR_INT, "diffserv-maxtp", {.num = 0}},
+ /** The Diffserv value to use on IPv6 connections when the
+ * minimize delay TOS value is used (see the TOS VALUES
+ * section). */
{CONF_VAR_INT, "diffserv-mindelay", {.num = 0}},
{CONF_VAR_END}
};
flog(LOG_ERR, "could not convert net.publicif into local charset: %s", strerror(errno));
return(-1);
}
+ if(!strcmp(pif, ""))
+ return(1);
if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
return(-1);
conf.ifc_buf = smalloc(conf.ifc_len = 65536);
ipv4 = NULL;
for(ifr = conf.ifc_ifcu.ifcu_req; (void *)ifr < bufend; ifr++)
{
+ if(strcmp(ifr->ifr_name, pif))
+ continue;
memset(&req, 0, sizeof(req));
memcpy(req.ifr_name, ifr->ifr_name, sizeof(ifr->ifr_name));
if(ioctl(sock, SIOCGIFFLAGS, &req) < 0)
+ break;
+ if(!(req.ifr_flags & IFF_UP))
{
- free(conf.ifc_buf);
- close(sock);
- return(-1);
+ flog(LOG_WARNING, "public interface is down");
+ break;
}
- if(!(req.ifr_flags & IFF_UP))
- continue;
- if(ifr->ifr_addr.sa_family == AF_INET)
+ if(ifr->ifr_addr.sa_family != AF_INET)
{
- if(ntohl(((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr.s_addr) == 0x7f000001)
- continue;
- if(ipv4 == NULL)
- {
- ipv4 = smalloc(sizeof(*ipv4));
- memcpy(ipv4, &ifr->ifr_addr, sizeof(ifr->ifr_addr));
- } else {
- free(ipv4);
- free(conf.ifc_buf);
- flog(LOG_WARNING, "could not locate an unambiguous interface for determining your public IP address - set net.publicif");
- errno = ENFILE; /* XXX: There's no appropriate one for this... */
- return(-1);
- }
+ flog(LOG_WARNING, "address of the public interface is not AF_INET");
+ break;
}
+ ipv4 = smalloc(sizeof(*ipv4));
+ memcpy(ipv4, &ifr->ifr_addr, sizeof(ifr->ifr_addr));
+ break;
}
free(conf.ifc_buf);
close(sock);
errno = ENETDOWN;
return(-1);
}
- errno = EPFNOSUPPORT;
- return(-1);
+ return(1);
}
static struct socket *newsock(int type)
new->close = 0;
new->remote = NULL;
new->remotelen = 0;
+ new->ucred.uid = -1;
+ new->ucred.gid = -1;
switch(type)
{
case SOCK_STREAM:
{
sk->outbuf.d.f = buf->next;
free(buf->data);
+ free(buf->addr);
free(buf);
}
while((buf = sk->inbuf.d.f) != NULL)
{
sk->inbuf.d.f = buf->next;
free(buf->data);
+ free(buf->addr);
free(buf);
}
break;
}
- if(sk->fd >= 0)
- close(sk->fd);
+ closesock(sk);
if(sk->remote != NULL)
free(sk->remote);
free(sk);
return(NULL);
}
+static void recvcmsg(struct socket *sk, struct msghdr *msg)
+{
+ struct cmsghdr *cmsg;
+
+ for(cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg))
+ {
+#if UNIX_AUTH_STYLE == 1
+ if((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_CREDENTIALS))
+ {
+ struct ucred *cred;
+ if(sk->ucred.uid == -1)
+ {
+ cred = (struct ucred *)CMSG_DATA(cmsg);
+ sk->ucred.uid = cred->uid;
+ sk->ucred.gid = cred->gid;
+ }
+ }
+#endif
+ }
+}
+
static void sockrecv(struct socket *sk)
{
int ret, inq;
struct dgrambuf *dbuf;
+ struct msghdr msg;
+ char cbuf[65536];
+ struct iovec bufvec;
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &bufvec;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
switch(sk->type)
{
case SOCK_STREAM:
if(inq > 65536)
inq = 65536;
sizebuf(&sk->inbuf.s.buf, &sk->inbuf.s.bufsize, sk->inbuf.s.datasize + inq, 1, 1);
- ret = read(sk->fd, sk->inbuf.s.buf + sk->inbuf.s.datasize, inq);
+ if(sk->isrealsocket)
+ {
+ bufvec.iov_base = sk->inbuf.s.buf + sk->inbuf.s.datasize;
+ bufvec.iov_len = inq;
+ ret = recvmsg(sk->fd, &msg, 0);
+ } else {
+ ret = read(sk->fd, sk->inbuf.s.buf + sk->inbuf.s.datasize, inq);
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ }
if(ret < 0)
{
if((errno == EINTR) || (errno == EAGAIN))
closesock(sk);
return;
}
+ if(msg.msg_flags & MSG_CTRUNC)
+ flog(LOG_DEBUG, "ancillary data was truncated");
+ else
+ recvcmsg(sk, &msg);
if(ret == 0)
{
if(sk->errcb != NULL)
sk->readcb(sk, sk->data);
break;
case SOCK_DGRAM:
+#if defined(HAVE_LINUX_SOCKIOS_H) && defined(SIOCINQ)
if(ioctl(sk->fd, SIOCINQ, &inq))
{
/* I don't really know what could go wrong here, so let's
flog(LOG_WARNING, "SIOCINQ return %s on socket %i", strerror(errno), sk->fd);
return;
}
+#else
+ inq = 65536;
+#endif
dbuf = smalloc(sizeof(*dbuf));
dbuf->data = smalloc(inq);
dbuf->addr = smalloc(dbuf->addrlen = sizeof(struct sockaddr_storage));
+ /*
ret = recvfrom(sk->fd, dbuf->data, inq, 0, dbuf->addr, &dbuf->addrlen);
+ */
+ msg.msg_name = dbuf->addr;
+ msg.msg_namelen = dbuf->addrlen;
+ bufvec.iov_base = dbuf->data;
+ bufvec.iov_len = inq;
+ ret = recvmsg(sk->fd, &msg, 0);
+ dbuf->addrlen = msg.msg_namelen;
if(ret < 0)
{
free(dbuf->addr);
closesock(sk);
return;
}
+ if(msg.msg_flags & MSG_CTRUNC)
+ flog(LOG_DEBUG, "ancillary data was truncated");
+ else
+ recvcmsg(sk, &msg);
/* On UDP/IPv[46], ret == 0 doesn't mean EOF (since UDP can't
* have EOF), but rather an empty packet. I don't know if any
* other potential DGRAM protocols might have an EOF
void closesock(struct socket *sk)
{
+ struct sockaddr_un *un;
+
+ if((sk->family == AF_UNIX) && !sockgetlocalname(sk, (struct sockaddr **)&un, NULL) && (un->sun_family == PF_UNIX))
+ {
+ if((sk->state == SOCK_LST) && strchr(un->sun_path, '/'))
+ {
+ if(unlink(un->sun_path))
+ flog(LOG_WARNING, "could not unlink Unix socket %s: %s", un->sun_path, strerror(errno));
+ }
+ }
sk->state = SOCK_STL;
close(sk->fd);
sk->fd = -1;
return(ret);
}
-struct socket *netcslisten(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct socket *, struct socket *, void *), void *data)
+/*
+ * Seriously, I don't know if it's naughty or not to remove
+ * pre-existing Unix sockets.
+ */
+static int rebindunix(struct socket *sk, struct sockaddr *name, socklen_t namelen)
{
- struct socket *sk;
- int intbuf;
+ struct sockaddr_un *un;
+ struct stat sb;
- if(confgetint("net", "mode") == 1)
- {
- errno = EOPNOTSUPP;
- return(NULL);
- }
- /* I don't know if this is actually correct (it probably isn't),
- * but since, at on least Linux systems, PF_* are specifically
- * #define'd to their AF_* counterparts, it allows for a severely
- * smoother implementation. If it breaks something on your
- * platform, please tell me so.
- */
- if(confgetint("net", "mode") == 0)
- {
- if((sk = mksock(name->sa_family, type)) == NULL)
- return(NULL);
- sk->state = SOCK_LST;
- if(confgetint("net", "reuseaddr"))
- {
- intbuf = 1;
- setsockopt(sk->fd, SOL_SOCKET, SO_REUSEADDR, &intbuf, sizeof(intbuf));
- }
- if(bind(sk->fd, name, namelen) < 0)
- {
- putsock(sk);
- return(NULL);
- }
- if(listen(sk->fd, 16) < 0)
- {
- putsock(sk);
- return(NULL);
- }
- sk->acceptcb = func;
- sk->data = data;
- return(sk);
- }
- errno = EOPNOTSUPP;
- return(NULL);
+ if((sk->family != AF_UNIX) || (name->sa_family != PF_UNIX))
+ return(-1);
+ un = (struct sockaddr_un *)name;
+ if(stat(un->sun_path, &sb))
+ return(-1);
+ if(!S_ISSOCK(sb.st_mode))
+ return(-1);
+ if(unlink(un->sun_path))
+ return(-1);
+ if(bind(sk->fd, name, namelen) < 0)
+ return(-1);
+ return(0);
}
/*
intbuf = 1;
setsockopt(sk->fd, SOL_SOCKET, SO_REUSEADDR, &intbuf, sizeof(intbuf));
}
- if(bind(sk->fd, name, namelen) < 0)
+ if((bind(sk->fd, name, namelen) < 0) && ((errno != EADDRINUSE) || (rebindunix(sk, name, namelen) < 0)))
{
putsock(sk);
return(NULL);
return(sk);
}
+struct socket *netcslisten(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct socket *, struct socket *, void *), void *data)
+{
+ if(confgetint("net", "mode") == 1)
+ {
+ errno = EOPNOTSUPP;
+ return(NULL);
+ }
+ if(confgetint("net", "mode") == 0)
+ return(netcslistenlocal(type, name, namelen, func, data));
+ errno = EOPNOTSUPP;
+ return(NULL);
+}
+
+struct socket *netcstcplisten(int port, int local, void (*func)(struct socket *, struct socket *, void *), void *data)
+{
+ struct sockaddr_in addr;
+#ifdef HAVE_IPV6
+ struct sockaddr_in6 addr6;
+#endif
+ struct socket *(*csfunc)(int, struct sockaddr *, socklen_t, void (*)(struct socket *, struct socket *, void *), void *);
+ struct socket *ret;
+
+ if(local)
+ csfunc = netcslistenlocal;
+ else
+ csfunc = netcslisten;
+#ifdef HAVE_IPV6
+ memset(&addr6, 0, sizeof(addr6));
+ addr6.sin6_family = AF_INET6;
+ addr6.sin6_port = htons(port);
+ addr6.sin6_addr = in6addr_any;
+ if((ret = csfunc(SOCK_STREAM, (struct sockaddr *)&addr6, sizeof(addr6), func, data)) != NULL)
+ return(ret);
+ if((ret == NULL) && (errno != EAFNOSUPPORT))
+ return(NULL);
+#endif
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ return(csfunc(SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), func, data));
+}
+
struct socket *netcsdgram(struct sockaddr *name, socklen_t namelen)
{
struct socket *sk;
return(NULL);
}
+static void acceptunix(struct socket *sk)
+{
+ int buf;
+
+ buf = 1;
+#if UNIX_AUTH_STYLE == 1
+ if(setsockopt(sk->fd, SOL_SOCKET, SO_PASSCRED, &buf, sizeof(buf)) < 0)
+ flog(LOG_WARNING, "could not enable SO_PASSCRED on Unix socket %i: %s", sk->fd, strerror(errno));
+#elif UNIX_AUTH_STYLE == 2
+ if(getpeereid(sk->fd, &sk->ucred.uid, &sk->ucred.gid) < 0)
+ {
+ flog(LOG_WARNING, "could not get peer creds on Unix socket %i: %s", sk->fd, strerror(errno));
+ sk->ucred.uid = -1;
+ sk->ucred.gid = -1;
+ }
+#endif
+}
+
int pollsocks(int timeout)
{
- int i, num, ret, retlen;
+ int i, num, ret;
+ socklen_t retlen;
int newfd;
struct pollfd *pfds;
struct socket *sk, *next, *newsk;
newsk->state = SOCK_EST;
memcpy(newsk->remote = smalloc(sslen), &ss, sslen);
newsk->remotelen = sslen;
- putsock(newsk);
+ if(ss.ss_family == PF_UNIX)
+ acceptunix(newsk);
if(sk->acceptcb != NULL)
sk->acceptcb(sk, newsk, sk->data);
+ putsock(newsk);
}
if(pfds[i].revents & POLLERR)
{
{
int buf;
+ if(sk->family == AF_UNIX)
+ return(0); /* Unix sockets are always perfect. :) */
if(sk->family == AF_INET)
{
switch(tos)
flog(LOG_WARNING, "attempted to set unknown TOS value %i to IPv4 sock", tos);
return(-1);
}
- if(setsockopt(sk->fd, SOL_IP, IP_TOS, &buf, sizeof(buf)) < 0)
+ if(setsockopt(sk->fd, IPPROTO_IP, IP_TOS, &buf, sizeof(buf)) < 0)
{
flog(LOG_WARNING, "could not set sock TOS to %i: %s", tos, strerror(errno));
return(-1);
{
if((ret = read(data->fd, buf, sizeof(buf))) != 4)
{
- errno = ENONET;
+ errno = ENOENT;
data->callback(NULL, 0, data->data);
} else {
ipv4 = (struct sockaddr_in *)&data->addr;
data->callback((struct sockaddr *)ipv4, sizeof(*ipv4), data->data);
}
} else {
- errno = ENONET;
+ errno = ENOENT;
data->callback(NULL, 0, data->data);
}
close(data->fd);
len = sizeof(name);
if(getsockname(sk->fd, (struct sockaddr *)&name, &len) < 0)
{
- flog(LOG_ERR, "BUG: alive socket with dead fd in sockgetlocalname");
+ flog(LOG_ERR, "BUG: alive socket with dead fd in sockgetlocalname (%s)", strerror(errno));
return(-1);
}
*namebuf = memcpy(smalloc(len), &name, len);
- *lenbuf = len;
+ if(lenbuf != NULL)
+ *lenbuf = len;
+ return(0);
+}
+
+static void sethostaddr(struct sockaddr *dst, struct sockaddr *src)
+{
+ if(dst->sa_family != src->sa_family)
+ {
+ flog(LOG_ERR, "BUG: non-matching socket families in sethostaddr (%i -> %i)", src->sa_family, dst->sa_family);
+ return;
+ }
+ switch(src->sa_family)
+ {
+ case AF_INET:
+ ((struct sockaddr_in *)dst)->sin_addr = ((struct sockaddr_in *)src)->sin_addr;
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *)dst)->sin6_addr = ((struct sockaddr_in6 *)src)->sin6_addr;
+ break;
+ default:
+ flog(LOG_WARNING, "sethostaddr unimplemented for family %i", src->sa_family);
+ break;
+ }
+}
+
+static int makepublic(struct sockaddr *addr)
+{
+ int ret;
+ socklen_t plen;
+ struct sockaddr *pname;
+
+ if((ret = getpublicaddr(addr->sa_family, &pname, &plen)) < 0)
+ {
+ flog(LOG_ERR, "could not get public address: %s", strerror(errno));
+ return(-1);
+ }
+ if(ret)
+ return(0);
+ sethostaddr(addr, pname);
+ free(pname);
return(0);
}
int sockgetremotename(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf)
{
socklen_t len;
- struct sockaddr_storage name;
- struct sockaddr_in *ipv4;
- struct sockaddr *pname;
- socklen_t pnamelen;
+ struct sockaddr *name;
switch(confgetint("net", "mode"))
{
case 0:
*namebuf = NULL;
if((sk->state == SOCK_STL) || (sk->fd < 0))
- return(-1);
- len = sizeof(name);
- if(getsockname(sk->fd, (struct sockaddr *)&name, &len) < 0)
{
- flog(LOG_ERR, "BUG: alive socket with dead fd in sockgetremotename");
+ errno = EBADF;
return(-1);
}
- if(name.ss_family == AF_INET)
+ if(!sockgetlocalname(sk, &name, &len))
{
- ipv4 = (struct sockaddr_in *)&name;
- if(getpublicaddr(AF_INET, &pname, &pnamelen) < 0)
- {
- flog(LOG_WARNING, "could not determine public IP address - strange things may happen");
- return(-1);
- }
- ipv4->sin_addr.s_addr = ((struct sockaddr_in *)pname)->sin_addr.s_addr;
- free(pname);
+ *namebuf = name;
+ *lenbuf = len;
+ makepublic(name);
+ return(0);
}
- *namebuf = memcpy(smalloc(len), &name, len);
- *lenbuf = len;
- return(0);
+ flog(LOG_ERR, "could not get remotely accessible name by any means");
+ return(-1);
case 1:
errno = EOPNOTSUPP;
return(-1);
}
}
+int sockgetremotename2(struct socket *sk, struct socket *sk2, struct sockaddr **namebuf, socklen_t *lenbuf)
+{
+ struct sockaddr *name1, *name2;
+ socklen_t len1, len2;
+
+ if(sk->family != sk2->family)
+ {
+ flog(LOG_ERR, "using sockgetremotename2 with sockets of differing family: %i %i", sk->family, sk2->family);
+ return(-1);
+ }
+ if(sockgetremotename(sk, &name1, &len1))
+ return(-1);
+ if(sockgetremotename(sk2, &name2, &len2)) {
+ free(name1);
+ return(-1);
+ }
+ sethostaddr(name1, name2);
+ free(name2);
+ *namebuf = name1;
+ *lenbuf = len1;
+ return(0);
+}
+
+int addreq(struct sockaddr *x, struct sockaddr *y)
+{
+ struct sockaddr_un *u1, *u2;
+ struct sockaddr_in *n1, *n2;
+#ifdef HAVE_IPV6
+ struct sockaddr_in6 *s1, *s2;
+#endif
+
+ if(x->sa_family != y->sa_family)
+ return(0);
+ switch(x->sa_family) {
+ case AF_UNIX:
+ u1 = (struct sockaddr_un *)x; u2 = (struct sockaddr_un *)y;
+ if(strncmp(u1->sun_path, u2->sun_path, sizeof(u1->sun_path)))
+ return(0);
+ break;
+ case AF_INET:
+ n1 = (struct sockaddr_in *)x; n2 = (struct sockaddr_in *)y;
+ if(n1->sin_port != n2->sin_port)
+ return(0);
+ if(n1->sin_addr.s_addr != n2->sin_addr.s_addr)
+ return(0);
+ break;
+#ifdef HAVE_IPV6
+ case AF_INET6:
+ s1 = (struct sockaddr_in6 *)x; s2 = (struct sockaddr_in6 *)y;
+ if(s1->sin6_port != s2->sin6_port)
+ return(0);
+ if(memcmp(s1->sin6_addr.s6_addr, s2->sin6_addr.s6_addr, sizeof(s1->sin6_addr.s6_addr)))
+ return(0);
+ break;
+#endif
+ }
+ return(1);
+}
+
char *formataddress(struct sockaddr *arg, socklen_t arglen)
{
- struct sockaddr_un *UNIX; /* Some wise guy has #defined unix with
- * lowercase letters to 1, so I do this
- * instead. */
struct sockaddr_in *ipv4;
#ifdef HAVE_IPV6
struct sockaddr_in6 *ipv6;
switch(arg->sa_family)
{
case AF_UNIX:
- UNIX = (struct sockaddr_un *)arg;
- ret = sprintf2("%s", UNIX->sun_path);
+ ret = sstrdup("Unix socket");
break;
case AF_INET:
ipv4 = (struct sockaddr_in *)arg;