| /* Copyright 2015-2022 Rivoreo |
| |
| Permission is hereby granted, free of charge, to any person obtaining |
| a copy of this software and associated documentation files (the |
| "Software"), to deal in the Software without restriction, including |
| without limitation the rights to use, copy, modify, merge, publish, |
| distribute, sublicense, and/or sell copies of the Software, and to |
| permit persons to whom the Software is furnished to do so, subject to |
| the following conditions: |
| |
| The above copyright notice and this permission notice shall be |
| included in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE |
| FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
| CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <netdb.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <errno.h> |
| |
| static void print_usage(const char *name) { |
| fprintf(stderr, "Usage:\n" |
| " %s [-d <fd>] <option>[=<value>] ...\n" |
| " %s [-d <fd>] {-a|-l}\n", |
| name, name); |
| } |
| |
| #define SOTYPE_INT 0 |
| |
| struct socket_option { |
| int data_type; |
| int number; |
| const char *name; |
| }; |
| |
| static const struct socket_option options[] = { |
| { SOTYPE_INT, SO_DEBUG , "debug" }, |
| { SOTYPE_INT, SO_ACCEPTCONN , "acceptconn" }, |
| { SOTYPE_INT, SO_REUSEADDR , "reuseaddr" }, |
| { SOTYPE_INT, SO_KEEPALIVE , "keepalive" }, |
| { SOTYPE_INT, SO_DONTROUTE , "dontroute" }, |
| { SOTYPE_INT, SO_BROADCAST , "broadcast" }, |
| #ifdef SO_USELOOPBACK |
| { SOTYPE_INT, SO_USELOOPBACK , "useloopback" }, |
| #endif |
| { SOTYPE_INT, SO_LINGER , "linger" }, |
| { SOTYPE_INT, SO_OOBINLINE , "oobinline" }, |
| #ifdef SO_REUSEPORT |
| { SOTYPE_INT, SO_REUSEPORT , "reuseport" }, |
| #endif |
| #ifdef SO_TIMESTAMP |
| { SOTYPE_INT, SO_TIMESTAMP , "timestamp" }, |
| #endif |
| #ifdef SO_NOSIGPIPE |
| { SOTYPE_INT, SO_NOSIGPIPE , "nosigpipe" }, |
| #endif |
| #ifdef SO_ACCEPTFILTER |
| { SOTYPE_INT, SO_ACCEPTFILTER, "acceptfilter" }, |
| #endif |
| #ifdef SO_BINTIME |
| { SOTYPE_INT, SO_BINTIME , "bintime" }, |
| #endif |
| #ifdef SO_NO_OFFLOAD |
| { SOTYPE_INT, SO_NO_OFFLOAD , "no_offload" }, |
| #endif |
| #ifdef SO_NO_DDP |
| { SOTYPE_INT, SO_NO_DDP , "no_ddp" }, |
| #endif |
| }; |
| |
| static const struct socket_option *get_option_by_name(const char *name) { |
| unsigned int i = sizeof options / sizeof *options; |
| while(i > 0) { |
| const struct socket_option *o = options + --i; |
| if(strcmp(o->name, name) == 0) return o; |
| } |
| return NULL; |
| } |
| |
| #define DEFINE_PRINT_OPTION_FUNCTION(T,FMT,CAST) \ |
| static int print_##T##_option(int fd, const struct socket_option *o) { \ |
| T value; \ |
| socklen_t len = sizeof value; \ |
| if(getsockopt(fd, SOL_SOCKET, o->number, &value, &len) < 0) return -1; \ |
| printf("%s=" FMT "\n", o->name, (CAST)value); \ |
| return 0; \ |
| } |
| |
| DEFINE_PRINT_OPTION_FUNCTION(int, "%d", int) |
| |
| static int print_option(int fd, const struct socket_option *o) { |
| switch(o->data_type) { |
| case SOTYPE_INT: |
| return print_int_option(fd, o); |
| default: |
| printf("%s=?\n", o->name); |
| return 0; |
| } |
| } |
| |
| static int set_int_option(int fd, const struct socket_option *o, const char *value_s) { |
| if(!*value_s) { |
| errno = EINVAL; |
| return -1; |
| } |
| char *end_p; |
| int v = strtol(value_s, &end_p, 0); |
| if(*end_p) { |
| errno = EINVAL; |
| return -1; |
| } |
| return setsockopt(fd, SOL_SOCKET, o->number, &v, sizeof v); |
| } |
| |
| static int set_option(int fd, const struct socket_option *o, const char *value) { |
| switch(o->data_type) { |
| case SOTYPE_INT: |
| return set_int_option(fd, o, value); |
| default: |
| errno = EPERM; |
| return -1; |
| } |
| } |
| |
| int main(int argc, char **argv) { |
| int fd = STDIN_FILENO; |
| int read_all = 0; |
| int list_all = 0; |
| char *(*kvps)[2] = NULL; |
| unsigned int kvp_count = 0; |
| int i = 1; |
| int end_of_options = 0; |
| while(i < argc) { |
| if(!end_of_options && argv[i][0] == '-') { |
| const char *o = argv[i] + 1; |
| if(!*o) goto not_an_option; |
| if(*o == '-') { |
| if(o[1]) { |
| fprintf(stderr, "%s: Invalid option '%s'\n", argv[0], argv[i]); |
| return -1; |
| } |
| end_of_options = 1; |
| } else while(*o) switch(*o++) { |
| char *end_p; |
| case 'd': |
| if(++i >= argc) { |
| fprintf(stderr, "%s: Option '-d' requires an argument\n", argv[0]); |
| return -1; |
| } |
| fd = strtol(argv[i], &end_p, 0); |
| if(*end_p) { |
| fprintf(stderr, "%s: Invalid file descriptor specification\n", |
| argv[0]); |
| return -1; |
| } |
| break; |
| case 'a': |
| read_all = 1; |
| break; |
| case 'l': |
| list_all = 1; |
| break; |
| case 'h': |
| print_usage(argv[0]); |
| return 0; |
| default: |
| fprintf(stderr, "%s: Invalid option '-%c'\n", argv[0], o[-1]); |
| return -1; |
| } |
| } else { |
| not_an_option: |
| kvps = realloc(kvps, (kvp_count + 1) * sizeof(char *) * 2); |
| if(!kvps) { |
| fprintf(stderr, "%s: Out of memory\n", argv[0]); |
| return 1; |
| } |
| char *value = strchr(argv[i], '='); |
| if(value) *value++ = 0; |
| kvps[kvp_count][0] = argv[i]; |
| kvps[kvp_count][1] = value; |
| kvp_count++; |
| } |
| i++; |
| } |
| if(((read_all && list_all)) || ((read_all || list_all) && kvps)) { |
| print_usage(argv[0]); |
| return -1; |
| } |
| if(read_all) for(i = 0; i < sizeof options / sizeof *options; i++) { |
| const struct socket_option *o = options + i; |
| if(print_option(fd, o) < 0) perror(o->name); |
| } else if(list_all) for(i = 0; i < sizeof options / sizeof *options; i++) { |
| const struct socket_option *o = options + i; |
| puts(o->name); |
| } else if(kvps) for(i = 0; i < kvp_count; i++) { |
| const char *key = kvps[i][0]; |
| const char *value = kvps[i][1]; |
| const struct socket_option *o = get_option_by_name(key); |
| if(!o) { |
| fprintf(stderr, "%s: Option '%s' not found\n", argv[0], key); |
| continue; |
| } |
| if((value ? set_option(fd, o, value) : print_option(fd, o)) < 0) perror(key); |
| } else { |
| print_usage(argv[0]); |
| return -1; |
| } |
| return 0; |
| } |