X-Git-Url: http://dolda2000.com/gitweb/?p=doldaconnect.git;a=blobdiff_plain;f=daemon%2Fnet.c;h=e7c63d1733042770598fb0de69a54485facf5094;hp=d98f6205ac7157367c8942afc4d9e6a25d90ea0a;hb=f966ec569e6f045426492efda0f1140ffa7b582b;hpb=18c1ae1d1b26cc4170eb19204bf2109fb7befe9e diff --git a/daemon/net.c b/daemon/net.c index d98f620..e7c63d1 100644 --- a/daemon/net.c +++ b/daemon/net.c @@ -1,6 +1,6 @@ /* * Dolda Connect - Modular multiuser Direct Connect-style client - * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com) + * Copyright (C) 2004 Fredrik Tolf * * 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 @@ -28,12 +28,12 @@ #include #include #include -#include +#include #include #include #include #include -#include +#include /* For rebindunix() */ #ifdef HAVE_LINUX_SOCKIOS_H #include #endif @@ -49,18 +49,44 @@ 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}}, + /** If enabled, the IP TOS interface will be used to set Diffserv + * codepoints on IPv4 sockets, by shifting the DSCP value to bits + * to the left (across the ECN bits). This may only work on + * Linux. */ + {CONF_VAR_BOOL, "dscp-tos", {.num = 0}}, {CONF_VAR_END} }; @@ -90,11 +116,13 @@ int getpublicaddr(int af, struct sockaddr **addr, socklen_t *lenbuf) *lenbuf = sizeof(*ipv4); return(0); } - if((pif = icwcstombs(confgetstr("net", "publicif"), NULL)) == NULL) + if((pif = icswcstombs(confgetstr("net", "publicif"), NULL, NULL)) == NULL) { 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); @@ -108,32 +136,27 @@ int getpublicaddr(int af, struct sockaddr **addr, socklen_t *lenbuf) 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); - 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); if(ipv4 != NULL) { @@ -144,8 +167,7 @@ int getpublicaddr(int af, struct sockaddr **addr, socklen_t *lenbuf) errno = ENETDOWN; return(-1); } - errno = EPFNOSUPPORT; - return(-1); + return(1); } static struct socket *newsock(int type) @@ -153,6 +175,7 @@ static struct socket *newsock(int type) struct socket *new; new = smalloc(sizeof(*new)); + memset(new, 0, sizeof(*new)); new->refcount = 2; new->fd = -1; new->isrealsocket = 1; @@ -164,6 +187,8 @@ 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: @@ -258,18 +283,19 @@ void putsock(struct socket *sk) { 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); @@ -326,23 +352,57 @@ void *sockgetinbuf(struct socket *sk, size_t *size) 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 defined(HAVE_LINUX_SOCKIOS_H) && defined(SIOCINQ) /* SIOCINQ is Linux-specific AFAIK, but I really have no idea * how to read the inqueue size on other OSs */ - if(ioctl(sk->fd, SIOCINQ, &inq)) - { - /* I don't really know what could go wrong here, so let's - * assume it's transient. */ - flog(LOG_WARNING, "SIOCINQ return %s on socket %i, falling back to 2048 bytes", strerror(errno), sk->fd); - inq = 2048; + if(sk->isrealsocket) { + if(ioctl(sk->fd, SIOCINQ, &inq)) + { + /* I don't really know what could go wrong here, so let's + * assume it's transient. */ + flog(LOG_WARNING, "SIOCINQ return %s on socket %i, falling back to 2048 bytes", strerror(errno), sk->fd); + inq = 2048; + } + } else { + /* There are perils when trying to use SIOCINQ on files >2GiB... */ + inq = 65536; } #else inq = 2048; @@ -350,7 +410,16 @@ 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); + 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)) @@ -360,6 +429,10 @@ static void sockrecv(struct socket *sk) 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) @@ -372,6 +445,7 @@ static void sockrecv(struct socket *sk) 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 @@ -379,10 +453,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); @@ -395,6 +480,10 @@ static void sockrecv(struct socket *sk) 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 @@ -440,8 +529,12 @@ static void sockflush(struct socket *sk) ret = write(sk->fd, sk->outbuf.s.buf, sk->outbuf.s.datasize); if(ret < 0) { - /* For now, assume transient error, since - * the socket is polled for errors */ + if((errno != EINTR) && (errno != EAGAIN)) + { + if(sk->errcb != NULL) + sk->errcb(sk, errno, sk->data); + closesock(sk); + } break; } if(ret > 0) @@ -467,6 +560,16 @@ static void sockflush(struct socket *sk) void closesock(struct socket *sk) { + struct sockaddr_un *un; + + if((sk->family == AF_UNIX) && !sockgetlocalname(sk, (struct sockaddr **)(void *)&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; @@ -542,48 +645,27 @@ size_t sockqueuesize(struct socket *sk) 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); } /* @@ -613,7 +695,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); @@ -628,6 +710,48 @@ struct socket *netcslistenlocal(int type, struct sockaddr *name, socklen_t namel 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; @@ -707,62 +831,75 @@ struct socket *netcsconn(struct sockaddr *addr, socklen_t addrlen, void (*func)( 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 newfd; - struct pollfd *pfds; + int ret, fd; + socklen_t retlen; + int newfd, maxfd; + fd_set rfds, wfds, efds; struct socket *sk, *next, *newsk; struct sockaddr_storage ss; socklen_t sslen; + struct timeval tv; - pfds = smalloc(sizeof(*pfds) * (num = numsocks)); - for(i = 0, sk = sockets; i < num; sk = sk->next) + FD_ZERO(&rfds); + FD_ZERO(&wfds); + FD_ZERO(&efds); + for(maxfd = 0, sk = sockets; sk != NULL; sk = sk->next) { - if(sk->state == SOCK_STL) - { - num--; + if((sk->state == SOCK_STL) || (sk->fd < 0)) continue; - } - pfds[i].fd = sk->fd; - pfds[i].events = 0; if(!sk->ignread) - pfds[i].events |= POLLIN; + FD_SET(sk->fd, &rfds); if((sk->state == SOCK_SYN) || (sockqueuesize(sk) > 0)) - pfds[i].events |= POLLOUT; - pfds[i].revents = 0; - i++; + FD_SET(sk->fd, &wfds); + FD_SET(sk->fd, &efds); + if(sk->fd > maxfd) + maxfd = sk->fd; } - ret = poll(pfds, num, timeout); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + ret = select(maxfd + 1, &rfds, &wfds, &efds, (timeout < 0)?NULL:&tv); if(ret < 0) { if(errno != EINTR) { - flog(LOG_CRIT, "pollsocks: poll errored out: %s", strerror(errno)); + flog(LOG_CRIT, "pollsocks: select errored out: %s", strerror(errno)); /* To avoid CPU hogging in case it's bad, which it * probably is. */ sleep(1); } - free(pfds); return(1); } for(sk = sockets; sk != NULL; sk = next) { next = sk->next; - for(i = 0; i < num; i++) - { - if(pfds[i].fd == sk->fd) - break; - } - if(i == num) - continue; + fd = sk->fd; switch(sk->state) { case SOCK_LST: - if(pfds[i].revents & POLLIN) + if(FD_ISSET(fd, &rfds)) { sslen = sizeof(ss); - if((newfd = accept(sk->fd, (struct sockaddr *)&ss, &sslen)) < 0) + if((newfd = accept(fd, (struct sockaddr *)&ss, &sslen)) < 0) { if(sk->errcb != NULL) sk->errcb(sk, errno, sk->data); @@ -773,30 +910,32 @@ int pollsocks(int timeout) 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) + if(FD_ISSET(fd, &efds)) { retlen = sizeof(ret); - getsockopt(sk->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen); + getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &retlen); if(sk->errcb != NULL) sk->errcb(sk, ret, sk->data); continue; } break; case SOCK_SYN: - if(pfds[i].revents & POLLERR) + if(FD_ISSET(fd, &efds)) { retlen = sizeof(ret); - getsockopt(sk->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen); + getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &retlen); if(sk->conncb != NULL) sk->conncb(sk, ret, sk->data); closesock(sk); continue; } - if(pfds[i].revents & (POLLIN | POLLOUT)) + if(FD_ISSET(fd, &rfds) || FD_ISSET(fd, &wfds)) { sk->state = SOCK_EST; if(sk->conncb != NULL) @@ -804,41 +943,25 @@ int pollsocks(int timeout) } break; case SOCK_EST: - if(pfds[i].revents & POLLERR) + if(FD_ISSET(fd, &efds)) { retlen = sizeof(ret); - getsockopt(sk->fd, SOL_SOCKET, SO_ERROR, &ret, &retlen); + getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &retlen); if(sk->errcb != NULL) sk->errcb(sk, ret, sk->data); closesock(sk); continue; } - if(pfds[i].revents & POLLIN) + if(FD_ISSET(fd, &rfds)) sockrecv(sk); - if(pfds[i].revents & POLLOUT) + if(FD_ISSET(fd, &wfds)) { if(sockqueuesize(sk) > 0) sockflush(sk); } break; } - if(pfds[i].revents & POLLNVAL) - { - flog(LOG_CRIT, "BUG: stale socket struct on fd %i", sk->fd); - sk->state = SOCK_STL; - unlinksock(sk); - continue; - } - if(pfds[i].revents & POLLHUP) - { - if(sk->errcb != NULL) - sk->errcb(sk, 0, sk->data); - closesock(sk); - unlinksock(sk); - continue; - } } - free(pfds); for(sk = sockets; sk != NULL; sk = next) { next = sk->next; @@ -861,28 +984,47 @@ int pollsocks(int timeout) int socksettos(struct socket *sk, int tos) { int buf; + int dscp2tos; + if(sk->family == AF_UNIX) + return(0); /* Unix sockets are always perfect. :) */ if(sk->family == AF_INET) { + dscp2tos = confgetint("net", "dscp-tos"); switch(tos) { + case 0: + buf = 0; + break; case SOCK_TOS_MINCOST: - buf = 0x02; + if(dscp2tos) + buf = confgetint("net", "diffserv-mincost") << 2; + else + buf = 0x02; break; case SOCK_TOS_MAXREL: - buf = 0x04; + if(dscp2tos) + buf = confgetint("net", "diffserv-maxrel") << 2; + else + buf = 0x04; break; case SOCK_TOS_MAXTP: - buf = 0x08; + if(dscp2tos) + buf = confgetint("net", "diffserv-maxtp") << 2; + else + buf = 0x08; break; case SOCK_TOS_MINDELAY: - buf = 0x10; + if(dscp2tos) + buf = confgetint("net", "diffserv-mindelay") << 2; + else + buf = 0x10; break; default: 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); @@ -893,6 +1035,8 @@ int socksettos(struct socket *sk, int tos) { switch(tos) { + case 0: + buf = 0; case SOCK_TOS_MINCOST: buf = confgetint("net", "diffserv-mincost"); break; @@ -944,7 +1088,7 @@ static void resolvecb(pid_t pid, int status, struct resolvedata *data) { 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; @@ -952,7 +1096,7 @@ static void resolvecb(pid_t pid, int status, struct resolvedata *data) data->callback((struct sockaddr *)ipv4, sizeof(*ipv4), data->data); } } else { - errno = ENONET; + errno = ENOENT; data->callback(NULL, 0, data->data); } close(data->fd); @@ -1033,48 +1177,77 @@ int sockgetlocalname(struct socket *sk, struct sockaddr **namebuf, socklen_t *le 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); @@ -1085,11 +1258,67 @@ int sockgetremotename(struct socket *sk, struct sockaddr **namebuf, socklen_t *l } } +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; @@ -1103,8 +1332,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; @@ -1117,7 +1345,7 @@ char *formataddress(struct sockaddr *arg, socklen_t arglen) 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)); + ret = sprintf2("[%s]:%i", buf, (int)ntohs(ipv6->sin6_port)); break; #endif default: