Is there any purpose to `bind()` unix domain socket client processes?
Asked Answered
E

5

8

When using AF_UNIX (unix domain sockets), is there any application to calling bind() in a process that never calls listen()?

In my systems programming lecture and lab, we are instructed to callbind() on a unix domain socket client processes. Is there any documented, undocumented, or practical application to calling bind on a client-only unix domain socket process? From my understanding bind() creates the special socket file, which is a responsibility a server process would undertake. Is this correct?

Here is an example code based off of what concepts discussed in class:

server.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>

int main() {
    int s0, s1;
    struct sockaddr sa0 = {AF_UNIX, "a"};

    s0 = socket(AF_UNIX, SOCK_STREAM, 0);

    bind(s0, &sa0, sizeof(sa0) + sizeof("a"));
    listen(s0, 1);
    for (;;) {
        s1 = accept(s0, NULL, NULL);
        puts("connected!");
        for (;;) {
            char text[1];
            ssize_t n = read(s1, &text, sizeof(text));
            if (n < 1) {
                break;
            }
            putchar(text[0]);
        }
        close(s1);
    }
    close(s0);
    unlink("a");
    return 0;
}

client.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>

int main() {
    int s0;
    struct sockaddr sa0 = {AF_UNIX, "b"};
    struct sockaddr sa1 = {AF_UNIX, "a"};
    socklen_t sa1_len;

    s0 = socket(AF_UNIX, SOCK_STREAM, 0);
    bind(s0, &sa0, sizeof(sa0) + sizeof("b")); // What does this do??
    connect(s0, &sa1, sizeof(sa1) + sizeof("b"));
    for (;;) {
        int c = fgetc(stdin);
        if (c == EOF) {
            break;
        }
        write(s0, &c, sizeof(c));
    }
    close(s0);
    unlink("b");
    return 0;
}
Eirene answered 7/12, 2017 at 20:40 Comment(3)
I don't believe the bind call in the client does anything useful, but I could have forgotten something, which is why this is not an answer. It is definitely not necessary, which you can observe for yourself by removing it from the client program.Bystander
Indeed it is not necessary. I should also mention when I run the client, it creates a socket file named b, if I delete it during runtime, the server still reads the client okay. And it works without the bind() in client.Eirene
Related: #5187895Eirene
W
3

The call to bind() is only required if you need to receive a connection with SOCK_STREAM type socket, but bind() behavior depends on the domain of the SOCKET. There is a manual page dedicated to this.

Useful information:

Address format

A UNIX domain socket address is represented in the following structure:

#define UNIX_PATH_MAX    108

struct sockaddr_un {
    sa_family_t sun_family;               /* AF_UNIX */
    char        sun_path[UNIX_PATH_MAX];  /* pathname */
};

Three types of address are distinguished in this structure:

  • pathname: a UNIX domain socket can be bound to a null-terminated file system pathname using bind(2). When the address of the socket is returned by getsockname(2), getpeername(2), and accept(2), its length is offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1, and sun_path contains the null-terminated pathname.

  • unnamed: A stream socket that has not been bound to a pathname using bind(2) has no name. Likewise, the two sockets created by socketpair(2) are unnamed. When the address of an unnamed socket is returned by getsockname(2), getpeername(2), and accept(2), its length is sizeof(sa_family_t), and sun_path should not be inspected.

  • abstract: an abstract socket address is distinguished by the fact that sun_path[0] is a null byte ('\0'). The socket's address in this namespace is given by the additional bytes in sun_path that are covered by the specified length of the address structure. (Null bytes in the name have no special significance.) The name has no connection with file system pathnames. When the address of an abstract socket is returned by getsockname(2), getpeername(2), and accept(2), the returned addrlen is greater than sizeof(sa_family_t) (i.e., greater than 2), and the name of the socket is contained in the first (addrlen

  • sizeof(sa_family_t)) bytes of sun_path. The abstract socket namespace is a nonportable Linux extension.

Binding to a socket with a filename creates a socket in the file system that must be deleted by the caller when it is no longer needed (using unlink(2)). The usual UNIX close-behind semantics apply; the socket can be unlinked at any time and will be finally removed from the file system when the last reference to it is closed.


So:

  • bind() is not necessary in a client.
  • bind() in your context give a name to yours sockets "a" and "b"
  • bind(s0, &sa0, sizeof(sa0) + sizeof("b")); and similar line in yours code are undefined behavior; it gives a wrong size to bind() that exceeds the bound of &sa0. The correct code is bind(s0, &sa0, sizeof sa0);
  • bind() in this context (Linux, AF_UNIX) does create a special socket file; if you want to remove it, you must call unlink() or remove().
Wastebasket answered 7/12, 2017 at 21:5 Comment(4)
Thank you for the note about the undefined behavior. May I leave the code as-is however, as I'm asking this in the context of a class, and this is inspired closely from a class example with undefined behavior?Eirene
Your update points out bind() doesn't create a special socket. I find after I run the client program, there is a b file of type socket in the same directory. Is something else creating this?Eirene
@Eirene If in your class, you are still send the wrong size to bind(), this will be undefined behavior, but it hard to answer you without code of the class. Yes, my bad bind() linux implementation show/create the socket in the file system.Wastebasket
thank you for clarifying - I certainly agree this is a bad code example, I can update it if you like.Eirene
S
2

man bind gives this answer:

   When  a  socket  is  created  with socket(2), it exists in a name space
   (address family) but has no address assigned to it.  bind() assigns the
   address  specified  by  addr  to  the  socket  referred  to by the file
   descriptor sockfd.  addrlen  specifies  the  size,  in  bytes,  of  the
   address structure pointed to by addr.  Traditionally, this operation is
   called “assigning a name to a socket”.

   It is normally necessary to assign a local address using bind()  before
   a SOCK_STREAM socket may receive connections (see accept(2)).
Studious answered 7/12, 2017 at 20:43 Comment(2)
Well the docs there tell you: you need to bind() to receive connections, not to make them.Studious
Indeed, however the manpages are not the end-all-be-all, thus I cannot accept this answer.Eirene
P
2

Calling bind() on a Unix-domain socket, with no intention of ever calling accept(), is a very useful way of assuring only one copy of a process is running. It's a lot more robust than relying on a process name, as binaries can be copied and run under another name.

Cleanup on abnormal termination (SIGSEGV, being the target of kill -9 ...) is a problem, though. as the socket won't be removed unless your application does it in a signal handler.

Pitre answered 7/12, 2017 at 22:55 Comment(3)
Do you know of any common programs that do this? This will aide in my research :). My understanding in other words, this acts as a lock file, is that correct?Eirene
@Eirene I don't know of any offhand, but it's the first answer to How to ensure only one copy of the application is running? question. A TCP socket is cleaner as the kernel will clean up the socket no matter how the process dies. I've used the Unix socket approach on systems where my customer wants copious documentation on all TCP port usage, so if their security scanners find a TCP port in use they have documentation that it's supposed to be in use. A Unix socket was so much easier...Pitre
I think your answer could use some elaboration - for example: how one might use this mechanism and if we ever find an example in the wild, that would be a nice link. But I think a little description on the idea of using a socket as a lock to prevent multiple versions of a process might actually work, would do wonders. This is the sort of answer I was looking for - it describes something that might not be best practice, but is none-the-less very useful and of practical use.Eirene
S
1

I've just encountered something like this with a unix datagram socket. wpa_supplicant has a control interface that uses a unix datagram socket. The client has to bind it's end of the socket to a path, even though it's a client socket that will connect to wpa_supplicant's server socket. If this step isn't done, then the server cannot send replies back to the client, attempts fail with ENOTCONN error.

I've been a C programmer for a long time though, and this is the first time I've ever encountered this behavior. It seems like wpa_supplicant is essentially trying to use datagram sockets as though they were stream sockets, and I don't understand why.

Stratification answered 16/6, 2022 at 11:28 Comment(0)
D
1

When using AF_UNIX (unix domain sockets), is there any application to calling bind() in a process that never calls listen()?

Short form: yes. Some socket protocols are connectionless, you can send and receive without ever doing connect or listen or accept; you can send() to a connected socket, but you can sendto() any socket you have a connectionless-protocol address for. For AF_UNIX that's SOCK_DGRAM sockets, and the only address those can have is a path.

Dejecta answered 12/3, 2024 at 2:50 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.