1 /* Copyright (C) 2015-2021 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #include <sys/socket.h>
19 #include <time.h>
20 #include <sysdep.h>
21 #include <socketcall.h>
22 #include <socket-constants-time64.h>
23 
24 static int
getsockopt_syscall(int fd,int level,int optname,void * optval,socklen_t * len)25 getsockopt_syscall (int fd, int level, int optname, void *optval,
26 		    socklen_t *len)
27 {
28 #ifdef __ASSUME_GETSOCKOPT_SYSCALL
29   return INLINE_SYSCALL (getsockopt, 5, fd, level, optname, optval, len);
30 #else
31   return SOCKETCALL (getsockopt, fd, level, optname, optval, len);
32 #endif
33 }
34 
35 #ifndef __ASSUME_TIME64_SYSCALLS
36 static int
getsockopt32(int fd,int level,int optname,void * optval,socklen_t * len)37 getsockopt32 (int fd, int level, int optname, void *optval,
38 	      socklen_t *len)
39 {
40   int r = -1;
41 
42   if (level != SOL_SOCKET)
43     return r;
44 
45   switch (optname)
46     {
47     case COMPAT_SO_RCVTIMEO_NEW:
48     case COMPAT_SO_SNDTIMEO_NEW:
49       {
50 	if (optname == COMPAT_SO_RCVTIMEO_NEW)
51 	  optname = COMPAT_SO_RCVTIMEO_OLD;
52 	if (optname == COMPAT_SO_SNDTIMEO_NEW)
53 	  optname = COMPAT_SO_SNDTIMEO_OLD;
54 
55 	struct __timeval32 tv32;
56 	r = getsockopt_syscall (fd, level, optname, &tv32,
57 				(socklen_t[]) { sizeof tv32 });
58 	if (r < 0)
59 	  break;
60 
61 	/* POSIX states that if the size of the option value is greater than
62 	   then option length, the option value argument shall be silently
63 	   truncated.  */
64 	if (*len >= sizeof (struct __timeval64))
65 	  {
66 	    struct __timeval64 *tv64 = (struct __timeval64 *) optval;
67 	    *tv64 = valid_timeval32_to_timeval64 (tv32);
68 	    *len = sizeof (*tv64);
69 	  }
70 	else
71 	  memcpy (optval, &tv32, sizeof tv32);
72       }
73       break;
74 
75     case COMPAT_SO_TIMESTAMP_NEW:
76     case COMPAT_SO_TIMESTAMPNS_NEW:
77       {
78 	if (optname == COMPAT_SO_TIMESTAMP_NEW)
79 	  optname = COMPAT_SO_TIMESTAMP_OLD;
80 	if (optname == COMPAT_SO_TIMESTAMPNS_NEW)
81 	  optname = COMPAT_SO_TIMESTAMPNS_OLD;
82 	r = getsockopt_syscall (fd, level, optname, optval, len);
83       }
84       break;
85     }
86 
87   return r;
88 }
89 #endif
90 
91 int
__getsockopt(int fd,int level,int optname,void * optval,socklen_t * len)92 __getsockopt (int fd, int level, int optname, void *optval, socklen_t *len)
93 {
94   int r = getsockopt_syscall (fd, level, optname, optval, len);
95 
96 #ifndef __ASSUME_TIME64_SYSCALLS
97   if (r == -1 && errno == ENOPROTOOPT)
98     r = getsockopt32 (fd, level, optname, optval, len);
99 #endif
100 
101  return r;
102 }
103 weak_alias (__getsockopt, getsockopt)
104 #if __TIMESIZE != 64
105 weak_alias (__getsockopt, __getsockopt64)
106 #endif
107