client: allow connecting to Unix domain sockets
If the specified hostname starts with /, consider it to be the path of the chronyd Unix domain command socket. Create the client socket in the same directory as the server socket (which is not accessible by others) and change its permission to 0666 to allow chronyd running without root privileges to send a reply. Remove the socket on exit.
This commit is contained in:
parent
70ad0bc573
commit
7079ca2718
1 changed files with 54 additions and 22 deletions
76
client.c
76
client.c
|
@ -51,12 +51,13 @@
|
|||
|
||||
/* ================================================== */
|
||||
|
||||
union sockaddr_in46 {
|
||||
union sockaddr_all {
|
||||
struct sockaddr_in in4;
|
||||
#ifdef FEAT_IPV6
|
||||
struct sockaddr_in6 in6;
|
||||
#endif
|
||||
struct sockaddr u;
|
||||
struct sockaddr_un un;
|
||||
struct sockaddr sa;
|
||||
};
|
||||
|
||||
static int sock_fd;
|
||||
|
@ -133,37 +134,60 @@ read_line(void)
|
|||
static void
|
||||
open_io(const char *hostname, int port)
|
||||
{
|
||||
union sockaddr_in46 addr;
|
||||
union sockaddr_all addr;
|
||||
socklen_t addr_len;
|
||||
IPAddr ip;
|
||||
char *dir;
|
||||
|
||||
/* Note, this call could block for a while */
|
||||
if (DNS_Name2IPAddress(hostname, &ip, 1) != DNS_Success) {
|
||||
LOG_FATAL(LOGF_Client, "Could not get IP address for %s", hostname);
|
||||
/* hostname starting with / is considered a path of Unix domain socket */
|
||||
if (hostname[0] == '/') {
|
||||
if (snprintf(addr.un.sun_path, sizeof (addr.un.sun_path), "%s", hostname) >=
|
||||
sizeof (addr.un.sun_path))
|
||||
LOG_FATAL(LOGF_Client, "Unix socket path too long");
|
||||
addr.un.sun_family = AF_UNIX;
|
||||
addr_len = sizeof (addr.un);
|
||||
} else {
|
||||
/* Note, this call could block for a while */
|
||||
if (DNS_Name2IPAddress(hostname, &ip, 1) != DNS_Success) {
|
||||
LOG_FATAL(LOGF_Client, "Could not get IP address for %s", hostname);
|
||||
}
|
||||
|
||||
addr_len = UTI_IPAndPortToSockaddr(&ip, port, &addr.sa);
|
||||
}
|
||||
|
||||
switch (ip.family) {
|
||||
case IPADDR_INET4:
|
||||
sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
break;
|
||||
#ifdef FEAT_IPV6
|
||||
case IPADDR_INET6:
|
||||
sock_fd = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
sock_fd = socket(addr.sa.sa_family, SOCK_DGRAM, 0);
|
||||
|
||||
if (sock_fd < 0) {
|
||||
LOG_FATAL(LOGF_Client, "Could not create socket : %s", strerror(errno));
|
||||
}
|
||||
|
||||
addr_len = UTI_IPAndPortToSockaddr(&ip, port, &addr.u);
|
||||
|
||||
if (connect(sock_fd, &addr.u, addr_len) < 0) {
|
||||
if (connect(sock_fd, &addr.sa, addr_len) < 0) {
|
||||
LOG_FATAL(LOGF_Client, "Could not connect socket : %s", strerror(errno));
|
||||
}
|
||||
|
||||
if (addr.sa.sa_family == AF_UNIX) {
|
||||
/* Construct path of our socket. Use the same directory as the server
|
||||
socket and include our process ID to allow multiple chronyc instances
|
||||
running at the same time. */
|
||||
dir = UTI_PathToDir(hostname);
|
||||
if (snprintf(addr.un.sun_path, sizeof (addr.un.sun_path),
|
||||
"%s/chronyc.%d.sock", dir, getpid()) >= sizeof (addr.un.sun_path))
|
||||
LOG_FATAL(LOGF_Client, "Unix socket path too long");
|
||||
Free(dir);
|
||||
|
||||
addr.un.sun_family = AF_UNIX;
|
||||
unlink(addr.un.sun_path);
|
||||
|
||||
/* Bind the socket to the path */
|
||||
if (bind(sock_fd, &addr.sa, sizeof (addr.un)) < 0) {
|
||||
LOG_FATAL(LOGF_Client, "Could not bind socket : %s", strerror(errno));
|
||||
}
|
||||
|
||||
/* Allow server without root privileges to send replies to our socket */
|
||||
if (chmod(addr.un.sun_path, 0666) < 0) {
|
||||
LOG_FATAL(LOGF_Client, "Could not change socket permissions : %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
@ -171,9 +195,17 @@ open_io(const char *hostname, int port)
|
|||
static void
|
||||
close_io(void)
|
||||
{
|
||||
union sockaddr_all addr;
|
||||
socklen_t addr_len = sizeof (addr);
|
||||
|
||||
/* Remove our Unix domain socket */
|
||||
if (getsockname(sock_fd, &addr.sa, &addr_len) < 0)
|
||||
LOG_FATAL(LOGF_Client, "getsockname() failed : %s", strerror(errno));
|
||||
if (addr_len <= sizeof (addr) && addr_len > sizeof (addr.sa.sa_family) &&
|
||||
addr.sa.sa_family == AF_UNIX)
|
||||
unlink(addr.un.sun_path);
|
||||
|
||||
close(sock_fd);
|
||||
|
||||
}
|
||||
|
||||
/* ================================================== */
|
||||
|
|
Loading…
Reference in a new issue