diff --git a/12-postbox/domain.c b/12-postbox/domain.c
index 8db3cda..e5d0207 100644
--- a/12-postbox/domain.c
+++ b/12-postbox/domain.c
@@ -1,12 +1,55 @@
 int domain_prepare(int epoll_fd) {
     printf("... by socket: echo 2 | nc -U socket\n");
-    return -1;
+    int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    if (sock_fd < 0) die("socket");
+
+    char *sock_name = "socket";
+    struct sockaddr_un server_sockaddr;
+    server_sockaddr.sun_family = AF_UNIX;
+    strcpy(server_sockaddr.sun_path, sock_name);
+
+    int rc = unlink(sock_name);
+    if (rc < 0 && errno != ENOENT) die("unlink/socket");
+
+    rc = bind(sock_fd, (struct sockaddr *) &server_sockaddr, sizeof(server_sockaddr));
+    if (rc == -1) die("bind/socket");
+
+    rc = listen(sock_fd, 10);
+    if (rc < 0) die("listen/socket");
+
+    epoll_add(epoll_fd, sock_fd, EPOLLIN);
+
+    return sock_fd;
 }
 
 
 void domain_accept(int epoll_fd, int sock_fd, int events) {
+    int client_fd = accept(sock_fd, NULL, NULL);
+    epoll_add(epoll_fd, client_fd, EPOLLIN);
 }
 
 
 void domain_recv(int epoll_fd, int sock_fd, int events) {
+    static char buf[128];
+    if (events & EPOLLIN) {
+        int len = recv(sock_fd, buf, sizeof(buf), 0);
+        if (len < 0) die("recvfrom");
+
+        while (len > 0 && buf[len-1] == '\n') len --;
+        buf[len] = 0;
+
+        struct ucred ucred;
+        {
+            socklen_t len = sizeof(struct ucred);
+            int rc = getsockopt(sock_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len);
+            if (rc < 0) die("getsockopt");
+        }
+
+        printf("socket[pid=%d,uid=%d,gid=%d]: %s\n", ucred.pid, ucred.uid, ucred.gid, buf);
+        epoll_del(epoll_fd, sock_fd);
+        close(sock_fd);
+    } else if (events & EPOLLHUP) {
+        epoll_del(epoll_fd, sock_fd);
+        close(sock_fd);
+    }
 }
diff --git a/12-postbox/fifo.c b/12-postbox/fifo.c
index 3947a76..27bb1a9 100644
--- a/12-postbox/fifo.c
+++ b/12-postbox/fifo.c
@@ -1,8 +1,39 @@
+static int open_fifo() {
+    int fd = open("fifo", O_RDONLY|O_NONBLOCK);
+    if (fd < 0) die("open/fifo");
+    return fd;
+}
 
 int fifo_prepare(int epoll_fd) {
     printf("... by fifo:   echo 1 > fifo\n");
-    return -1;
+    int rc = unlink("fifo");
+    if (rc < 0 && errno != ENOENT) die("unlink/fifo");
+    rc = mknod("fifo",  0666 | S_IFIFO, 0);
+    if (rc < 0) die("mknod/fifo");
+
+    int fifo_fd = open_fifo();
+    epoll_add(epoll_fd, fifo_fd, EPOLLIN);
+
+    return fifo_fd;
 }
 
 void fifo_handle(int epoll_fd, int fifo_fd, int events) {
+    static char buf[128];
+
+    if (events & EPOLLIN) {
+        int len = read(fifo_fd, buf, sizeof(buf));
+        if (len < 0) die("read/fifo");
+        if (len == 0)
+            goto close;
+        while (len > 1 && buf[len-1] == '\n') len --;
+        buf[len] = 0;
+        printf("fifo: %s\n", buf);
+    } else if (events & EPOLLHUP) {
+    close:
+        epoll_del(epoll_fd, fifo_fd);
+        close(fifo_fd);
+
+        fifo_fd = open_fifo();
+        epoll_add(epoll_fd, fifo_fd, EPOLLIN);
+    }
 }