X-Git-Url: http://dolda2000.com/gitweb/?a=blobdiff_plain;f=daemon%2Fnet.c;h=5960a552611600c31a74e14937f08b7e1b58135c;hb=1315d02ac996285e943de172ccd6c420633eeeec;hp=1eeaff15742b70eb3c2135790137db77f5b33d6e;hpb=347d6d76f535256863bcf8a13b3b06a859bf27d2;p=doldaconnect.git diff --git a/daemon/net.c b/daemon/net.c index 1eeaff1..5960a55 100644 --- a/daemon/net.c +++ b/daemon/net.c @@ -33,7 +33,6 @@ #include #include #include -#include #ifdef HAVE_LINUX_SOCKIOS_H #include #endif @@ -55,6 +54,12 @@ static struct configvar myvars[] = /* Only for direct mode */ {CONF_VAR_IPV4, "visibleipv4", {.ipv4 = {0}}}, {CONF_VAR_STRING, "publicif", {.str = L""}}, + /* Diffserv should be supported on IPv4, too, but I don't know the + * API to do that. */ + {CONF_VAR_INT, "diffserv-mincost", {.num = 0}}, + {CONF_VAR_INT, "diffserv-maxrel", {.num = 0}}, + {CONF_VAR_INT, "diffserv-maxtp", {.num = 0}}, + {CONF_VAR_INT, "diffserv-mindelay", {.num = 0}}, {CONF_VAR_END} }; @@ -84,11 +89,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); @@ -102,32 +109,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) { @@ -138,8 +140,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) @@ -270,6 +271,23 @@ void putsock(struct socket *sk) } } +void sockpushdata(struct socket *sk, void *buf, size_t size) +{ + switch(sk->type) + { + case SOCK_STREAM: + sizebuf(&sk->inbuf.s.buf, &sk->inbuf.s.bufsize, sk->inbuf.s.datasize + size, 1, 1); + memmove(sk->inbuf.s.buf + size, sk->inbuf.s.buf, sk->inbuf.s.datasize); + memcpy(sk->inbuf.s.buf, buf, size); + sk->inbuf.s.datasize += size; + break; + case SOCK_DGRAM: + /* XXX */ + break; + } + return; +} + void *sockgetinbuf(struct socket *sk, size_t *size) { void *buf; @@ -519,50 +537,6 @@ 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) -{ - struct socket *sk; - int intbuf; - - 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); -} - /* * The difference between netcslisten() and netcslistenlocal() is that * netcslistenlocal() always listens on the local host, instead of @@ -574,6 +548,7 @@ struct socket *netcslisten(int type, struct sockaddr *name, socklen_t namelen, v 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; /* I don't know if this is actually correct (it probably isn't), * but since, at on least Linux systems, PF_* are specifically @@ -584,6 +559,11 @@ struct socket *netcslistenlocal(int type, struct sockaddr *name, socklen_t namel 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); @@ -599,6 +579,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; @@ -831,16 +853,72 @@ int pollsocks(int timeout) int socksettos(struct socket *sk, int tos) { + int buf; + if(sk->family == AF_INET) { - if(setsockopt(sk->fd, SOL_IP, IP_TOS, &tos, sizeof(tos)) < 0) + switch(tos) + { + case 0: + buf = 0; + break; + case SOCK_TOS_MINCOST: + buf = 0x02; + break; + case SOCK_TOS_MAXREL: + buf = 0x04; + break; + case SOCK_TOS_MAXTP: + buf = 0x08; + break; + case SOCK_TOS_MINDELAY: + 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) { flog(LOG_WARNING, "could not set sock TOS to %i: %s", tos, strerror(errno)); return(-1); } return(0); } - /* XXX: How does the IPv6 traffic class work? */ + if(sk->family == AF_INET6) + { + switch(tos) + { + case 0: + buf = 0; + case SOCK_TOS_MINCOST: + buf = confgetint("net", "diffserv-mincost"); + break; + case SOCK_TOS_MAXREL: + buf = confgetint("net", "diffserv-maxrel"); + break; + case SOCK_TOS_MAXTP: + buf = confgetint("net", "diffserv-maxtp"); + break; + case SOCK_TOS_MINDELAY: + buf = confgetint("net", "diffserv-mindelay"); + break; + default: + flog(LOG_WARNING, "attempted to set unknown TOS value %i to IPv4 sock", tos); + return(-1); + } + /* + On Linux, the API IPv6 flow label management doesn't seem to + be entirely complete, so I guess this will have to wait. + + if(setsockopt(...) < 0) + { + flog(LOG_WARNING, "could not set sock traffic class to %i: %s", tos, strerror(errno)); + return(-1); + } + */ + return(0); + } flog(LOG_WARNING, "could not set TOS on sock of family %i", sk->family); return(1); } @@ -953,7 +1031,7 @@ 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); @@ -963,11 +1041,9 @@ int sockgetlocalname(struct socket *sk, struct sockaddr **namebuf, socklen_t *le int sockgetremotename(struct socket *sk, struct sockaddr **namebuf, socklen_t *lenbuf) { + int ret; socklen_t len; - struct sockaddr_storage name; - struct sockaddr_in *ipv4; - struct sockaddr *pname; - socklen_t pnamelen; + struct sockaddr *name; switch(confgetint("net", "mode")) { @@ -975,26 +1051,25 @@ int sockgetremotename(struct socket *sk, struct sockaddr **namebuf, socklen_t *l *namebuf = NULL; if((sk->state == SOCK_STL) || (sk->fd < 0)) return(-1); - len = sizeof(name); - if(getsockname(sk->fd, (struct sockaddr *)&name, &len) < 0) + if((ret = getpublicaddr(sk->family, &name, &len)) < 0) { - flog(LOG_ERR, "BUG: alive socket with dead fd in sockgetremotename"); + flog(LOG_ERR, "could not get public address: %s", strerror(errno)); return(-1); } - if(name.ss_family == AF_INET) + if(ret == 0) { - 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; + return(0); } - *namebuf = memcpy(smalloc(len), &name, len); - *lenbuf = len; - return(0); + if(!sockgetlocalname(sk, &name, &len)) + { + *namebuf = name; + *lenbuf = len; + return(0); + } + flog(LOG_ERR, "could not get remotely accessible name by any means"); + return(-1); case 1: errno = EOPNOTSUPP; return(-1); @@ -1005,6 +1080,40 @@ int sockgetremotename(struct socket *sk, struct sockaddr **namebuf, socklen_t *l } } +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; + 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; + } + return(1); +} + char *formataddress(struct sockaddr *arg, socklen_t arglen) { struct sockaddr_un *UNIX; /* Some wise guy has #defined unix with @@ -1037,7 +1146,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: