#ifdef __linux__ #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #ifdef __linux__ #include #endif #include "net.h" #include "util.h" int voyeur_create_server_socket(struct sockaddr_un* sockinfo) { // Configure a unix domain socket at a temporary path. char sockdir[] = "/tmp/libvoyeur-XXXXXXXXX"; mkdtemp(sockdir); memset(sockinfo, 0, sizeof(struct sockaddr_un)); sockinfo->sun_family = AF_UNIX; strlcpy(sockinfo->sun_path, sockdir, sizeof(sockinfo->sun_path)); strlcat(sockinfo->sun_path, "/socket", sizeof(sockinfo->sun_path)); unlink(sockinfo->sun_path); // Start the server. int server_sock = socket(AF_UNIX, SOCK_STREAM, 0); RETURN_ERROR_ON_FAIL(bind, server_sock, (struct sockaddr*) sockinfo, sizeof(struct sockaddr_un)); RETURN_ERROR_ON_FAIL(listen, server_sock, 200); //chmod(sockinfo->sun_path, 0777); //fchmod(server_sock, 0777); fcntl(server_sock, F_SETFD, FD_CLOEXEC); return server_sock; } #define CONNECT_RETRIES 20 #define CONNECT_WAIT_MS 50 #define CONNECT_DEVIATION_MS 20 int voyeur_create_client_socket(const char* sockpath) { struct sockaddr_un sockinfo; memset(&sockinfo, 0, sizeof(struct sockaddr_un)); sockinfo.sun_family = AF_UNIX; strlcpy(sockinfo.sun_path, sockpath, sizeof(sockinfo.sun_path)); // Connect to the server. int client_sock = socket(AF_UNIX, SOCK_STREAM, 0); int set = 1; #ifdef __APPLE__ setsockopt(client_sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); #endif unsigned try = 0; int connect_status = connect(client_sock, (struct sockaddr*) &sockinfo, sizeof(struct sockaddr_un)); while (connect_status < 0 && ++try < CONNECT_RETRIES) { usleep(((CONNECT_WAIT_MS - CONNECT_DEVIATION_MS / 2) + arc4random_uniform(CONNECT_DEVIATION_MS)) * 1000); connect_status = connect(client_sock, (struct sockaddr*) &sockinfo, sizeof(struct sockaddr_un)); } fcntl(client_sock, F_SETFD, FD_CLOEXEC); return client_sock; } void voyeur_close_socket(int fd) { while (close(fd) < 0) { if (errno != EINTR) { // We really only want to keep spinning for EINTR. break; } } } #ifdef __linux__ #define SEND_OPTS MSG_NOSIGNAL #else #define SEND_OPTS 0 #endif static int do_write(int fd, void* buf, size_t buf_size) { ssize_t total_out = 0; while (total_out < (ssize_t) buf_size) { ssize_t out = send(fd, buf + total_out, buf_size - total_out, SEND_OPTS); // Handle errors. if (out < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { continue; } else { return -1; } } total_out += out; } return 0; } static int do_read(int fd, void* buf, size_t buf_size) { ssize_t total_in = 0; while (total_in < (ssize_t) buf_size) { ssize_t in = read(fd, buf + total_in, buf_size - total_in); // Handle errors. if (in < 0) { if (errno == EAGAIN || errno == EINTR) { continue; } else { return -1; } } // Handle EOF. if (in == 0) { return -1; } total_in += in; } return 0; } int voyeur_write_msg_type(int fd, voyeur_msg_type val) { return do_write(fd, (void*) &val, sizeof(voyeur_msg_type)); } int voyeur_read_msg_type(int fd, voyeur_msg_type* val) { return do_read(fd, (void*) val, sizeof(voyeur_msg_type)); } int voyeur_write_event_type(int fd, voyeur_event_type val) { return do_write(fd, (void*) &val, sizeof(voyeur_event_type)); } int voyeur_read_event_type(int fd, voyeur_event_type* val) { return do_read(fd, (void*) val, sizeof(voyeur_event_type)); } int voyeur_write_byte(int fd, char val) { return do_write(fd, (void*) &val, sizeof(char)); } int voyeur_read_byte(int fd, char* val) { return do_read(fd, (void*) val, sizeof(char)); } int voyeur_write_int(int fd, int val) { return do_write(fd, (void*) &val, sizeof(int)); } int voyeur_read_int(int fd, int* val) { return do_read(fd, (void*) val, sizeof(int)); } int voyeur_write_size(int fd, size_t val) { return do_write(fd, (void*) &val, sizeof(size_t)); } int voyeur_read_size(int fd, size_t* val) { return do_read(fd, (void*) val, sizeof(size_t)); } int voyeur_write_pid(int fd, pid_t val) { return do_write(fd, (void*) &val, sizeof(pid_t)); } int voyeur_read_pid(int fd, pid_t* val) { return do_read(fd, (void*) val, sizeof(pid_t)); } int voyeur_write_string(int fd, const char* val, size_t len) { if (val == NULL) { len = 0; } else if (len == 0) { len = strnlen(val, VOYEUR_MAX_STRLEN); } if (voyeur_write_size(fd, len) < 0) { return -1; } return do_write(fd, (void*) val, len); } int voyeur_read_string(int fd, char** val, size_t maxlen) { size_t len; if (voyeur_read_size(fd, &len) < 0) { return -1; } if (maxlen == 0) { *val = malloc(len + 1); } else if (maxlen <= len) { SHOULD_NOT_REACH("libvoyeur: string buffer of size %zu " "is too small for string of length %zu\n", maxlen, len); return -1; } if (do_read(fd, (void*) *val, len) < 0) { if (maxlen == 0) { free(*val); } return -1; } (*val)[len] = '\0'; return 0; }