#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 }