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  #include <sys/un.h>
      22  
      23  #include <hurd.h>
      24  #include <hurd/fd.h>
      25  #include <hurd/ifsock.h>
      26  #include <hurd/socket.h>
      27  #include <sysdep-cancel.h>
      28  #include "hurd/hurdsocket.h"
      29  
      30  /* Send a message described MESSAGE on socket FD.
      31     Returns the number of bytes sent, or -1 for errors.  */
      32  ssize_t
      33  __libc_sendmsg (int fd, const struct msghdr *message, int flags)
      34  {
      35    error_t err = 0;
      36    struct cmsghdr *cmsg;
      37    mach_port_t *ports = NULL;
      38    mach_msg_type_number_t nports = 0;
      39    int *fds, nfds;
      40    struct sockaddr_un *addr = message->msg_name;
      41    socklen_t addr_len = message->msg_namelen;
      42    addr_port_t aport = MACH_PORT_NULL;
      43    union
      44    {
      45      char *ptr;
      46      vm_address_t addr;
      47    } data = { .ptr = NULL };
      48    char data_buf[2048];
      49    mach_msg_type_number_t len;
      50    vm_size_t amount;
      51    int dealloc = 0;
      52    int socketrpc = 0;
      53    int i;
      54  
      55    /* Find the total number of bytes to be written.  */
      56    len = 0;
      57    for (i = 0; i < message->msg_iovlen; i++)
      58      {
      59        if (message->msg_iov[i].iov_len > 0)
      60  	{
      61  	  /* As an optimization, if we only have a single non-empty
      62               iovec, we set DATA and LEN from it.  */
      63  	  if (len == 0)
      64  	    data.ptr = message->msg_iov[i].iov_base;
      65  	  else
      66  	    data.ptr = NULL;
      67  
      68  	  len += message->msg_iov[i].iov_len;
      69  	}
      70      }
      71  
      72    if (data.ptr == NULL)
      73      {
      74        size_t to_copy;
      75        char *buf;
      76  
      77        /* Allocate a temporary buffer to hold the data.  For small
      78           amounts of data, we allocate a buffer on the stack.  Larger
      79           amounts of data are stored in a page-aligned buffer.  The
      80           limit of 2048 bytes is inspired by the MiG stubs.  */
      81        if (len > 2048)
      82  	{
      83  	  err = __vm_allocate (__mach_task_self (), &data.addr, len, 1);
      84  	  if (err)
      85  	    return __hurd_fail (err);
      86  	  dealloc = 1;
      87  	}
      88        else
      89  	data.ptr = data_buf;
      90  
      91        /* Copy the data into DATA.  */
      92        to_copy = len;
      93        buf = data.ptr;
      94        for (i = 0; i < len; i++)
      95  	{
      96  #define	min(a, b)	((a) > (b) ? (b) : (a))
      97  	  size_t copy = min (message->msg_iov[i].iov_len, to_copy);
      98  
      99  	  buf = __mempcpy (buf, message->msg_iov[i].iov_base, copy);
     100  
     101  	  to_copy -= copy;
     102  	  if (to_copy == 0)
     103  	    break;
     104  	}
     105      }
     106  
     107    /* Allocate enough room for ports.  */
     108    cmsg = CMSG_FIRSTHDR (message);
     109    for (; cmsg; cmsg = CMSG_NXTHDR ((struct msghdr *) message, cmsg))
     110      if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
     111        nports += (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
     112  		/ sizeof (int);
     113  
     114    if (nports)
     115      ports = __alloca (nports * sizeof (mach_port_t));
     116  
     117    nports = 0;
     118    for (cmsg = CMSG_FIRSTHDR (message);
     119         cmsg;
     120         cmsg = CMSG_NXTHDR ((struct msghdr *) message, cmsg))
     121      {
     122        if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
     123  	{
     124  	  /* SCM_RIGHTS support: send FDs.   */
     125  	  fds = (int *) CMSG_DATA (cmsg);
     126  	  nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
     127  		 / sizeof (int);
     128  
     129  	  for (i = 0; i < nfds; i++)
     130  	    {
     131  	      err = HURD_DPORT_USE
     132  		(fds[i],
     133  		 ({
     134  		   err = __io_restrict_auth (port, &ports[nports],
     135  					     0, 0, 0, 0);
     136  		   if (! err)
     137  		     nports++;
     138  		   /* We do not currently have flags to pass.  */
     139  		   fds[i] = 0;
     140  		   err;
     141  		 }));
     142  
     143  	      if (err)
     144  		goto out;
     145  	    }
     146  	}
     147      }
     148  
     149    if (addr)
     150      {
     151        if (addr->sun_family == AF_LOCAL)
     152  	{
     153  	  char *name = _hurd_sun_path_dupa (addr, addr_len);
     154  	  /* For the local domain, we must look up the name as a file
     155  	     and talk to it with the ifsock protocol.  */
     156  	  file_t file = __file_name_lookup (name, 0, 0);
     157  	  if (file == MACH_PORT_NULL)
     158  	    {
     159  	      err = errno;
     160  	      goto out;
     161  	    }
     162  	  err = __ifsock_getsockaddr (file, &aport);
     163  	  __mach_port_deallocate (__mach_task_self (), file);
     164  	  if (err == MIG_BAD_ID || err == EOPNOTSUPP)
     165  	    /* The file did not grok the ifsock protocol.  */
     166  	    err = ENOTSOCK;
     167  	  if (err)
     168  	    goto out;
     169  	}
     170        else
     171  	err = EIEIO;
     172      }
     173  
     174    err = HURD_DPORT_USE_CANCEL (fd,
     175  			({
     176  			  if (err)
     177  			    err = __socket_create_address (port,
     178  							   addr->sun_family,
     179  							   (char *) addr,
     180  							   addr_len,
     181  							   &aport);
     182  			  if (! err)
     183  			    {
     184  			      /* Send the data.  */
     185  			      int cancel_oldtype = LIBC_CANCEL_ASYNC();
     186  			      err = __socket_send (port, aport,
     187  						   flags, data.ptr, len,
     188  						   ports,
     189  						   MACH_MSG_TYPE_COPY_SEND,
     190  						   nports,
     191  						   message->msg_control,
     192  						   message->msg_controllen,
     193  						   &amount);
     194  			      LIBC_CANCEL_RESET (cancel_oldtype);
     195  			      if (MACH_PORT_VALID (aport))
     196  				__mach_port_deallocate (__mach_task_self (),
     197  							aport);
     198  			    }
     199  			  err;
     200  			}));
     201    socketrpc = 1;
     202  
     203   out:
     204    for (i = 0; i < nports; i++)
     205      __mach_port_deallocate (__mach_task_self (), ports[i]);
     206  
     207    if (dealloc)
     208      __vm_deallocate (__mach_task_self (), data.addr, len);
     209  
     210    if (socketrpc)
     211      return err ? __hurd_sockfail (fd, flags, err) : amount;
     212    else
     213      return __hurd_fail (err);
     214  }
     215  
     216  weak_alias (__libc_sendmsg, sendmsg)
     217  weak_alias (__libc_sendmsg, __sendmsg)