Use proper e-mail address format everywhere.
[doldaconnect.git] / daemon / net.c
index 16c7bbb..6af0e0a 100644 (file)
@@ -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 <fredrik@dolda2000.com>
  *  
  *  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
@@ -183,6 +183,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:
@@ -346,11 +348,40 @@ 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:
@@ -370,7 +401,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))
@@ -380,6 +420,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)
@@ -392,6 +436,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
@@ -399,10 +444,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);
@@ -415,6 +471,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
@@ -758,6 +818,24 @@ 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;
@@ -825,9 +903,11 @@ 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)
            {
@@ -939,7 +1019,7 @@ int socksettos(struct socket *sk, int 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);
@@ -1003,7 +1083,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;
@@ -1011,7 +1091,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);