localserv/lib/server.c

151 lines
4.9 KiB
C

#include "server.h"
struct {
int sock_fd;
char* request;
uid_t uid;
char* client_path;
} server_globals = {
.request = NULL,
.uid = 0,
.client_path = NULL,
};
int server_init(char* path) {
umask(0);
server_globals.sock_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
if (server_globals.sock_fd < 0) {
return errno;
}
struct sockaddr_un server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
// copy in the provided directory path for the socket
memcpy(server_addr.sun_path, path, strlen(path));
// // is this needed?
// signal(SIGPIPE, SIG_IGN);
socklen_t server_addr_len = sizeof(server_addr.sun_family) + strlen(server_addr.sun_path);
unlink(server_addr.sun_path);
if (bind(server_globals.sock_fd, (struct sockaddr *)&server_addr, server_addr_len) != 0) {
return errno;
}
int optval = 1;
if (setsockopt(server_globals.sock_fd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) != 0) {
return errno;
}
server_globals.client_path = malloc(CLIENT_PATH_LENGTH);
return 0;
}
int server_listen() {
struct sockaddr_un client_addr;
struct msghdr message_hdr;
// tells recvmsg where to put the client address name
message_hdr.msg_name = (struct sockaddr *)&client_addr;
// only ever expect the abstract address to be 5 chars long (plus one for null at beginning)
// sockaddr_un is big enough to handle up to 108 characters but we don't want more than 6
message_hdr.msg_namelen = sizeof(sa_family_t) + CLIENT_PATH_LENGTH;
// this is where the message (the client's request) will be stored, it's weird cause it can support multiple buffers
struct iovec iovec;
// the buffer to receive into is just the memory for the request in the return result, this way no copying is required
iovec.iov_base = NULL;
iovec.iov_len = 0;
message_hdr.msg_iov = &iovec;
// there's only one element in the iovec cause we only use one buffer
message_hdr.msg_iovlen = 1;
// set after call, not checked so it shouldn't matter
message_hdr.msg_flags = 0;
// this is the buffer for getting ancillary data (namely, the UID of the requesting client)
char message_control_buffer[CONTROL_BUFFER_SIZE];
message_hdr.msg_control = &message_control_buffer;
message_hdr.msg_controllen = CONTROL_BUFFER_SIZE;
// call gets info about waiting message and sender (address and uid)
ssize_t msg_size = recvmsg(server_globals.sock_fd, &message_hdr, MSG_PEEK | MSG_TRUNC);
// for now, just output how big it was, later, we will check to make sure it's the correct size
if (msg_size < 0) {
return errno;
}
// extract credentials from the message
struct ucred credentials;
memcpy(&credentials, CMSG_DATA((struct cmsghdr *)message_hdr.msg_control), sizeof(credentials));
// recvmsg populated msg_name with the client's actual path info.
// copy that into the client path
// client_addr.sun_path should be updated by recvmsg
memcpy(server_globals.client_path, client_addr.sun_path, CLIENT_PATH_LENGTH);
server_globals.uid = credentials.uid;
// username = getpwuid(credentials.uid)->pw_name;
// allocate buffer to receive the actual message
free(server_globals.request);
server_globals.request = malloc(msg_size);
ssize_t recv_size = recv(server_globals.sock_fd, server_globals.request, msg_size, 0);
if (recv_size < 0) {
return errno;
}
// request was already added to result, so it's ready to go
return 0;
}
int server_respond(char* response) {
// set up client address
struct sockaddr_un client_addr;
client_addr.sun_family = AF_UNIX;
memcpy(client_addr.sun_path, server_globals.client_path, CLIENT_PATH_LENGTH);
// send stream
// NOTE: I added a 1 here to include the null byte at the end of the string, because it didn't seem to be included otherwise
// but it appears to work without that when the client sends the request? Probably needs more investigation
ssize_t sent = sendto(server_globals.sock_fd, response, strlen(response) + 1, 0, (struct sockaddr *)&client_addr, sizeof(sa_family_t) + CLIENT_PATH_LENGTH);
if (sent < 0) {
return errno;
}
return 0;
}
char* server_get_request() {
return server_globals.request;
}
char* server_get_username(uid_t uid) {
// if this doesn't work sometime you can try storing the uid instead and then calling getpwuid in here
return getpwuid(uid)->pw_name;
}
uid_t server_get_uid() {
return server_globals.uid;
}
// void __attribute__ ((constructor)) init(void) {
// }
// TODO: proper signal handling (e.g. this doesn't run when SIGINT stops the program)
void __attribute__ ((destructor)) server_clean_up(void) {
// TODO: error handling?
close(server_globals.sock_fd);
free(server_globals.request);
free(server_globals.client_path);
// don't need to free username since it's provided by getpwuid
}