Unix-domain socket

Unix-domain socket code is akin to its network counterpart, and they share one readwrite() function. So I only list the caveats during creating Unix-domain socket connection:

(1) Like network socket, Unix-domain socket also supports connection & connection-less services; instead of using IP address + port, Unix-domain should bind to a filesystem pathname. Check unix_bind function:

  1. /*
  2. * unix_bind()
  3. * Returns a unix socket bound to the given path
  4. */
  5. int
  6. unix_bind(char *path, int flags)
  7. {
  8. struct sockaddr_un s_un;
  9. int s, save_errno;
  10. /* Create unix domain socket. */
  11. if ((s = socket(AF_UNIX, flags | (uflag ? SOCK_DGRAM : SOCK_STREAM),
  12. 0)) < 0)
  13. return -1;
  14. memset(&s_un, 0, sizeof(struct sockaddr_un));
  15. s_un.sun_family = AF_UNIX;
  16. if (strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path)) >=
  17. sizeof(s_un.sun_path)) {
  18. close(s);
  19. errno = ENAMETOOLONG;
  20. return -1;
  21. }
  22. if (bind(s, (struct sockaddr *)&s_un, sizeof(s_un)) < 0) {
  23. save_errno = errno;
  24. close(s);
  25. errno = save_errno;
  26. return -1;
  27. }
  28. return s;
  29. }

(2) The biggest surprise is connection-less Unix-domain client also needs to be bound to a pathname:

  1. ......
  2. /* Get name of temporary socket for unix datagram client */
  3. if ((family == AF_UNIX) && uflag && !lflag) {
  4. if (sflag) {
  5. unix_dg_tmp_socket = sflag;
  6. } else {
  7. strlcpy(unix_dg_tmp_socket_buf, "/tmp/nc.XXXXXXXXXX",
  8. UNIX_DG_TMP_SOCKET_SIZE);
  9. if (mktemp(unix_dg_tmp_socket_buf) == NULL)
  10. err(1, "mktemp");
  11. unix_dg_tmp_socket = unix_dg_tmp_socket_buf;
  12. }
  13. }
  14. ......

sflag will be set with -s source option:

  1. ......
  2. char *sflag; /* Source Address */
  3. ......

So if user doesn’t specify a pathname for client, the client will create a temporary socket.

Therefore, the connection-less client also needs to call unix_bind() before connecting server:

  1. /*
  2. * unix_connect()
  3. * Returns a socket connected to a local unix socket. Returns -1 on failure.
  4. */
  5. int
  6. unix_connect(char *path)
  7. {
  8. struct sockaddr_un s_un;
  9. int s, save_errno;
  10. if (uflag) {
  11. if ((s = unix_bind(unix_dg_tmp_socket, SOCK_CLOEXEC)) < 0)
  12. return -1;
  13. } else {
  14. if ((s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0)
  15. return -1;
  16. }
  17. ......
  18. if (connect(s, (struct sockaddr *)&s_un, sizeof(s_un)) < 0) {
  19. ......
  20. }
  21. return s;
  22. }

For the connection-less server part, there is also one more step needed:

  1. ......
  2. if (family == AF_UNIX && uflag) {
  3. if (connect(s, NULL, 0) < 0)
  4. err(1, "connect");
  5. }
  6. ......