/* 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; }