(root)/
glibc-2.38/
sysdeps/
mach/
hurd/
recvmsg.c
       1  /* Copyright (C) 2001-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 License as
       6     published by the Free Software Foundation; either version 2.1 of the
       7     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; see the file COPYING.LIB.  If
      16     not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <errno.h>
      19  #include <string.h>
      20  #include <sys/socket.h>
      21  
      22  #include <hurd.h>
      23  #include <hurd/fd.h>
      24  #include <hurd/socket.h>
      25  #include <sysdep-cancel.h>
      26  
      27  /* Receive a message as described by MESSAGE from socket FD.
      28     Returns the number of bytes read or -1 for errors.  */
      29  ssize_t
      30  __libc_recvmsg (int fd, struct msghdr *message, int flags)
      31  {
      32    error_t err;
      33    addr_port_t aport;
      34    char *data = NULL;
      35    mach_msg_type_number_t len = 0;
      36    mach_port_t *ports, *newports = NULL;
      37    mach_msg_type_number_t nports = 0;
      38    struct cmsghdr *cmsg;
      39    char *cdata = NULL;
      40    mach_msg_type_number_t clen = 0;
      41    size_t amount;
      42    char *buf;
      43    int nfds, *opened_fds = NULL;
      44    int i, ii, j;
      45    int newfds;
      46    int cancel_oldtype;
      47  
      48    error_t reauthenticate (mach_port_t port, mach_port_t *result)
      49      {
      50        error_t err;
      51        mach_port_t ref;
      52        ref = __mach_reply_port ();
      53        int cancel_oldtype;
      54  
      55        cancel_oldtype = LIBC_CANCEL_ASYNC();
      56        do
      57  	err = __io_reauthenticate (port, ref, MACH_MSG_TYPE_MAKE_SEND);
      58        while (err == EINTR);
      59        if (!err)
      60  	do
      61  	  err = __USEPORT_CANCEL (AUTH, __auth_user_authenticate (port,
      62  					  ref, MACH_MSG_TYPE_MAKE_SEND,
      63  					  result));
      64  	while (err == EINTR);
      65        LIBC_CANCEL_RESET (cancel_oldtype);
      66  
      67        __mach_port_destroy (__mach_task_self (), ref);
      68        return err;
      69      }
      70  
      71    /* Find the total number of bytes to be read.  */
      72    amount = 0;
      73    for (i = 0; i < message->msg_iovlen; i++)
      74      {
      75        amount += message->msg_iov[i].iov_len;
      76  
      77        /* As an optimization, we set the initial values of DATA and LEN
      78           from the first non-empty iovec.  This kicks-in in the case
      79           where the whole packet fits into that iovec buffer.  */
      80        if (data == NULL && message->msg_iov[i].iov_len > 0)
      81  	{
      82  	  data = message->msg_iov[i].iov_base;
      83  	  len = message->msg_iov[i].iov_len;
      84  	}
      85      }
      86  
      87    buf = data;
      88    cancel_oldtype = LIBC_CANCEL_ASYNC();
      89    err = HURD_DPORT_USE_CANCEL (fd, __socket_recv (port, &aport,
      90  						  flags, &data, &len,
      91  						  &ports, &nports,
      92  						  &cdata, &clen,
      93  						  &message->msg_flags, amount));
      94    LIBC_CANCEL_RESET (cancel_oldtype);
      95    if (err)
      96      return __hurd_sockfail (fd, flags, err);
      97  
      98    if (message->msg_name != NULL && aport != MACH_PORT_NULL)
      99      {
     100        char *buf = message->msg_name;
     101        mach_msg_type_number_t buflen = message->msg_namelen;
     102        int type;
     103  
     104        cancel_oldtype = LIBC_CANCEL_ASYNC();
     105        err = __socket_whatis_address (aport, &type, &buf, &buflen);
     106        LIBC_CANCEL_RESET (cancel_oldtype);
     107  
     108        if (err == EOPNOTSUPP)
     109  	/* If the protocol server can't tell us the address, just return a
     110  	   zero-length one.  */
     111  	{
     112  	  buf = message->msg_name;
     113  	  buflen = 0;
     114  	  err = 0;
     115  	}
     116  
     117        if (err)
     118  	{
     119  	  __mach_port_deallocate (__mach_task_self (), aport);
     120  	  return __hurd_sockfail (fd, flags, err);
     121  	}
     122  
     123        if (message->msg_namelen > buflen)
     124  	message->msg_namelen = buflen;
     125  
     126        if (buf != message->msg_name)
     127  	{
     128  	  memcpy (message->msg_name, buf, message->msg_namelen);
     129  	  __vm_deallocate (__mach_task_self (), (vm_address_t) buf, buflen);
     130  	}
     131  
     132        if (buflen > 0)
     133  	((struct sockaddr *) message->msg_name)->sa_family = type;
     134      }
     135    else if (message->msg_name != NULL)
     136      message->msg_namelen = 0;
     137  
     138    if (MACH_PORT_VALID (aport))
     139      __mach_port_deallocate (__mach_task_self (), aport);
     140  
     141    if (buf == data)
     142      buf += len;
     143    else
     144      {
     145        /* Copy the data into MSG.  */
     146        if (len > amount)
     147  	message->msg_flags |= MSG_TRUNC;
     148        else
     149  	amount = len;
     150  
     151        buf = data;
     152        for (i = 0; i < message->msg_iovlen; i++)
     153  	{
     154  #define min(a, b)	((a) > (b) ? (b) : (a))
     155  	  size_t copy = min (message->msg_iov[i].iov_len, amount);
     156  
     157  	  memcpy (message->msg_iov[i].iov_base, buf, copy);
     158  
     159  	  buf += copy;
     160  	  amount -= copy;
     161  	  if (len == 0)
     162  	    break;
     163  	}
     164  
     165        __vm_deallocate (__mach_task_self (), (vm_address_t) data, len);
     166      }
     167  
     168    /* Copy the control message into MSG.  */
     169    if (clen > message->msg_controllen)
     170      message->msg_flags |= MSG_CTRUNC;
     171    else
     172      message->msg_controllen = clen;
     173    memcpy (message->msg_control, cdata, message->msg_controllen);
     174  
     175    if (nports > 0)
     176      {
     177        newports = __alloca (nports * sizeof (mach_port_t));
     178        opened_fds = __alloca (nports * sizeof (int));
     179      }
     180  
     181    /* This counts how many ports we processed completely.  */
     182    i = 0;
     183    /* This counts how many new fds we create.  */
     184    newfds = 0;
     185  
     186    for (cmsg = CMSG_FIRSTHDR (message);
     187         cmsg;
     188         cmsg = CMSG_NXTHDR (message, cmsg))
     189    {
     190      if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
     191        {
     192  	/* SCM_RIGHTS support.  */
     193  	/* The fd's flags are passed in the control data.  */
     194  	int *fds = (int *) CMSG_DATA (cmsg);
     195  	nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
     196  	       / sizeof (int);
     197  
     198  	for (j = 0; j < nfds; j++)
     199  	  {
     200  	    int fd_flags = (flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0;
     201  	    err = reauthenticate (ports[i], &newports[newfds]);
     202  	    if (err)
     203  	      goto cleanup;
     204  	    /* We do not currently take any flag from the sender.  */
     205  	    fds[j] = opened_fds[newfds] = _hurd_intern_fd (newports[newfds],
     206  							   (fds[j] & 0)
     207  							   | fd_flags,
     208  							   0);
     209  	    if (fds[j] == -1)
     210  	      {
     211  		err = errno;
     212  		__mach_port_deallocate (__mach_task_self (), newports[newfds]);
     213  		goto cleanup;
     214  	      }
     215  	    i++;
     216  	    newfds++;
     217  	  }
     218        }
     219    }
     220  
     221    for (i = 0; i < nports; i++)
     222      __mach_port_deallocate (mach_task_self (), ports[i]);
     223  
     224    __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen);
     225  
     226    return (buf - data);
     227  
     228  cleanup:
     229    /* Clean up all the file descriptors from port 0 to i-1.  */
     230    if (nports > 0)
     231      {
     232        ii = 0;
     233        newfds = 0;
     234        for (cmsg = CMSG_FIRSTHDR (message);
     235  	   cmsg;
     236  	   cmsg = CMSG_NXTHDR (message, cmsg))
     237  	{
     238  	  if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
     239  	    {
     240  	      nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
     241  		     / sizeof (int);
     242  	      for (j = 0; j < nfds && ii < i; j++, ii++, newfds++)
     243  	      {
     244  		_hurd_fd_close (_hurd_fd_get (opened_fds[newfds]));
     245  		__mach_port_deallocate (__mach_task_self (), newports[newfds]);
     246  		__mach_port_deallocate (__mach_task_self (), ports[ii]);
     247  	      }
     248  	    }
     249  	}
     250      }
     251  
     252    __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen);
     253    return __hurd_fail (err);
     254  }
     255  
     256  weak_alias (__libc_recvmsg, recvmsg)
     257  weak_alias (__libc_recvmsg, __recvmsg)