kindwolf.org Git repositories so_priority.so / master so_priority.c
master

Tree @master (Download .tar.gz)

so_priority.c @masterraw · history · blame

/*
	Set SO_PRIORITY right after each socket() call.
	Compile with: gcc -shared -ldl -fPIC so_priority.c -o so_priority.so
	Usage: SO_PRIORITY_DEBUG=1 SO_PRIORITY_VALUE=6 LD_PRELOAD=/path/to/so_priority.so program arg1 arg2

	(c) 2020 - Xavier Guerrin
	This program is free software. It comes without any warranty, to
	the extent permitted by applicable law. You can redistribute it
	and/or modify it under the terms of the Do What The Fuck You Want
	To Public License, Version 2, as published by Sam Hocevar. See
	http://www.wtfpl.net/ for more details.
*/

#define _GNU_SOURCE /* RTLD_NEXT */
#include <dlfcn.h> /* dlsym, dlerror */
#include <errno.h> /* errno */
#include <stdio.h> /* dprintf */
#include <stdlib.h> /* atoi, getenv */
#include <string.h> /* strerror */
#include <sys/types.h> /* setsockopt */
#include <sys/socket.h> /* setsockopt */

#ifndef SOPRIORITY_SO_NAME
#define SOPRIORITY_SO_NAME "so_priority.so"
#endif
#ifndef SOPRIORITY_VALUE_EV
#define SOPRIORITY_VALUE_EV "SO_PRIORITY_VALUE"
#endif
#ifndef SOPRIORITY_DEBUG_EV
#define SOPRIORITY_DEBUG_EV "SO_PRIORITY_DEBUG"
#endif

typedef int (*socket_function_type)(int, int, int);
 
/* Pointer to the actual socket symbol. */
socket_function_type so_priority_socket = NULL;

/* SO_PRIORITY value to set; can be configured using the SO_PRIORITY_VALUE environment variable. */
int so_priority_value = 0;
/* Debug level; can be configured using the SO_PRIORITY_DEBUG environment variable. */
int so_priority_debug = 0;
int so_priority_known = 0;

/* Ask the linker to provide the actual "socket" symbol: */
static int so_priority_get_socket() {
	char *error_string;
	so_priority_socket = (socket_function_type)dlsym(RTLD_NEXT, "socket");
	if (!so_priority_socket) {
		error_string = dlerror();
		if (error_string) {
			dprintf(2, "%s: unable to find the actual socket symbol: %s\n", SOPRIORITY_SO_NAME, error_string);
		}
		else {
			dprintf(2, "%s: unable to find the actual socket symbol\n", SOPRIORITY_SO_NAME);
		}
		return 0;
	}
	return 1;
}

/* Read the priority value from the environment. */
static void so_priority_set_options() {
	char *str;
	str = getenv(SOPRIORITY_VALUE_EV);
	if (str) so_priority_value = atoi(str);
	str = getenv(SOPRIORITY_DEBUG_EV);
	if (str) so_priority_debug = atoi(str);
	so_priority_known = 1;
}

/* Wrapper around the actual socket() function that sets SO_PRIORITY by calling setsockopt(). */
int socket(int domain, int type, int protocol) {
	int ret, setsockopt_errno, socket_errno, sockfd;

	if (!so_priority_socket && !so_priority_get_socket()) {
		errno = ENOENT;
		return -1;
	}
	/* Actually call socket(): */
	sockfd = so_priority_socket(domain, type, protocol);
	/* Do not call setsockopt() if socket() failed: */
	if (sockfd < 0) return sockfd;

	/* Time for the actual work: */
	socket_errno = errno;

	if (!so_priority_known) so_priority_set_options();
	ret = setsockopt(sockfd, SOL_SOCKET, SO_PRIORITY, &so_priority_value, sizeof(so_priority_value));
	/* Ignore setsockopt() errors, except for debugging purposes. */
	if (so_priority_debug) {
		setsockopt_errno = errno;
		dprintf(2, "%s: setsockopt(%d, SOL_SOCKET, SO_PRIORITY, %d, %zu) returned %d",
		        SOPRIORITY_SO_NAME, sockfd, so_priority_value, sizeof(so_priority_value), ret);
		if (ret < 0) dprintf(2, "; errno is %d: %s", setsockopt_errno, strerror(setsockopt_errno));
		dprintf(2, "\n");
	}
	
	errno = socket_errno;
	return sockfd;
}