X-Git-Url: http://git.dolda2000.com/gitweb/?a=blobdiff_plain;f=daemon%2Fnet.c;h=d90146d97e5ce1f58e9108063909d3e6a1e9b925;hb=9e5f2b29cf819c9f79113bf3ae7edcb484d8ee14;hp=405451848f478934e904ca2c7cbb3eab27d126c5;hpb=5ec60e4217863b0e2d6a86ba3f60f60c68608227;p=doldaconnect.git diff --git a/daemon/net.c b/daemon/net.c index 4054518..d90146d 100644 --- a/daemon/net.c +++ b/daemon/net.c @@ -33,6 +33,7 @@ #include #include #include +#include /* For rebindunix() */ #ifdef HAVE_LINUX_SOCKIOS_H #include #endif @@ -48,17 +49,40 @@ 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} }; @@ -159,6 +183,7 @@ static struct socket *newsock(int type) new->close = 0; new->remote = NULL; new->remotelen = 0; + memset(&new->ucred, 0, sizeof(new->ucred)); switch(type) { case SOCK_STREAM: @@ -174,11 +199,11 @@ static struct socket *newsock(int type) 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) @@ -240,11 +265,6 @@ void putsock(struct socket *sk) 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: @@ -270,8 +290,7 @@ void putsock(struct socket *sk) } break; } - if(sk->fd >= 0) - close(sk->fd); + closesock(sk); if(sk->remote != NULL) free(sk->remote); free(sk); @@ -328,11 +347,38 @@ void *sockgetinbuf(struct socket *sk, size_t *size) return(NULL); } +static void recvcmsg(struct socket *sk, struct msghdr *msg) +{ + struct cmsghdr *cmsg; + struct ucred *cred; + + for(cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) + { + if((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_CREDENTIALS)) + { + if(sk->ucred.pid == 0) + { + cred = (struct ucred *)CMSG_DATA(cmsg); + memcpy(&sk->ucred, cred, sizeof(*cred)); + flog(LOG_INFO, "received Unix creds: pid %i, uid %i, gid %i", cred->pid, cred->uid, cred->gid); + } + } + } +} + 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: @@ -352,25 +398,38 @@ static void sockrecv(struct socket *sk) 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); + */ + bufvec.iov_base = sk->inbuf.s.buf + sk->inbuf.s.datasize; + bufvec.iov_len = inq; + ret = recvmsg(sk->fd, &msg, 0); if(ret < 0) { 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(msg.msg_flags & MSG_CTRUNC) + flog(LOG_DEBUG, "ancillary data was truncated"); + else + recvcmsg(sk, &msg); 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 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 @@ -378,10 +437,21 @@ static void sockrecv(struct socket *sk) 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); @@ -389,10 +459,15 @@ static void sockrecv(struct socket *sk) 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; } + 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 @@ -404,7 +479,8 @@ static void sockrecv(struct socket *sk) 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; @@ -417,7 +493,8 @@ static void sockrecv(struct socket *sk) 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; } } @@ -443,7 +520,8 @@ static void sockflush(struct socket *sk) 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: @@ -454,13 +532,24 @@ static void sockflush(struct socket *sk) 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; @@ -537,6 +626,29 @@ size_t sockqueuesize(struct socket *sk) } /* + * 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 @@ -544,7 +656,7 @@ size_t sockqueuesize(struct socket *sk) * 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; @@ -563,7 +675,7 @@ struct socket *netcslistenlocal(int type, struct sockaddr *name, socklen_t namel 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); @@ -573,12 +685,12 @@ struct socket *netcslistenlocal(int type, struct sockaddr *name, socklen_t namel 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) { @@ -591,13 +703,13 @@ struct socket *netcslisten(int type, struct sockaddr *name, socklen_t namelen, i 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) @@ -668,7 +780,7 @@ void netdgramconn(struct socket *sk, struct sockaddr *addr, socklen_t addrlen) 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; @@ -688,8 +800,8 @@ struct socket *netcsconn(struct sockaddr *addr, socklen_t addrlen, int (*func)(s 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); @@ -699,6 +811,15 @@ struct socket *netcsconn(struct sockaddr *addr, socklen_t addrlen, int (*func)(s return(NULL); } +static void acceptunix(struct socket *sk) +{ + int buf; + + buf = 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)); +} + int pollsocks(int timeout) { int i, num, ret; @@ -756,21 +877,28 @@ int pollsocks(int timeout) { 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; newsk->state = SOCK_EST; memcpy(newsk->remote = smalloc(sslen), &ss, sslen); newsk->remotelen = sslen; + if(ss.ss_family == PF_UNIX) + acceptunix(newsk); + if(sk->acceptcb != NULL) + sk->acceptcb(sk, newsk, sk->data); putsock(newsk); - CBCHAINDOCB(sk, socket_accept, sk, newsk); } 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; @@ -779,14 +907,16 @@ int pollsocks(int timeout) { 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: @@ -794,7 +924,8 @@ int pollsocks(int timeout) { 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; } @@ -816,7 +947,8 @@ int pollsocks(int timeout) } 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; @@ -846,6 +978,8 @@ int socksettos(struct socket *sk, int tos) { int buf; + if(sk->family == AF_UNIX) + return(0); /* Unix sockets are always perfect. :) */ if(sk->family == AF_INET) { switch(tos) @@ -1026,7 +1160,8 @@ int sockgetlocalname(struct socket *sk, struct sockaddr **namebuf, socklen_t *le return(-1); } *namebuf = memcpy(smalloc(len), &name, len); - *lenbuf = len; + if(lenbuf != NULL) + *lenbuf = len; return(0); } @@ -1163,9 +1298,6 @@ int addreq(struct sockaddr *x, struct sockaddr *y) 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; @@ -1179,8 +1311,7 @@ char *formataddress(struct sockaddr *arg, socklen_t arglen) 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;