1  /* Copyright (C) 2015-2023 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
      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
      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
      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