add support for unix socket transport

In addition to SSH and TLS transport, add the support for UNIX
socket. Despite it's not in NETCONF standard, using a UNIX socket has
several advantages:

- no need of password, keys, or certificate. The connection is allowed
  for a local user authenticated on the system, and having the proper
  permissions to connect to the netopeer socket.

- the connection to netopeer is possible even if the firewall is
  enabled. This is an advantage when configuring a firewall equipment
  through netconf.

- the connection to netopeer is possible from another netns.

Signed-off-by: Olivier Matz <olivier.matz@6wind.com>
diff --git a/src/io.c b/src/io.c
index ada8020..9c4fa9f 100644
--- a/src/io.c
+++ b/src/io.c
@@ -75,6 +75,7 @@
 {
     size_t readd = 0;
     ssize_t r = -1;
+    int fd;
     struct timespec ts_cur, ts_inact_timeout;
 
     assert(session);
@@ -96,22 +97,24 @@
             return 0;
 
         case NC_TI_FD:
+        case NC_TI_UNIX:
+            fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock;
             /* read via standard file descriptor */
-            r = read(session->ti.fd.in, buf + readd, count - readd);
+            r = read(fd, buf + readd, count - readd);
             if (r < 0) {
                 if ((errno == EAGAIN) || (errno == EINTR)) {
                     r = 0;
                     break;
                 } else {
                     ERR("Session %u: reading from file descriptor (%d) failed (%s).",
-                        session->id, session->ti.fd.in, strerror(errno));
+                        session->id, fd, strerror(errno));
                     session->status = NC_STATUS_INVALID;
                     session->term_reason = NC_SESSION_TERM_OTHER;
                     return -1;
                 }
             } else if (r == 0) {
                 ERR("Session %u: communication file descriptor (%d) unexpectedly closed.",
-                    session->id, session->ti.fd.in);
+                    session->id, fd);
                 session->status = NC_STATUS_INVALID;
                 session->term_reason = NC_SESSION_TERM_DROPPED;
                 return -1;
@@ -552,9 +555,11 @@
 #endif
         /* fallthrough */
     case NC_TI_FD:
-        if (session->ti_type == NC_TI_FD) {
+    case NC_TI_UNIX:
+        if (session->ti_type == NC_TI_FD)
             fds.fd = session->ti.fd.in;
-        }
+        else if (session->ti_type == NC_TI_UNIX)
+            fds.fd = session->ti.unixsock.sock;
 
         fds.events = POLLIN;
         fds.revents = 0;
@@ -649,6 +654,9 @@
     case NC_TI_FD:
         fds.fd = session->ti.fd.in;
         break;
+    case NC_TI_UNIX:
+        fds.fd = session->ti.unixsock.sock;
+        break;
 #ifdef NC_ENABLED_SSH
     case NC_TI_LIBSSH:
         return ssh_is_connected(session->ti.libssh.session);
@@ -692,7 +700,7 @@
 static int
 nc_write(struct nc_session *session, const void *buf, size_t count)
 {
-    int c;
+    int c, fd;
     size_t written = 0;
 #ifdef NC_ENABLED_TLS
     unsigned long e;
@@ -715,8 +723,12 @@
     do {
         switch (session->ti_type) {
         case NC_TI_FD:
-            c = write(session->ti.fd.out, (char *)(buf + written), count - written);
-            if (c < 0) {
+        case NC_TI_UNIX:
+            fd = session->ti_type == NC_TI_FD ? session->ti.fd.out : session->ti.unixsock.sock;
+            c = write(fd, (char *)(buf + written), count - written);
+            if (c < 0 && errno == EAGAIN) {
+                c = 0;
+            } else if (c < 0) {
                 ERR("Session %u: socket error (%s).", session->id, strerror(errno));
                 return -1;
             }