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.
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 byaddr
.
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
.