about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2015-01-06 00:31:40 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2015-01-06 00:31:40 +0000
commitaa081897ac57658482143f29f4b88b1ebbddede3 (patch)
treecc16f9e77185652899ed7ea798b56031c69ece80 /src
parent5b4cf1798bfaf7be1dfaea36614757db80cae23d (diff)
downloadskalibs-aa081897ac57658482143f29f4b88b1ebbddede3.tar.gz
skalibs-aa081897ac57658482143f29f4b88b1ebbddede3.tar.xz
skalibs-aa081897ac57658482143f29f4b88b1ebbddede3.zip
- Bugfixes in unixmessage/skaclient (short writes / fd leakage / DoS) v2.1.0.0
 - ABI change: unixmessage protocol header is now 6 bytes (was 8)
 - API change: skaclient_start(_async) now takes an "options" argument
 - version increase to 2.1.0.0
Diffstat (limited to 'src')
-rw-r--r--src/include/skalibs/skaclient.h52
-rw-r--r--src/include/skalibs/unixmessage.h25
-rw-r--r--src/libunixonacid/skaclient-internal.h4
-rw-r--r--src/libunixonacid/skaclient_start.c4
-rw-r--r--src/libunixonacid/skaclient_start_async.c7
-rw-r--r--src/libunixonacid/skaclient_start_cb.c16
-rw-r--r--src/libunixonacid/skaclient_startf_async.c3
-rw-r--r--src/libunixonacid/unixmessage_put.c3
-rw-r--r--src/libunixonacid/unixmessage_receive.c56
-rw-r--r--src/libunixonacid/unixmessage_receiver_free.c19
-rw-r--r--src/libunixonacid/unixmessage_receiver_init.c1
-rw-r--r--src/libunixonacid/unixmessage_sender_flush.c65
12 files changed, 182 insertions, 73 deletions
diff --git a/src/include/skalibs/skaclient.h b/src/include/skalibs/skaclient.h
index 2875304..5b73852 100644
--- a/src/include/skalibs/skaclient.h
+++ b/src/include/skalibs/skaclient.h
@@ -25,23 +25,13 @@ extern int skaclient_server_init (unixmessage_receiver_t *, unixmessage_sender_t
 
 #define SKACLIENT_KOLBAK_SIZE 64
 #define SKACLIENT_OPTION_WAITPID 0x00000001U
+#define SKACLIENT_OPTION_ASYNC_ACCEPT_FDS 0x00000002U
+#define SKACLIENT_OPTION_SYNC_ACCEPT_FDS 0x00000004U
 
 #define skaclient_buffer_type(bufsn, auxbufsn, bufan, auxbufan, qlen) struct { char bufs[bufsn] ; char auxbufs[auxbufsn] ; char bufa[bufan] ; char auxbufa[auxbufan] ; kolbak_closure_t q[qlen] ; }
 typedef skaclient_buffer_type(UNIXMESSAGE_BUFSIZE, UNIXMESSAGE_AUXBUFSIZE, UNIXMESSAGE_BUFSIZE, UNIXMESSAGE_AUXBUFSIZE, SKACLIENT_KOLBAK_SIZE) skaclient_buffer_t, *skaclient_buffer_t_ref ;
 
 
- /* Callback data for init */
-
-typedef struct skaclient_cbdata_s skaclient_cbdata_t, *skaclient_cbdata_t_ref ;
-struct skaclient_cbdata_s
-{
-  unixmessage_receiver_t *asyncin ;
-  unixmessage_sender_t *asyncout ;
-  char const *after ;
-  unsigned int afterlen ;
-} ;
-
-
  /* User structure */
 
 typedef struct skaclient_s skaclient_t, *skaclient_t_ref ;
@@ -58,23 +48,40 @@ struct skaclient_s
 #define SKACLIENT_ZERO { .syncin = UNIXMESSAGE_RECEIVER_ZERO, .syncout = UNIXMESSAGE_SENDER_ZERO, .kq = KOLBAK_QUEUE_ZERO, .asyncin = UNIXMESSAGE_RECEIVER_ZERO, .asyncout = UNIXMESSAGE_SENDER_ZERO, .pid = 0, .options = 0 }
 extern skaclient_t const skaclient_zero ;
 
+
+ /* Callback data for init */
+
+typedef struct skaclient_cbdata_s skaclient_cbdata_t, *skaclient_cbdata_t_ref ;
+struct skaclient_cbdata_s
+{
+  skaclient_t *a ;
+  char const *after ;
+  unsigned int afterlen ;
+} ;
+
+
+ /* Starting and ending */
+
 extern void skaclient_end (skaclient_t *) ;
 
-extern int skaclient_start_async (skaclient_t *, char *, unsigned int, char *, unsigned int, char *, unsigned int, char *, unsigned int, kolbak_closure_t *, unsigned int, char const *, char const *, unsigned int, char const *, unsigned int, skaclient_cbdata_t *) ;
-#define skaclient_start_async_b(a, sb, path, before, beforelen, after, afterlen, blah) skaclient_start_async(a, (sb)->bufs, sizeof((sb)->bufs), (sb)->auxbufs, sizeof((sb)->auxbufs), (sb)->bufa, sizeof((sb)->bufa), (sb)->auxbufa, sizeof((sb)->auxbufa), (sb)->q, sizeof((sb)->q), path, before, beforelen, after, afterlen, blah)
+extern int skaclient_start_async (skaclient_t *, char *, unsigned int, char *, unsigned int, char *, unsigned int, char *, unsigned int, kolbak_closure_t *, unsigned int, char const *, uint32, char const *, unsigned int, char const *, unsigned int, skaclient_cbdata_t *) ;
+#define skaclient_start_async_b(a, sb, path, options, before, beforelen, after, afterlen, blah) skaclient_start_async(a, (sb)->bufs, sizeof((sb)->bufs), (sb)->auxbufs, sizeof((sb)->auxbufs), (sb)->bufa, sizeof((sb)->bufa), (sb)->auxbufa, sizeof((sb)->auxbufa), (sb)->q, sizeof((sb)->q), path, options, before, beforelen, after, afterlen, blah)
 extern int skaclient_startf_async (skaclient_t *, char *, unsigned int, char *, unsigned int, char *, unsigned int, char *, unsigned int, kolbak_closure_t *, unsigned int, char const *, char const *const *, char const *const *, uint32, char const *, unsigned int, char const *, unsigned int, skaclient_cbdata_t *) ;
 #define skaclient_startf_async_b(a, sb, prog, argv, envp, options, before, beforelen, after, afterlen, blah) skaclient_startf_async(a, (sb)->bufs, sizeof((sb)->bufs), (sb)->auxbufs, sizeof((sb)->auxbufs), (sb)->bufa, sizeof((sb)->bufa), (sb)->auxbufa, sizeof((sb)->auxbufa), (sb)->q, sizeof((sb)->q) / sizeof(kolbak_closure_t), prog, argv, envp, options, before, beforelen, after, afterlen, blah)
 
-extern int skaclient_start (skaclient_t *, char *, unsigned int, char *, unsigned int, char *, unsigned int, char *, unsigned int, kolbak_closure_t *, unsigned int, char const *, char const *, unsigned int, char const *, unsigned int, tain_t const *, tain_t *) ;
-#define skaclient_start_b(a, sb, path, before, beforelen, after, afterlen, deadline, stamp) skaclient_start(a, (sb)->bufs, sizeof((sb)->bufs), (sb)->auxbufs, sizeof((sb)->auxbufs), (sb)->bufa, sizeof((sb)->bufa), (sb)->auxbufa, sizeof((sb)->auxbufa), (sb)->q, sizeof((sb)->q) / sizeof(kolbak_closure_t), path, before, beforelen, after, afterlen, deadline, stamp)
-#define skaclient_start_g(a, bufs, bufsn, auxbufs, auxbufsn, bufa, bufan, auxbufa, auxbufan, q, qlen, path, before, beforelen, after, afterlen, deadline)  skaclient_start(a, bufs, bufsn, auxbufs, auxbufsn, bufa, bufan, auxbufa, auxbufan, q, qlen, path, before, beforelen, after, afterlen, (deadline), &STAMP)
-#define skaclient_start_b_g(a, sb, path, before, beforelen, after, afterlen, deadline) skaclient_start_b(a, sb, path, before, beforelen, after, afterlen, (deadline), &STAMP)
+extern int skaclient_start (skaclient_t *, char *, unsigned int, char *, unsigned int, char *, unsigned int, char *, unsigned int, kolbak_closure_t *, unsigned int, char const *, uint32, char const *, unsigned int, char const *, unsigned int, tain_t const *, tain_t *) ;
+#define skaclient_start_b(a, sb, path, options, before, beforelen, after, afterlen, deadline, stamp) skaclient_start(a, (sb)->bufs, sizeof((sb)->bufs), (sb)->auxbufs, sizeof((sb)->auxbufs), (sb)->bufa, sizeof((sb)->bufa), (sb)->auxbufa, sizeof((sb)->auxbufa), (sb)->q, sizeof((sb)->q) / sizeof(kolbak_closure_t), path, options, before, beforelen, after, afterlen, deadline, stamp)
+#define skaclient_start_g(a, bufs, bufsn, auxbufs, auxbufsn, bufa, bufan, auxbufa, auxbufan, q, qlen, path, options, before, beforelen, after, afterlen, deadline)  skaclient_start(a, bufs, bufsn, auxbufs, auxbufsn, bufa, bufan, auxbufa, auxbufan, q, qlen, path, options, before, beforelen, after, afterlen, (deadline), &STAMP)
+#define skaclient_start_b_g(a, sb, path, options, before, beforelen, after, afterlen, deadline) skaclient_start_b(a, sb, path, options, before, beforelen, after, afterlen, (deadline), &STAMP)
 
 extern int skaclient_startf (skaclient_t *, char *, unsigned int, char *, unsigned int, char *, unsigned int, char *, unsigned int, kolbak_closure_t *, unsigned int, char const *, char const *const *, char const *const *, uint32, char const *, unsigned int, char const *, unsigned int, tain_t const *, tain_t *) ;
 #define skaclient_startf_b(a, sb, prog, argv, envp, options, before, beforelen, after, afterlen, deadline, stamp) skaclient_startf(a, (sb)->bufs, sizeof((sb)->bufs), (sb)->auxbufs, sizeof((sb)->auxbufs), (sb)->bufa, sizeof((sb)->bufa), (sb)->auxbufa, sizeof((sb)->auxbufa), (sb)->q, sizeof((sb)->q) / sizeof(kolbak_closure_t), prog, argv, envp, options, before, beforelen, after, afterlen, deadline, stamp)
 #define skaclient_startf_g(a, bufs, bufsn, auxbufs, auxbufsn, bufa, bufan, auxbufa, auxbufan, q, qlen, prog, argv, envp, options, before, beforelen, after, afterlen, deadline) skaclient_startf(a, bufs, bufsn, auxbufs, auxbufsn, bufa, bufan, auxbufa, auxbufan, q, qlen, prog, argv, envp, options, before, beforelen, after, afterlen, (deadline), &STAMP)
 #define skaclient_startf_b_g(a, sb, prog, argv, envp, options, before, beforelen, after, afterlen, deadline) skaclient_startf_b(a, sb, prog, argv, envp, options, before, beforelen, after, afterlen, (deadline), &STAMP)
 
+
+ /* Writing */
+
 extern int skaclient_putmsg_and_close (skaclient_t *, unixmessage_t const *, unsigned char const *, unixmessage_handler_func_t *, void *) ;
 #define skaclient_putmsg(a, m, cb, result) skaclient_putmsg_and_close(a, m, unixmessage_bits_closenone, cb, result)
 extern int skaclient_putmsgv_and_close (skaclient_t *, unixmessage_v_t const *, unsigned char const *, unixmessage_handler_func_t *, void *) ;
@@ -83,6 +90,9 @@ extern int skaclient_putmsgv_and_close (skaclient_t *, unixmessage_v_t const *,
 extern int skaclient_put (skaclient_t *, char const *, unsigned int, unixmessage_handler_func_t *, void *) ;
 extern int skaclient_putv (skaclient_t *, siovec_t const *, unsigned int, unixmessage_handler_func_t *, void *) ;
 
+
+ /* Writing and flushing */
+
 extern int skaclient_sendmsg_and_close (skaclient_t *, unixmessage_t const *, unsigned char const *, unixmessage_handler_func_t *, void *, tain_t const *, tain_t *) ;
 #define skaclient_sendmsg_and_close_g(a, m, bits, cb, result, deadline) skaclient_sendmsg_and_close(a, m, bits, cb, result, (deadline), &STAMP)
 #define skaclient_sendmsg(a, m, cb, result, deadline, stamp) skaclient_sendmsg_and_close(a, m, unixmessage_bits_closenone, cb, result, deadline, stamp)
@@ -98,6 +108,9 @@ extern int skaclient_send (skaclient_t *, char const *, unsigned int, unixmessag
 extern int skaclient_sendv (skaclient_t *, siovec_t const *, unsigned int, unixmessage_handler_func_t *, void *, tain_t const *, tain_t *) ;
 #define skaclient_sendv_g(a, v, vlen, cb, result, deadline) skaclient_sendv(a, v, vlen, cb, result, (deadline), &STAMP)
 
+
+ /* Helpers for full async */
+
 #define skaclient_sfd(a) unixmessage_receiver_fd(&(a)->syncin)
 #define skaclient_siswritable(a) (!unixmessage_sender_isempty(&(a)->syncout))
 #define skaclient_flush(a) unixmessage_sender_flush(&(a)->syncout)
@@ -117,6 +130,9 @@ extern int skaclient_syncify (skaclient_t *, tain_t const *, tain_t *) ;
 
 extern unixmessage_handler_func_t skaclient_default_cb ;
 
+
+ /* When asyncout is actually used (skabus...) */
+
 #define skaclient_aiswritable(a) (!unixmessage_sender_isempty(&(a)->asyncout))
 #define skaclient_aput_and_close(a, m, bits) unixmessage_put_and_close(&(a)->asyncout, m, bits)
 #define skaclient_aputv_and_close(a, m, bits) unixmessage_putv_and_close(&(a)->asyncout, m, bits)
diff --git a/src/include/skalibs/unixmessage.h b/src/include/skalibs/unixmessage.h
index eaf66c5..3252a50 100644
--- a/src/include/skalibs/unixmessage.h
+++ b/src/include/skalibs/unixmessage.h
@@ -3,6 +3,8 @@
 #ifndef UNIXMESSAGE_H
 #define UNIXMESSAGE_H
 
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
 #include <skalibs/buffer.h>
 #include <skalibs/cbuffer.h>
 #include <skalibs/gccattributes.h>
@@ -39,8 +41,9 @@ struct unixmessage_v_s
 #define UNIXMESSAGE_V_ZERO { .v = 0, .vlen = 0, .fds = 0, .nfds = 0 }
 extern unixmessage_v_t const unixmessage_v_zero ;
 
-#define UNIXMESSAGE_BUFSIZE 2048
+#define UNIXMESSAGE_MAXSIZE (2U << 27)
 #define UNIXMESSAGE_MAXFDS 255
+#define UNIXMESSAGE_BUFSIZE 2048
 #define UNIXMESSAGE_AUXBUFSIZE (sizeof(int) * UNIXMESSAGE_MAXFDS + 1)
 #define UNIXMESSAGE_MAXREADS 32
 
@@ -55,9 +58,10 @@ struct unixmessage_sender_s
   genalloc fds ; /* int */
   genalloc offsets ; /* diuint */
   unsigned int head ;
+  unsigned int shorty ;
 } ;
 #define UNIXMESSAGE_SENDER_ZERO UNIXMESSAGE_SENDER_INIT(-1)
-#define UNIXMESSAGE_SENDER_INIT(s) { .fd = (s), .data = STRALLOC_ZERO, .fds = GENALLOC_ZERO, .offsets = GENALLOC_ZERO, .head = 0 }
+#define UNIXMESSAGE_SENDER_INIT(s) { .fd = (s), .data = STRALLOC_ZERO, .fds = GENALLOC_ZERO, .offsets = GENALLOC_ZERO, .head = 0, .shorty = 0 }
 
 extern unixmessage_sender_t const unixmessage_sender_zero ;
 extern void unixmessage_sender_init (unixmessage_sender_t *, int) ;
@@ -87,21 +91,23 @@ struct unixmessage_receiver_s
   int fd ;
   cbuffer_t mainb ;
   cbuffer_t auxb ;
-  unsigned int mainlen ;
-  unsigned int auxlen ;
   stralloc maindata ;
   stralloc auxdata ;
+  uint32 mainlen ;
+  uint16 auxlen ;
+  unsigned int fds_ok : 2 ;
 } ;
-#define UNIXMESSAGE_RECEIVER_ZERO { .fd = -1, .mainb = CBUFFER_ZERO, .auxb = CBUFFER_ZERO, .mainlen = 0, .auxlen = 0, .maindata = STRALLOC_ZERO, .auxdata = STRALLOC_ZERO }
+#define UNIXMESSAGE_RECEIVER_ZERO { .fd = -1, .mainb = CBUFFER_ZERO, .auxb = CBUFFER_ZERO, .maindata = STRALLOC_ZERO, .auxdata = STRALLOC_ZERO, .mainlen = 0, .auxlen = 0, .fds_ok = 3 }
 #define UNIXMESSAGE_RECEIVER_INIT(d, mains, mainn, auxs, auxn) \
 { \
   .fd = d, \
   .mainb = CBUFFER_INIT(mains, mainn), \
   .auxb = CBUFFER_INIT(auxs, auxn), \
+  .maindata = STRALLOC_ZERO, \
+  .auxdata = STRALLOC_ZERO, \
   .mainlen = 0, \
   .auxlen = 0, \
-  .maindata = STRALLOC_ZERO, \
-  .auxdata = STRALLOC_ZERO \
+  .fds_ok = 3 \
 }
 extern int unixmessage_receiver_init (unixmessage_receiver_t *, int, char *, unsigned int, char *, unsigned int) ;
 extern void unixmessage_receiver_free (unixmessage_receiver_t *) ;
@@ -113,6 +119,10 @@ extern int unixmessage_receive (unixmessage_receiver_t *, unixmessage_t *) ;
 extern int unixmessage_timed_receive (unixmessage_receiver_t *, unixmessage_t *, tain_t const *, tain_t *) ;
 #define unixmessage_timed_receive_g(receiver, msg, deadline) unixmessage_timed_receive(receiver, msg, (deadline), &STAMP)
 
+#define unixmessage_receiver_accept_fds(b) ((b)->fds_ok = 3)
+#define unixmessage_receiver_refuse_fds(b) ((b)->fds_ok = 1)
+#define unixmessage_receiver_ignore_fds(b) ((b)->fds_ok = 0)
+
 typedef int unixmessage_handler_func_t (unixmessage_t const *, void *) ;
 typedef unixmessage_handler_func_t *unixmessage_handler_func_t_ref ;
 
@@ -121,6 +131,7 @@ extern int unixmessage_timed_handle (unixmessage_receiver_t *, unixmessage_handl
 #define unixmessage_timed_handle_g(b, f, p, deadline) unixmessage_timed_handle(b, f, p, (deadline), &STAMP)
 
 
+
  /* Globals */
 
 extern unixmessage_receiver_t unixmessage_receiver_0_ ;
diff --git a/src/libunixonacid/skaclient-internal.h b/src/libunixonacid/skaclient-internal.h
index f0a9bfe..3c73d1f 100644
--- a/src/libunixonacid/skaclient-internal.h
+++ b/src/libunixonacid/skaclient-internal.h
@@ -4,12 +4,10 @@
 #define SKACLIENT_INTERNAL_H
 
 #include <skalibs/kolbak.h>
-#include <skalibs/skaclient.h>
 #include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
 
 extern int skaclient_init (skaclient_t *, int, char *, unsigned int, char *, unsigned int, char *, unsigned int, char *, unsigned int, kolbak_closure_t *, unsigned int, char const *, unsigned int) ;
-extern int skaclient_start_async_th (skaclient_t *, char *, unsigned int, char *, unsigned int, char *, unsigned int, char *, unsigned int, kolbak_closure_t *, unsigned int, char const *, char const *, unsigned int) ;
-extern int skaclient_startf_async_th (skaclient_t *, char *, unsigned int, char *, unsigned int, char *, unsigned int, char *, unsigned int, kolbak_closure_t *, unsigned int, char const *, char const *const *, char const *const *, uint32, char const *, unsigned int) ;
 extern int skaclient_start_cb (unixmessage_t const *, skaclient_cbdata_t *) ;
 
 #endif
diff --git a/src/libunixonacid/skaclient_start.c b/src/libunixonacid/skaclient_start.c
index a7e3e67..85021cb 100644
--- a/src/libunixonacid/skaclient_start.c
+++ b/src/libunixonacid/skaclient_start.c
@@ -1,6 +1,7 @@
 /* ISC license. */
 
 #include <errno.h>
+#include <skalibs/uint32.h>
 #include <skalibs/kolbak.h>
 #include <skalibs/skaclient.h>
 #include <skalibs/tai.h>
@@ -19,6 +20,7 @@ int skaclient_start (
   kolbak_closure_t *q,
   unsigned int qlen,
   char const *path,
+  uint32 options,
   char const *before,
   unsigned int beforelen,
   char const *after,
@@ -29,7 +31,7 @@ int skaclient_start (
   skaclient_cbdata_t blah ;
   unixmessage_t m ;
   register int r ;
-  if (!skaclient_start_async(a, bufss, bufsn, auxbufss, auxbufsn, bufas, bufan, auxbufas, auxbufan, q, qlen, path, before, beforelen, after, afterlen, &blah)) return 0 ;
+  if (!skaclient_start_async(a, bufss, bufsn, auxbufss, auxbufsn, bufas, bufan, auxbufas, auxbufan, q, qlen, path, options, before, beforelen, after, afterlen, &blah)) return 0 ;
   if (!skaclient_timed_flush(a, deadline, stamp))
   {
     register int e = errno ;
diff --git a/src/libunixonacid/skaclient_start_async.c b/src/libunixonacid/skaclient_start_async.c
index 4dbbf1c..79ea5df 100644
--- a/src/libunixonacid/skaclient_start_async.c
+++ b/src/libunixonacid/skaclient_start_async.c
@@ -1,6 +1,7 @@
 /* ISC license. */
 
 #include <errno.h>
+#include <skalibs/uint32.h>
 #include <skalibs/error.h>
 #include <skalibs/kolbak.h>
 #include <skalibs/skaclient.h>
@@ -21,6 +22,7 @@ int skaclient_start_async (
   kolbak_closure_t *q,
   unsigned int qlen,
   char const *path,
+  uint32 options,
   char const *before,
   unsigned int beforelen,
   char const *after,
@@ -38,14 +40,13 @@ int skaclient_start_async (
     return 0 ;
   }
   a->pid = 0 ;
-  a->options = 0 ;
+  a->options = options & ~SKACLIENT_OPTION_WAITPID ;
   if (!kolbak_enqueue(&a->kq, (unixmessage_handler_func_t_ref)&skaclient_start_cb, blah))
   {
     skaclient_end(a) ;
     return 0 ;
   }
-  blah->asyncin = &a->asyncin ;
-  blah->asyncout = &a->asyncout ;
+  blah->a = a ;
   blah->after = after ;
   blah->afterlen = afterlen ;
   return 1 ;
diff --git a/src/libunixonacid/skaclient_start_cb.c b/src/libunixonacid/skaclient_start_cb.c
index 4a82b9b..ff11a21 100644
--- a/src/libunixonacid/skaclient_start_cb.c
+++ b/src/libunixonacid/skaclient_start_cb.c
@@ -3,16 +3,24 @@
 #include <errno.h>
 #include <skalibs/bytestr.h>
 #include <skalibs/error.h>
-#include <skalibs/skaclient.h>
 #include <skalibs/unixmessage.h>
+#include <skalibs/skaclient.h>
 #include "skaclient-internal.h"
 
 int skaclient_start_cb (unixmessage_t const *m, skaclient_cbdata_t *blah)
 {
   if (m->len != blah->afterlen
    || byte_diff(m->s, m->len, blah->after)
-   || m->nfds != 1) return (errno = EPROTO, 0) ;
-  blah->asyncin->fd = m->fds[0] ;
-  blah->asyncout->fd = m->fds[0] ;
+   || m->nfds != 1)
+  {
+    unixmessage_drop(m) ;
+    return (errno = EPROTO, 0) ;
+  }
+  blah->a->asyncin.fd = m->fds[0] ;
+  blah->a->asyncout.fd = m->fds[0] ;
+  if (!(blah->a->options & SKACLIENT_OPTION_ASYNC_ACCEPT_FDS))
+    unixmessage_receiver_refuse_fds(&blah->a->asyncin) ;
+  if (!(blah->a->options & SKACLIENT_OPTION_SYNC_ACCEPT_FDS))
+    unixmessage_receiver_refuse_fds(&blah->a->syncin) ;
   return 1 ;
 }
diff --git a/src/libunixonacid/skaclient_startf_async.c b/src/libunixonacid/skaclient_startf_async.c
index eaeba6c..dd1688e 100644
--- a/src/libunixonacid/skaclient_startf_async.c
+++ b/src/libunixonacid/skaclient_startf_async.c
@@ -53,8 +53,7 @@ int skaclient_startf_async (
     skaclient_end(a) ;
     return 0 ;
   }
-  blah->asyncin = &a->asyncin ;
-  blah->asyncout = &a->asyncout ;
+  blah->a = a ;
   blah->after = after ;
   blah->afterlen = afterlen ;
   return 1 ;
diff --git a/src/libunixonacid/unixmessage_put.c b/src/libunixonacid/unixmessage_put.c
index f6db23b..908bc31 100644
--- a/src/libunixonacid/unixmessage_put.c
+++ b/src/libunixonacid/unixmessage_put.c
@@ -8,6 +8,7 @@
 #include <skalibs/bitarray.h>
 #include <skalibs/bytestr.h>
 #include <skalibs/diuint.h>
+#include <skalibs/error.h>
 #include <skalibs/stralloc.h>
 #include <skalibs/genalloc.h>
 #include <skalibs/siovec.h>
@@ -49,6 +50,8 @@ static inline int copyfds (char *s, int const *fds, unsigned int n, unsigned cha
 static int reserve_and_copy (unixmessage_sender_t *b, unsigned int len, int const *fds, unsigned int nfds, unsigned char const *bits)
 {
   diuint cur = { .left = b->data.len, .right = b->fds.len } ;
+  if (len > UNIXMESSAGE_MAXSIZE || nfds > UNIXMESSAGE_MAXFDS)
+    return (errno = EPROTO, 0) ;
   if (!genalloc_readyplus(diuint, &b->offsets, 1)
    || !genalloc_readyplus(int, &b->fds, nfds)
    || !stralloc_readyplus(&b->data, len))
diff --git a/src/libunixonacid/unixmessage_receive.c b/src/libunixonacid/unixmessage_receive.c
index 21491fa..5fa16c4 100644
--- a/src/libunixonacid/unixmessage_receive.c
+++ b/src/libunixonacid/unixmessage_receive.c
@@ -5,7 +5,8 @@
 #include <errno.h>
 #include <sys/socket.h>
 #include <sys/uio.h>
-#include <skalibs/uint.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
 #include <skalibs/cbuffer.h>
 #include <skalibs/djbunix.h>
 #include <skalibs/error.h>
@@ -45,12 +46,12 @@ static int unixmessage_receiver_fill (unixmessage_receiver_t *b)
     .msg_iov = iov,
     .msg_iovlen = 2,
     .msg_flags = 0,
-    .msg_control = ancilbuf,
-    .msg_controllen = sizeof(ancilbuf)
+    .msg_control = b->fds_ok & 1 ? ancilbuf : 0,
+    .msg_controllen = b->fds_ok & 1 ? sizeof(ancilbuf) : 0
   } ;
   unsigned int auxlen ;
   int r = -1 ;
-  if (cbuffer_isfull(&b->mainb) || cbuffer_isfull(&b->auxb))
+  if (cbuffer_isfull(&b->mainb) || ((b->fds_ok & 1) && cbuffer_isfull(&b->auxb)))
     return (errno = ENOBUFS, -1) ;
   {
     siovec_t v[2] ;
@@ -62,6 +63,7 @@ static int unixmessage_receiver_fill (unixmessage_receiver_t *b)
     r = recvmsg(b->fd, &msghdr, awesomeflags) ;
     if (!r || (r < 0 && errno != EINTR)) return r ;
   }
+  if (b->fds_ok & 1)
   {
     struct cmsghdr *c = CMSG_FIRSTHDR(&msghdr) ;
     if (c)
@@ -69,16 +71,32 @@ static int unixmessage_receiver_fill (unixmessage_receiver_t *b)
       if (c->cmsg_level != SOL_SOCKET
        || c->cmsg_type != SCM_RIGHTS) return (errno = EPROTO, -1) ;
       auxlen = (unsigned int)(c->cmsg_len - (CMSG_DATA(c) - (unsigned char *)c)) ;
+      if (auxlen && !(b->fds_ok & 2))
+      {
+        register unsigned int i = auxlen/sizeof(int) ;
+        while (i--) fd_close(((int *)CMSG_DATA(c))[i]) ;
+        return (errno = EPROTO, -1) ;
+      }
 #ifndef SKALIBS_HASCMSGCLOEXEC
       {
         register unsigned int i = 0 ;
         for (; i < auxlen/sizeof(int) ; i++)
-          if (coe(((int *)CMSG_DATA(c))[i]) < 0) return -1 ;
+          if (coe(((int *)CMSG_DATA(c))[i]) < 0)
+          {
+            int e = errno ;
+            i++ ;
+            while (i--) fd_close(((int *)CMSG_DATA(c))[i]) ;
+            errno = e ;
+            return -1 ;
+          }
       }
 #endif
-      if (msghdr.msg_flags & MSG_CTRUNC) return (errno = EPROTO, -1) ;
-      if (cbuffer_put(&b->auxb, (char *)CMSG_DATA(c), auxlen) < auxlen)
+      if ((msghdr.msg_flags & MSG_CTRUNC) || cbuffer_put(&b->auxb, (char *)CMSG_DATA(c), auxlen) < auxlen)
+      {
+        register unsigned int i = auxlen/sizeof(int) ;
+        while (i--) fd_close(((int *)CMSG_DATA(c))[i]) ;
         return (errno = ENOBUFS, -1) ;
+      }
     }
   }
   cbuffer_WSEEK(&b->mainb, r) ;
@@ -89,21 +107,25 @@ int unixmessage_receive (unixmessage_receiver_t *b, unixmessage_t *m)
 {
   if (b->maindata.len == b->mainlen && b->auxdata.len == b->auxlen)
   {
-    char pack[sizeof(unsigned int) << 1] ;
-    if (cbuffer_len(&b->mainb) < sizeof(unsigned int) << 1)
+    char pack[6] ;
+    if (cbuffer_len(&b->mainb) < 6)
     {
       register int r = sanitize_read(unixmessage_receiver_fill(b)) ;
       if (r <= 0) return r ;
-      if (cbuffer_len(&b->mainb) < sizeof(unsigned int) << 1)
-        return (errno = EWOULDBLOCK, 0) ;
+      if (cbuffer_len(&b->mainb) < 6) return (errno = EWOULDBLOCK, 0) ;
     }
-    cbuffer_get(&b->mainb, pack, sizeof(unsigned int) << 1) ;
-    uint_unpack_big(pack, &b->mainlen) ;
-    uint_unpack_big(pack + sizeof(unsigned int), &b->auxlen) ;
+    cbuffer_get(&b->mainb, pack, 6) ;
+    uint32_unpack_big(pack, &b->mainlen) ;
+    if (b->fds_ok & 1) uint16_unpack_big(pack + 4, &b->auxlen) ;
+    else b->auxlen = 0 ;
     b->auxlen *= sizeof(int) ;
-    if (!stralloc_ready(&b->maindata, b->mainlen)) return -1 ;
+    if (b->mainlen > UNIXMESSAGE_MAXSIZE
+     || b->auxlen > ((b->fds_ok & 2) ? UNIXMESSAGE_MAXFDS * sizeof(int) : 0))
+      return (errno = EPROTO, -1) ;
+    if (!stralloc_ready(&b->maindata, b->mainlen)
+     || !stralloc_ready(&b->auxdata, b->auxlen))
+      return -1 ;
     b->maindata.len = 0 ;
-    if (!stralloc_ready(&b->auxdata, b->auxlen)) return -1 ;
     b->auxdata.len = 0 ;
   }
 
@@ -124,6 +146,6 @@ int unixmessage_receive (unixmessage_receiver_t *b, unixmessage_t *m)
   m->s = b->maindata.s ;
   m->len = b->maindata.len ;
   m->fds = (int *)b->auxdata.s ;
-  m->nfds = b->auxlen / sizeof(int) ;
+  m->nfds = b->auxdata.len / sizeof(int) ;
   return 1 ;
 }
diff --git a/src/libunixonacid/unixmessage_receiver_free.c b/src/libunixonacid/unixmessage_receiver_free.c
index 353797b..74654d0 100644
--- a/src/libunixonacid/unixmessage_receiver_free.c
+++ b/src/libunixonacid/unixmessage_receiver_free.c
@@ -1,12 +1,29 @@
 /* ISC license. */
 
 #include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
 #include <skalibs/unixmessage.h>
 
 void unixmessage_receiver_free (unixmessage_receiver_t *b)
 {
+  register unsigned int h = b->maindata.len ;
   b->fd = -1 ;
   stralloc_free(&b->maindata) ;
+  h = h != b->mainlen || b->auxdata.len != b->auxlen || cbuffer_len(&b->auxb) ;
+  if (h)
+  {
+    register unsigned int n = b->auxdata.len / sizeof(int) ;
+    while (n--) fd_close(((int *)b->auxdata.s)[n]) ;
+  }
   stralloc_free(&b->auxdata) ;
-  b->mainlen = b->auxlen = 0 ;
+  if (h)
+  {
+    register unsigned int n = cbuffer_len(&b->auxb) / sizeof(int) ;
+    if (n)
+    {
+      int fds[n] ;
+      cbuffer_get(&b->auxb, (char *)fds, n * sizeof(int)) ;
+      while (n--) fd_close(fds[n]) ;
+    }
+  }
 }
diff --git a/src/libunixonacid/unixmessage_receiver_init.c b/src/libunixonacid/unixmessage_receiver_init.c
index 5f702cf..430ccac 100644
--- a/src/libunixonacid/unixmessage_receiver_init.c
+++ b/src/libunixonacid/unixmessage_receiver_init.c
@@ -13,5 +13,6 @@ int unixmessage_receiver_init (unixmessage_receiver_t *b, int fd, char *mainbuf,
   b->mainlen = b->auxlen = 0 ;
   b->maindata = stralloc_zero ;
   b->auxdata = stralloc_zero ;
+  b->fds_ok = 3 ;
   return 1 ;
 }
diff --git a/src/libunixonacid/unixmessage_sender_flush.c b/src/libunixonacid/unixmessage_sender_flush.c
index bd4c704..bea3585 100644
--- a/src/libunixonacid/unixmessage_sender_flush.c
+++ b/src/libunixonacid/unixmessage_sender_flush.c
@@ -6,9 +6,10 @@
 #include <sys/uio.h>
 #include <unistd.h>
 #include <errno.h>
-#include <skalibs/uint.h>
+#include <skalibs/uint16.h>
+#include <skalibs/uint32.h>
 #include <skalibs/diuint.h>
-#include <skalibs/stralloc.h>
+#include <skalibs/allreadwrite.h>
 #include <skalibs/genalloc.h>
 #include <skalibs/djbunix.h>
 #include <skalibs/unixmessage.h>
@@ -18,21 +19,51 @@
 #define MSG_NOSIGNAL 0
 #endif
 
+ /*
+    XXX: sendmsg/recvmsg is badly, badly specified.
+    XXX: We assume ancillary data is attached to the first byte.
+ */
+
 int unixmessage_sender_flush (unixmessage_sender_t *b)
 {
-  diuint last = { .left = b->data.len, .right = genalloc_len(int, &b->fds) } ;
+  diuint last = { .left = (uint32)b->data.len, .right = genalloc_len(int, &b->fds) } ;
   diuint *offsets = genalloc_s(diuint, &b->offsets) ;
   unsigned int n = genalloc_len(diuint, &b->offsets) ;
-  unsigned int oldhead = b->head ;
+  register int r ;
+
+  if (b->shorty) /* we had a short write, gotta send the remainder first */
+  {
+    diuint *next = b->head+1 < n ? offsets + b->head+1 : &last ;
+    unsigned int len = next->left - offsets[b->head].left ;
+    if (b->shorty <= len)
+      r = fd_write(b->fd, b->data.s + offsets[b->head].left + (len - b->shorty), b->shorty) ;
+    else
+    {
+      unsigned int nfds = next->right - offsets[b->head].right ;
+      char pack[6] ;
+      struct iovec v[2] =
+      {
+        { .iov_base = pack + 6 - (b->shorty - len), .iov_len = b->shorty - len },
+        { .iov_base = b->data.s + offsets[b->head].left, .iov_len = len }
+      } ;
+      uint32_pack_big(pack, (uint32)len) ;
+      uint16_pack_big(pack + 4, (uint16)nfds) ;
+      r = fd_writev(b->fd, v, 2) ;
+    }
+    if (r <= 0) return 0 ;
+    b->shorty -= r ;
+    if (b->shorty) return (errno = EWOULDBLOCK, 0) ;
+  }
+
   for (; b->head < n ; b->head++)
   {
     diuint *next = b->head+1 < n ? offsets + b->head+1 : &last ;
     unsigned int len = next->left - offsets[b->head].left ;
     unsigned int nfds = next->right - offsets[b->head].right ;
-    char pack[sizeof(unsigned int) << 1] ;
+    char pack[6] ;
     struct iovec v[2] =
     {
-      { .iov_base = pack, .iov_len = sizeof(unsigned int) << 1 },
+      { .iov_base = pack, .iov_len = 6 },
       { .iov_base = b->data.s + offsets[b->head].left, .iov_len = len }
     } ;
     char ancilbuf[CMSG_SPACE(nfds * sizeof(int))] ;
@@ -45,8 +76,8 @@ int unixmessage_sender_flush (unixmessage_sender_t *b)
       .msg_control = nfds ? ancilbuf : 0,
       .msg_controllen = nfds ? sizeof(ancilbuf) : 0
     } ;
-    uint_pack_big(pack, len) ;
-    uint_pack_big(pack + sizeof(unsigned int), nfds) ;
+    uint32_pack_big(pack, (uint32)len) ;
+    uint16_pack_big(pack + 4, (uint16)nfds) ;
     if (nfds)
     {
       struct cmsghdr *cp = CMSG_FIRSTHDR(&hdr) ;
@@ -60,14 +91,9 @@ int unixmessage_sender_flush (unixmessage_sender_t *b)
         ((int *)CMSG_DATA(cp))[i] = fd < 0 ? -(fd+1) : fd ;
       }
     }
-    for (;;)
-    {
-      register int r = sendmsg(b->fd, &hdr, MSG_NOSIGNAL) ;
-      if (r == -1 && errno == EINTR) continue ;
-      if (r < (int)(len + (sizeof(unsigned int) << 1)))
-        return -(int)(b->head-oldhead)-1 ;
-      break ;
-    }
+    do r = sendmsg(b->fd, &hdr, MSG_NOSIGNAL) ;
+    while (r < 0 && errno == EINTR) ;
+    if (r <= 0) return 0 ;
 #ifndef SKALIBS_HASANCILAUTOCLOSE
     if (nfds)
     {
@@ -79,10 +105,15 @@ int unixmessage_sender_flush (unixmessage_sender_t *b)
       }
     }
 #endif
+    if ((unsigned int)r < 6 + len)
+    {
+      b->shorty = 6 + len - r ;
+      return (errno = EWOULDBLOCK, 0) ;
+    }
   }
   b->data.len = 0 ;
   genalloc_setlen(int, &b->fds, 0) ;
   genalloc_setlen(diuint, &b->offsets, 0) ;
   b->head = 0 ;
-  return (int)(n - oldhead) ;
+  return 1 ;
 }