Search code examples
c++socketsunixc++14unix-socket

Unix socket filename error


This is the first time I am programming with Unix sockets so forgive the stupid errors. I wrote the following function to create a unix socket.

int create_server_unix_socket(const string& socket_path) {

    // STEP 1 : socket()
    int unix_socket = socket(AF_UNIX, SOCK_STREAM, 0);
    if (unix_socket == -1) {
        throw std::runtime_error {"Error occured in socket() call : "s +
            string(std::strerror(errno))};
    }

    // STEP 2 : bind(), setup the address structures for bind()
    sockaddr_un local_address;
    local_address.sun_family = AF_UNIX;
    std::strcpy(local_address.sun_path, socket_path.c_str());

    // unlink from before
    unlink(socket_path.c_str());

    // STEP 2 : bind()
    size_t length = socket_path.size() + sizeof(local_address.sun_family);
    if (::bind(unix_socket, reinterpret_cast<sockaddr*>(&local_address),
                length) == -1) {
        throw std::runtime_error {"Error occured in bind() call : "s +
            string(strerror(errno))};
    }

    // STEP 3 : listen()
    if (listen(unix_socket, 5) == -1) {
        throw std::runtime_error {"Error occured in listen() call : "s +
            string(strerror(errno))};
    }

    return unix_socket;
}

But whenever I try and create a socket like so

create_server_unix_socket("./unix_socket"s);

The file that is created in the current working directory is unix_socke instead of unix_socket and in fact this happens to any name that I try. If I try unix_socket_longer_name, the file produced is unix_socket_longer_nam. I am running this on a Mac OS X (version 10.11.4).

Any ideas on what I am doing wrong? Also if you notice something horribly wrong in my code, please let me know! I wrote this by consulting the man pages and I am not completely sure I did it right. Thanks!

NOTE : I am compiling this with C++14, you would need to import the std::literals::string_literals namespace to make this work. Add using namespace std::literals::string_literals to the top of your code.


Solution

  • Let's take a look at the man page of bind

    int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);
    

    The third parameter is the addrlen

    addrlen specifies the size, in bytes, of the address structure pointed to by addr.

    So, what you should be passing, is sizeof local_address. Instead, you passed length which is socket_path.size() + sizeof(local_address.sun_family). That size is almost enough, but there is no room for the null terminator and it ignores the possibility that sockaddr_un may contain padding.

    If you really want to pass a length that matches the path rather than the full size of local_address, you could use offsetof:

    length = offsetof(sockaddr_un, sun_path) + socket_path.size() + 1;
    

    But I don't see an advantage to it over simply passing sizeof local_address.