skalibs

Mirror/fork of https://skarnet.org/software/skalibs/
git clone https://ccx.te2000.cz/git/skalibs
Log | Files | Refs | README | LICENSE

unixmessage_receive.c (4074B)


      1 /* ISC license. */
      2 
      3 #include <skalibs/sysdeps.h>
      4 #include <skalibs/nonposix.h>
      5 
      6 #include <errno.h>
      7 #include <sys/socket.h>
      8 #include <sys/uio.h>
      9 
     10 #include <skalibs/uint16.h>
     11 #include <skalibs/uint32.h>
     12 #include <skalibs/cbuffer.h>
     13 #include <skalibs/djbunix.h>
     14 #include <skalibs/allreadwrite.h>
     15 #include <skalibs/stralloc.h>
     16 #include <skalibs/unixmessage.h>
     17 #include <skalibs/posixishard.h>
     18 
     19 union aligner_u
     20 {
     21   struct cmsghdr cmsghdr ;
     22   int i ;
     23 } ;
     24 
     25 static int const awesomeflags =
     26 #ifdef SKALIBS_HASMSGDONTWAIT
     27   MSG_DONTWAIT
     28 #else
     29   0
     30 #endif
     31   |
     32 #ifdef SKALIBS_HASCMSGCLOEXEC
     33   MSG_CMSG_CLOEXEC
     34 #else
     35   0
     36 #endif
     37   ;
     38 
     39 static int unixmessage_receiver_fill (unixmessage_receiver *b)
     40 {
     41   union aligner_u ancilbuf[1 + CMSG_SPACE(b->auxb.a - 1) / sizeof(union aligner_u)] ;
     42   struct iovec iov[2] ;
     43   struct msghdr msghdr =
     44   {
     45     .msg_name = 0,
     46     .msg_namelen = 0,
     47     .msg_iov = iov,
     48     .msg_iovlen = 2,
     49     .msg_flags = 0,
     50     .msg_control = b->fds_ok & 1 ? ancilbuf : 0,
     51     .msg_controllen = b->fds_ok & 1 ? CMSG_SPACE(b->auxb.a - 1) : 0
     52   } ;
     53   ssize_t r = -1 ;
     54   if (cbuffer_isfull(&b->mainb) || ((b->fds_ok & 1) && cbuffer_isfull(&b->auxb)))
     55     return (errno = ENOBUFS, -1) ;
     56   cbuffer_wpeek(&b->mainb, iov) ;
     57   while (r < 0)
     58   {
     59     r = recvmsg(b->fd, &msghdr, awesomeflags) ;
     60     if (!r || (r < 0 && errno != EINTR)) return r ;
     61   }
     62   if (b->fds_ok & 1)
     63   {
     64     struct cmsghdr *c = CMSG_FIRSTHDR(&msghdr) ;
     65     if (c)
     66     {
     67       size_t auxlen ;
     68       if (c->cmsg_level != SOL_SOCKET
     69        || c->cmsg_type != SCM_RIGHTS) return (errno = EPROTO, -1) ;
     70       auxlen = (size_t)(c->cmsg_len - (CMSG_DATA(c) - (unsigned char *)c)) ;
     71       if (auxlen && !(b->fds_ok & 2))
     72       {
     73         size_t i = auxlen / sizeof(int) ;
     74         while (i--) fd_close(((int *)CMSG_DATA(c))[i]) ;
     75         return (errno = EPROTO, -1) ;
     76       }
     77 #ifndef SKALIBS_HASCMSGCLOEXEC
     78       {
     79         size_t i = 0 ;
     80         for (; i < auxlen / sizeof(int) ; i++)
     81           if (coe(((int *)CMSG_DATA(c))[i]) < 0)
     82           {
     83             i++ ;
     84             while (i--) fd_close(((int *)CMSG_DATA(c))[i]) ;
     85             return -1 ;
     86           }
     87       }
     88 #endif
     89       if ((msghdr.msg_flags & MSG_CTRUNC) || cbuffer_put(&b->auxb, (char *)CMSG_DATA(c), auxlen) < auxlen)
     90       {
     91         size_t i = auxlen/sizeof(int) ;
     92         while (i--) fd_close(((int *)CMSG_DATA(c))[i]) ;
     93         return (errno = ENOBUFS, -1) ;
     94       }
     95     }
     96   }
     97   cbuffer_WSEEK(&b->mainb, r) ;
     98   return 1 ;
     99 }
    100 
    101 int unixmessage_receive (unixmessage_receiver *b, unixmessage *m)
    102 {
    103   if (b->maindata.len == b->mainlen && b->auxdata.len == b->auxlen)
    104   {
    105     char pack[6] ;
    106     if (cbuffer_len(&b->mainb) < 6)
    107     {
    108       ssize_t r = sanitize_read(unixmessage_receiver_fill(b)) ;
    109       if (r <= 0) return r ;
    110       if (cbuffer_len(&b->mainb) < 6) return (errno = EWOULDBLOCK, 0) ;
    111     }
    112     cbuffer_get(&b->mainb, pack, 6) ;
    113     uint32_unpack_big(pack, &b->mainlen) ;
    114     if (b->fds_ok & 1) uint16_unpack_big(pack + 4, &b->auxlen) ;
    115     else b->auxlen = 0 ;
    116     b->auxlen *= sizeof(int) ;
    117     if (b->mainlen > UNIXMESSAGE_MAXSIZE
    118      || b->auxlen > ((b->fds_ok & 2) ? UNIXMESSAGE_MAXFDS * sizeof(int) : 0))
    119       return (errno = EPROTO, -1) ;
    120     if (!stralloc_ready(&b->maindata, b->mainlen)
    121      || !stralloc_ready(&b->auxdata, b->auxlen))
    122       return -1 ;
    123     b->maindata.len = 0 ;
    124     b->auxdata.len = 0 ;
    125   }
    126 
    127   for (;;)
    128   {
    129     ssize_t r ;
    130     size_t n = cbuffer_len(&b->mainb) ;
    131     if (n > b->mainlen - b->maindata.len) n = b->mainlen - b->maindata.len ;
    132     b->maindata.len += cbuffer_get(&b->mainb, b->maindata.s + b->maindata.len, n) ;
    133     n = cbuffer_len(&b->auxb) ;
    134     if (n > b->auxlen - b->auxdata.len) n = b->auxlen - b->auxdata.len ;
    135     b->auxdata.len += cbuffer_get(&b->auxb, b->auxdata.s + b->auxdata.len, n) ;
    136     if (b->maindata.len == b->mainlen && b->auxdata.len == b->auxlen) break ;
    137     r = sanitize_read(unixmessage_receiver_fill(b)) ;
    138     if (r <= 0) return r ;
    139   }
    140 
    141   m->s = b->maindata.s ;
    142   m->len = b->maindata.len ;
    143   m->fds = (int *)b->auxdata.s ;
    144   m->nfds = b->auxdata.len / sizeof(int) ;
    145   return 1 ;
    146 }