149 lines
3.5 KiB
C
149 lines
3.5 KiB
C
/* socket_trans.c - Functions to transfer data through socket
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include "magisk.h"
|
|
#include "utils.h"
|
|
#include "daemon.h"
|
|
|
|
/*
|
|
* Receive a file descriptor from a Unix socket.
|
|
* Contributed by @mkasick
|
|
*
|
|
* Returns the file descriptor on success, or -1 if a file
|
|
* descriptor was not actually included in the message
|
|
*
|
|
* On error the function terminates by calling exit(-1)
|
|
*/
|
|
int recv_fd(int sockfd) {
|
|
// Need to receive data from the message, otherwise don't care about it.
|
|
char iovbuf;
|
|
|
|
struct iovec iov = {
|
|
.iov_base = &iovbuf,
|
|
.iov_len = 1,
|
|
};
|
|
|
|
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
|
|
|
struct msghdr msg = {
|
|
.msg_iov = &iov,
|
|
.msg_iovlen = 1,
|
|
.msg_control = cmsgbuf,
|
|
.msg_controllen = sizeof(cmsgbuf),
|
|
};
|
|
|
|
xrecvmsg(sockfd, &msg, MSG_WAITALL);
|
|
|
|
// Was a control message actually sent?
|
|
switch (msg.msg_controllen) {
|
|
case 0:
|
|
// No, so the file descriptor was closed and won't be used.
|
|
return -1;
|
|
case sizeof(cmsgbuf):
|
|
// Yes, grab the file descriptor from it.
|
|
break;
|
|
default:
|
|
goto error;
|
|
}
|
|
|
|
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
|
|
|
if (cmsg == NULL ||
|
|
cmsg->cmsg_len != CMSG_LEN(sizeof(int)) ||
|
|
cmsg->cmsg_level != SOL_SOCKET ||
|
|
cmsg->cmsg_type != SCM_RIGHTS) {
|
|
error:
|
|
LOGE("unable to read fd");
|
|
exit(-1);
|
|
}
|
|
|
|
return *(int *)CMSG_DATA(cmsg);
|
|
}
|
|
|
|
/*
|
|
* Send a file descriptor through a Unix socket.
|
|
* Contributed by @mkasick
|
|
*
|
|
* On error the function terminates by calling exit(-1)
|
|
*
|
|
* fd may be -1, in which case the dummy data is sent,
|
|
* but no control message with the FD is sent.
|
|
*/
|
|
void send_fd(int sockfd, int fd) {
|
|
// Need to send some data in the message, this will do.
|
|
struct iovec iov = {
|
|
.iov_base = "",
|
|
.iov_len = 1,
|
|
};
|
|
|
|
struct msghdr msg = {
|
|
.msg_iov = &iov,
|
|
.msg_iovlen = 1,
|
|
};
|
|
|
|
char cmsgbuf[CMSG_SPACE(sizeof(int))];
|
|
|
|
if (fd != -1) {
|
|
// Is the file descriptor actually open?
|
|
if (fcntl(fd, F_GETFD) == -1) {
|
|
if (errno != EBADF) {
|
|
PLOGE("unable to send fd");
|
|
}
|
|
// It's closed, don't send a control message or sendmsg will EBADF.
|
|
} else {
|
|
// It's open, send the file descriptor in a control message.
|
|
msg.msg_control = cmsgbuf;
|
|
msg.msg_controllen = sizeof(cmsgbuf);
|
|
|
|
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
|
|
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
|
|
*(int *)CMSG_DATA(cmsg) = fd;
|
|
}
|
|
}
|
|
|
|
xsendmsg(sockfd, &msg, 0);
|
|
}
|
|
|
|
int read_int(int fd) {
|
|
int val;
|
|
xxread(fd, &val, sizeof(int));
|
|
return val;
|
|
}
|
|
|
|
void write_int(int fd, int val) {
|
|
if (fd < 0) return;
|
|
xwrite(fd, &val, sizeof(int));
|
|
}
|
|
|
|
char* read_string(int fd) {
|
|
int len = read_int(fd);
|
|
if (len > PATH_MAX || len < 0) {
|
|
LOGE("invalid string length %d", len);
|
|
exit(1);
|
|
}
|
|
char* val = xmalloc(sizeof(char) * (len + 1));
|
|
xxread(fd, val, len);
|
|
val[len] = '\0';
|
|
return val;
|
|
}
|
|
|
|
void write_string(int fd, const char* val) {
|
|
if (fd < 0) return;
|
|
int len = strlen(val);
|
|
write_int(fd, len);
|
|
xwrite(fd, val, len);
|
|
}
|