blob: 73d9a5c08cafe21fc8ddefbf72f1ca0814573a01 [file] [log] [blame] [raw]
/* 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;
}