diff --git a/CHANGES b/CHANGES index d61235a..8b50c67 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,10 @@ new features: - added address options IOCTL-VOID, IOCTL-INT, IOCTL-INTP, IOCTL-STRING, - IOCTL-BIN for generic ioctl() calls. + added address options ioctl-void, ioctl-int, ioctl-intp, ioctl-string, + ioctl-bin for generic ioctl() calls. + + added address options setsockopt-int, setsockopt-bin, and + setsockopt-string corrections: some raw IP and UNIX datagram modes failed on BSD systems diff --git a/EXAMPLES b/EXAMPLES index d528e4b..60ca020 100644 --- a/EXAMPLES +++ b/EXAMPLES @@ -286,11 +286,27 @@ $ socat -d -d -d -d - udp-datagram:224.0.0.2:6666,bind=:6666,ip-add-membership=2 // socket bound to specific address //============================================================================= -// IOCTL +// GENERIC FUNCTION CALLS -// open CD drive (given value valid on Linux) +// ioctl(): open CD drive (given value valid on Linux) +// on my Linux system I find in /usr/include/linux/cdrom.h the define: +// #define CDROMEJECT 0x5309 /* Ejects the cdrom media */ +// the following command makes something like ioctl(fd, CDROMEJECT, NULL) $ socat /dev/cdrom,o-nonblock,ioctl-void=0x5309 - +// setsockopt(): SO_REUSEADDR +// the following command performs - beyond lots of overhead - something like: +// myint=1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &myint, sizeof(myint)) +$ socat -u udp-recv:7777,setsockopt-int=1:2:1 - +// setsockopt(): SO_BINDTODEVICE + +// ways to apply SO_BINDTODEVICE without using the special socat address option +// so-bindtodevice: +// with string argument: +$ sudo ./socat tcp-l:7777,setsockopt-string=1:25:eth0 pipe +// with binary argument: +$ sudo ./socat tcp-l:7777,setsockopt-bin=1:25:x6574683000 pipe + =============================================================================== // not tested, just ideas, or have problems diff --git a/VERSION b/VERSION index 049994b..0b595f8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -"1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll+udplistencont+ignoreeofunblock+ioctl" +"1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll+udplistencont+ignoreeofunblock+ioctl+ioctl+setsockopt" diff --git a/doc/socat.yo b/doc/socat.yo index 556aaeb..4df8cd2 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -1653,6 +1653,22 @@ COMMENT(label(OPTION_USEIFBUFS)dit(bf(tt(useifbufs))) label(OPTION_PROTOCOL_FAMILY)dit(bf(tt(pf=))) Forces the use of the specified IP version. can be something like "ip4" or "ip6". +label(OPTION_SETSOCKOPT_INT)dit(bf(tt(setsockopt-int=::))) + Invokes tt(setsockopt()) for the socket with the given parameters. tt(level) + [link(int)(TYPE_INT)] specifies the second argument to tt(setsockopt()) + which specifies the layer, e.g. SOL_TCP (=6 on Linux) for TCP, or SOL_SOCKET + (=1 on Linux) for the socket layer. tt(optname) [link(int)(TYPE_INT)] + selects the third argument to tt(setsockopt()) option. For the actual + integer values you might have to look up the appropriate include file of + your system. The 4th tt(setsockopt()) parameter, tt(value) + [link(int)(TYPE_INT)], is passed to the function per pointer, and the length + parameter to the tt(setsockopt()) call is derived from this type. +label(OPTION_SETSOCKOPT_BIN)dit(bf(tt(setsockopt-bin=::))) + Like tt(setsockopt-int), but is expected in link(dalan)(TYPE_DATA) + format and specifies an arbitrary sequence of bytes. +label(OPTION_SETSOCKOPT_STRING)dit(bf(tt(setsockopt-string=::))) + Like tt(setsockopt-int), but must be a link(string)(TYPE_STRING). + This string is passed to the function with trailing null character. enddit() startdit()enddit()nl() diff --git a/xio-socket.c b/xio-socket.c index d93bb98..5399967 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -128,6 +128,12 @@ const struct optdesc opt_bind = { "bind", NULL, OPT_BIND, GRO const struct optdesc opt_connect_timeout = { "connect-timeout", NULL, OPT_CONNECT_TIMEOUT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_TIMEVAL, OFUNC_OFFSET, (int)&((xiofile_t *)0)->stream.para.socket.connect_timeout }; const struct optdesc opt_protocol_family = { "protocol-family", "pf", OPT_PROTOCOL_FAMILY, GROUP_SOCKET, PH_PRESOCKET, TYPE_STRING, OFUNC_SPEC }; +/* generic setsockopt() options */ +const struct optdesc opt_setsockopt_int = { "setsockopt-int", "sockopt-int", OPT_SETSOCKOPT_INT, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT_INT_INT, OFUNC_SPEC, 0, 0 }; +const struct optdesc opt_setsockopt_bin = { "setsockopt-bin", "sockopt-bin", OPT_SETSOCKOPT_BIN, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT_INT_BIN, OFUNC_SPEC, 0, 0 }; +const struct optdesc opt_setsockopt_string = { "setsockopt-string", "sockopt-string", OPT_SETSOCKOPT_STRING, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT_INT_STRING, OFUNC_SPEC, 0, 0 }; + + /* a subroutine that is common to all socket addresses that want to connect to a peer address. might fork. diff --git a/xio-socket.h b/xio-socket.h index 04734f9..4947ead 100644 --- a/xio-socket.h +++ b/xio-socket.h @@ -50,6 +50,9 @@ extern const struct optdesc opt_fiosetown; extern const struct optdesc opt_siocspgrp; extern const struct optdesc opt_bind; extern const struct optdesc opt_protocol_family; +extern const struct optdesc opt_setsockopt_int; +extern const struct optdesc opt_setsockopt_bin; +extern const struct optdesc opt_setsockopt_string; extern int retropt_socket_pf(struct opt *opts, int *pf); diff --git a/xio.h b/xio.h index ddfdd6b..4dc1464 100644 --- a/xio.h +++ b/xio.h @@ -356,6 +356,7 @@ struct opt { const struct optdesc *desc; union integral value; union integral value2; + union integral value3; } ; extern const char *PIPESEP; diff --git a/xioopts.c b/xioopts.c index 3d7b53e..f843da2 100644 --- a/xioopts.c +++ b/xioopts.c @@ -1196,6 +1196,9 @@ const struct optname optionnames[] = { #if WITH_EXEC || WITH_SYSTEM IF_EXEC ("setsid", &opt_setsid) #endif + IF_SOCKET ("setsockopt-bin", &opt_setsockopt_bin) + IF_SOCKET ("setsockopt-int", &opt_setsockopt_int) + IF_SOCKET ("setsockopt-string", &opt_setsockopt_string) IF_ANY ("setuid", &opt_setuid) IF_ANY ("setuid-early", &opt_setuid_early) #if WITH_EXEC || WITH_SYSTEM @@ -2031,13 +2034,97 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts, ent->desc->defname); } ++rest; - if (((*opts)[i].value2.u_string = strdup(token)) == NULL) { + if (((*opts)[i].value2.u_string = strdup(rest)) == NULL) { Error("out of memory"); return -1; } } - Info3("setting option \"%s\" to %d:\"$s\"", ent->desc->defname, + Info3("setting option \"%s\" to %d:\"%s\"", ent->desc->defname, (*opts)[i].value.u_int, (*opts)[i].value2.u_string); break; + case TYPE_INT_INT_INT: + if (!assign) { + Error1("option \"%s\": values required", a0); + continue; + } + { + char *rest; + (*opts)[i].value.u_int = strtoul(token, &rest, 0); + if (*rest != ':') { + Error1("option \"%s\": 3 arguments required", + ent->desc->defname); + } + ++rest; + (*opts)[i].value2.u_int = strtoul(rest, &rest, 0); + if (*rest != ':') { + Error1("option \"%s\": 3 arguments required", + ent->desc->defname); + } + ++rest; + (*opts)[i].value3.u_int = strtoul(rest, &rest, 0); + } + Info4("setting option \"%s\" to %d:%d:%d", ent->desc->defname, + (*opts)[i].value.u_int, (*opts)[i].value2.u_int, (*opts)[i].value3.u_int); + break; + case TYPE_INT_INT_BIN: + if (!assign) { + Error1("option \"%s\": values required", a0); + continue; + } + { + char *rest; + (*opts)[i].value.u_int = strtoul(token, &rest, 0); + if (*rest != ':') { + Error1("option \"%s\": 3 arguments required", + ent->desc->defname); + } + ++rest; + (*opts)[i].value2.u_int = strtoul(rest, &rest, 0); + if (*rest != ':') { + Error1("option \"%s\": 3 arguments required", + ent->desc->defname); + } + ++rest; + optlen = 0; + if ((result = dalan(rest, optbuf, &optlen, sizeof(optbuf))) != 0) { + Error1("parseopts(): problem with \"%s\" data", rest); + continue; + } + if (((*opts)[i].value3.u_bin.b_data = memdup(optbuf, optlen)) == NULL) { + Error1("memdup(, "F_Zu"): out of memory", optlen); + return -1; + } + (*opts)[i].value3.u_bin.b_len = optlen; + } + Info3("setting option \"%s\" to %d:%d:..."/*!!!*/, ent->desc->defname, + (*opts)[i].value.u_int, (*opts)[i].value2.u_int); + break; + case TYPE_INT_INT_STRING: + if (!assign) { + Error1("option \"%s\": values required", a0); + continue; + } + { + char *rest; + (*opts)[i].value.u_int = strtoul(token, &rest, 0); + if (*rest != ':') { + Error1("option \"%s\": 3 arguments required", + ent->desc->defname); + } + ++rest; + (*opts)[i].value2.u_int = strtoul(rest, &rest, 0); + if (*rest != ':') { + Error1("option \"%s\": 3 arguments required", + ent->desc->defname); + } + ++rest; + if (((*opts)[i].value3.u_string = strdup(rest)) == NULL) { + Error("out of memory"); return -1; + } + } + Info4("setting option \"%s\" to %d:%d:\"%s\"", ent->desc->defname, + (*opts)[i].value.u_int, (*opts)[i].value2.u_int, + (*opts)[i].value3.u_int); + break; #if defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN) case TYPE_IP_MREQN: { @@ -3134,6 +3221,53 @@ int applyopts(int fd, struct opt *opts, unsigned int phase) { Error1("ioctl() data type %d not implemented", opt->desc->type); } break; + case OPT_SETSOCKOPT_INT: + switch (opt->desc->type) { + case TYPE_INT_INT_INT: + if (Setsockopt(fd, opt->value.u_int, opt->value2.u_int, + &opt->value3.u_int, sizeof(int)) < 0) { + Error6("setsockopt(%d, %d, %d, {%d}, "F_Zu"): %s", + fd, opt->value.u_int, opt->value2.u_int, + opt->value3.u_int, sizeof(int), strerror(errno)); + } + break; + default: + Error1("setsockopt() data type %d not implemented", + opt->desc->type); + } + break; + case OPT_SETSOCKOPT_BIN: + switch (opt->desc->type) { + case TYPE_INT_INT_BIN: + if (Setsockopt(fd, opt->value.u_int, opt->value2.u_int, + opt->value3.u_bin.b_data, opt->value3.u_bin.b_len) < 0) { + Error5("setsockopt(%d, %d, %d, {...}, "F_Zu"): %s", + fd, opt->value.u_int, opt->value2.u_int, + opt->value3.u_bin.b_len, strerror(errno)); + } + break; + default: + Error1("setsockopt() data type %d not implemented", + opt->desc->type); + } + break; + case OPT_SETSOCKOPT_STRING: + switch (opt->desc->type) { + case TYPE_INT_INT_STRING: + if (Setsockopt(fd, opt->value.u_int, opt->value2.u_int, + opt->value3.u_string, + strlen(opt->value3.u_string)+1) < 0) { + Error6("setsockopt(%d, %d, %d, \"%s\", "F_Zu"): %s", + fd, opt->value.u_int, opt->value2.u_int, + opt->value3.u_string, strlen(opt->value3.u_string)+1, + strerror(errno)); + } + break; + default: + Error1("setsockopt() data type %d not implemented", + opt->desc->type); + } + break; default: Error1("applyopts(): option \"%s\" not implemented", opt->desc->defname); diff --git a/xioopts.h b/xioopts.h index 0981e45..d6d9f59 100644 --- a/xioopts.h +++ b/xioopts.h @@ -56,6 +56,9 @@ enum e_types { TYPE_INT_INT, /* 2 parameters: first is int, second is int */ TYPE_INT_BIN, /* 2 parameters: first is int, second is binary */ TYPE_INT_STRING, /* 2 parameters: first is int, second is req string */ + TYPE_INT_INT_INT, /* 3 params: first and second are int, 3rd is int */ + TYPE_INT_INT_BIN, /* 3 params: first and second are int, 3rd is binary */ + TYPE_INT_INT_STRING, /* 3 params: first and second are int, 3rd is string */ TYPE_2BYTE = TYPE_USHORT } ; @@ -542,6 +545,9 @@ enum e_optcode { OPT_SETGID_EARLY, OPT_SETPGID, OPT_SETSID, + OPT_SETSOCKOPT_BIN, + OPT_SETSOCKOPT_INT, + OPT_SETSOCKOPT_STRING, OPT_SETUID, OPT_SETUID_EARLY, OPT_SIGHUP,