#include <netinet/in.h>
#include <netdb.h>
#include <sys/signal.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}
};
new->inbuf.d.f = new->inbuf.d.l = NULL;
break;
}
- CBCHAININIT(new, socket_conn);
- CBCHAININIT(new, socket_err);
- CBCHAININIT(new, socket_read);
- CBCHAININIT(new, socket_write);
- CBCHAININIT(new, socket_accept);
+ new->conncb = NULL;
+ new->errcb = NULL;
+ new->readcb = NULL;
+ new->writecb = NULL;
+ new->acceptcb = NULL;
new->next = sockets;
new->prev = NULL;
if(sockets != NULL)
if(--(sk->refcount) == 0)
{
- CBCHAINFREE(sk, socket_conn);
- CBCHAINFREE(sk, socket_err);
- CBCHAINFREE(sk, socket_read);
- CBCHAINFREE(sk, socket_write);
- CBCHAINFREE(sk, socket_accept);
switch(sk->type)
{
case SOCK_STREAM:
}
break;
}
- if(sk->fd >= 0)
- close(sk->fd);
+ closesock(sk);
if(sk->remote != NULL)
free(sk->remote);
free(sk);
{
if((errno == EINTR) || (errno == EAGAIN))
return;
- CBCHAINDOCB(sk, socket_err, sk, errno);
+ if(sk->errcb != NULL)
+ sk->errcb(sk, errno, sk->data);
closesock(sk);
return;
}
if(ret == 0)
{
- CBCHAINDOCB(sk, socket_err, sk, 0);
+ if(sk->errcb != NULL)
+ sk->errcb(sk, 0, sk->data);
closesock(sk);
return;
}
sk->inbuf.s.datasize += ret;
- CBCHAINDOCB(sk, socket_read, sk);
+ if(sk->readcb != NULL)
+ sk->readcb(sk, sk->data);
break;
case SOCK_DGRAM:
if(ioctl(sk->fd, SIOCINQ, &inq))
free(dbuf);
if((errno == EINTR) || (errno == EAGAIN))
return;
- CBCHAINDOCB(sk, socket_err, sk, errno);
+ if(sk->errcb != NULL)
+ sk->errcb(sk, errno, sk->data);
closesock(sk);
return;
}
free(dbuf);
if(!((sk->family == AF_INET) || (sk->family == AF_INET6)))
{
- CBCHAINDOCB(sk, socket_err, sk, 0);
+ if(sk->errcb != NULL)
+ sk->errcb(sk, 0, sk->data);
closesock(sk);
}
return;
else
sk->inbuf.d.f = dbuf;
sk->inbuf.d.l = dbuf;
- CBCHAINDOCB(sk, socket_read, sk);
+ if(sk->readcb != NULL)
+ sk->readcb(sk, sk->data);
break;
}
}
if(ret > 0)
{
memmove(sk->outbuf.s.buf, ((char *)sk->outbuf.s.buf) + ret, sk->outbuf.s.datasize -= ret);
- CBCHAINDOCB(sk, socket_write, sk);
+ if(sk->writecb != NULL)
+ sk->writecb(sk, sk->data);
}
break;
case SOCK_DGRAM:
free(dbuf->data);
free(dbuf->addr);
free(dbuf);
- CBCHAINDOCB(sk, socket_write, sk);
+ if(sk->writecb != NULL)
+ sk->writecb(sk, sk->data);
break;
}
}
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;
}
/*
+ * 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 sockaddr_un *un;
+ struct stat sb;
+
+ 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);
+}
+
+/*
* The difference between netcslisten() and netcslistenlocal() is that
* netcslistenlocal() always listens on the local host, instead of
* following proxy/passive mode directions. It is suitable for eg. the
* netcslisten() instead.
*/
-struct socket *netcslistenlocal(int type, struct sockaddr *name, socklen_t namelen, int (*func)(struct socket *, struct socket *, void *), void *data)
+struct socket *netcslistenlocal(int type, struct sockaddr *name, socklen_t namelen, void (*func)(struct socket *, struct socket *, void *), void *data)
{
struct socket *sk;
int intbuf;
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);
putsock(sk);
return(NULL);
}
- if(func != NULL)
- CBREG(sk, socket_accept, func, NULL, data);
+ sk->acceptcb = func;
+ sk->data = data;
return(sk);
}
-struct socket *netcslisten(int type, struct sockaddr *name, socklen_t namelen, int (*func)(struct socket *, struct socket *, void *), void *data)
+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)
{
return(NULL);
}
-struct socket *netcstcplisten(int port, int local, int (*func)(struct socket *, struct socket *, void *), void *data)
+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, int (*)(struct socket *, struct socket *, void *), void *);
+ struct socket *(*csfunc)(int, struct sockaddr *, socklen_t, void (*)(struct socket *, struct socket *, void *), void *);
struct socket *ret;
if(local)
sk->ignread = 1;
}
-struct socket *netcsconn(struct sockaddr *addr, socklen_t addrlen, int (*func)(struct socket *, int, void *), void *data)
+struct socket *netcsconn(struct sockaddr *addr, socklen_t addrlen, void (*func)(struct socket *, int, void *), void *data)
{
struct socket *sk;
int mode;
if(errno == EINPROGRESS)
{
sk->state = SOCK_SYN;
- if(func != NULL)
- CBREG(sk, socket_conn, func, NULL, data);
+ sk->conncb = func;
+ sk->data = data;
return(sk);
}
putsock(sk);
{
sslen = sizeof(ss);
if((newfd = accept(sk->fd, (struct sockaddr *)&ss, &sslen)) < 0)
- CBCHAINDOCB(sk, socket_err, sk, errno);
+ {
+ if(sk->errcb != NULL)
+ sk->errcb(sk, errno, sk->data);
+ }
newsk = newsock(sk->type);
newsk->fd = newfd;
newsk->family = sk->family;
memcpy(newsk->remote = smalloc(sslen), &ss, sslen);
newsk->remotelen = sslen;
putsock(newsk);
- CBCHAINDOCB(sk, socket_accept, sk, newsk);
+ if(sk->acceptcb != NULL)
+ sk->acceptcb(sk, newsk, sk->data);
}
if(pfds[i].revents & POLLERR)
{
retlen = sizeof(ret);
getsockopt(sk->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen);
- CBCHAINDOCB(sk, socket_err, sk, ret);
+ if(sk->errcb != NULL)
+ sk->errcb(sk, ret, sk->data);
continue;
}
break;
{
retlen = sizeof(ret);
getsockopt(sk->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen);
- CBCHAINDOCB(sk, socket_conn, sk, ret);
+ if(sk->conncb != NULL)
+ sk->conncb(sk, ret, sk->data);
closesock(sk);
continue;
}
if(pfds[i].revents & (POLLIN | POLLOUT))
{
sk->state = SOCK_EST;
- CBCHAINDOCB(sk, socket_conn, sk, 0);
+ if(sk->conncb != NULL)
+ sk->conncb(sk, 0, sk->data);
}
break;
case SOCK_EST:
{
retlen = sizeof(ret);
getsockopt(sk->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen);
- CBCHAINDOCB(sk, socket_err, sk, ret);
+ if(sk->errcb != NULL)
+ sk->errcb(sk, ret, sk->data);
closesock(sk);
continue;
}
}
if(pfds[i].revents & POLLHUP)
{
- CBCHAINDOCB(sk, socket_err, sk, 0);
+ if(sk->errcb != NULL)
+ sk->errcb(sk, 0, sk->data);
closesock(sk);
unlinksock(sk);
continue;
{
int buf;
+ if(sk->family == AF_UNIX)
+ return(0); /* Unix sockets are always perfect. :) */
if(sk->family == AF_INET)
{
switch(tos)
return(-1);
}
*namebuf = memcpy(smalloc(len), &name, len);
- *lenbuf = len;
+ if(lenbuf != NULL)
+ *lenbuf = len;
return(0);
}