diff options
Diffstat (limited to 'src/network/setsockopt.c')
-rw-r--r-- | src/network/setsockopt.c | 32 |
1 files changed, 31 insertions, 1 deletions
diff --git a/src/network/setsockopt.c b/src/network/setsockopt.c index c960c9ca..2c188a96 100644 --- a/src/network/setsockopt.c +++ b/src/network/setsockopt.c @@ -1,7 +1,37 @@ #include <sys/socket.h> +#include <sys/time.h> +#include <errno.h> #include "syscall.h" +#define IS32BIT(x) !((x)+0x80000000ULL>>32) +#define CLAMP(x) (int)(IS32BIT(x) ? (x) : 0x7fffffffU+((0ULL+(x))>>63)) + int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) { - return socketcall(setsockopt, fd, level, optname, optval, optlen, 0); + const struct timeval *tv; + time_t s; + suseconds_t us; + + int r = __socketcall(setsockopt, fd, level, optname, optval, optlen, 0); + + if (r==-ENOPROTOOPT) switch (level) { + case SOL_SOCKET: + switch (optname) { + case SO_RCVTIMEO: + case SO_SNDTIMEO: + if (SO_RCVTIMEO == SO_RCVTIMEO_OLD) break; + if (optlen < sizeof *tv) return __syscall_ret(-EINVAL); + tv = optval; + s = tv->tv_sec; + us = tv->tv_usec; + if (!IS32BIT(s)) return __syscall_ret(-ENOTSUP); + + if (optname==SO_RCVTIMEO) optname=SO_RCVTIMEO_OLD; + if (optname==SO_SNDTIMEO) optname=SO_SNDTIMEO_OLD; + + r = __socketcall(setsockopt, fd, level, optname, + ((long[]){s, CLAMP(us)}), 2*sizeof(long), 0); + } + } + return __syscall_ret(r); } |