1 /* Copyright (C) 2001-2022 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
__libc_sendmsg(int fd,const struct msghdr * message,int flags)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   mach_msg_type_number_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 	    {
86 	      __set_errno (err);
87 	      return -1;
88 	    }
89 	  dealloc = 1;
90 	}
91       else
92 	data.ptr = data_buf;
93 
94       /* Copy the data into DATA.  */
95       to_copy = len;
96       buf = data.ptr;
97       for (i = 0; i < len; i++)
98 	{
99 #define	min(a, b)	((a) > (b) ? (b) : (a))
100 	  size_t copy = min (message->msg_iov[i].iov_len, to_copy);
101 
102 	  buf = __mempcpy (buf, message->msg_iov[i].iov_base, copy);
103 
104 	  to_copy -= copy;
105 	  if (to_copy == 0)
106 	    break;
107 	}
108     }
109 
110   /* Allocate enough room for ports.  */
111   cmsg = CMSG_FIRSTHDR (message);
112   for (; cmsg; cmsg = CMSG_NXTHDR ((struct msghdr *) message, cmsg))
113     if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
114       nports += (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
115 		/ sizeof (int);
116 
117   if (nports)
118     ports = __alloca (nports * sizeof (mach_port_t));
119 
120   nports = 0;
121   for (cmsg = CMSG_FIRSTHDR (message);
122        cmsg;
123        cmsg = CMSG_NXTHDR ((struct msghdr *) message, cmsg))
124     {
125       if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
126 	{
127 	  /* SCM_RIGHTS support: send FDs.   */
128 	  fds = (int *) CMSG_DATA (cmsg);
129 	  nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr)))
130 		 / sizeof (int);
131 
132 	  for (i = 0; i < nfds; i++)
133 	    {
134 	      err = HURD_DPORT_USE
135 		(fds[i],
136 		 ({
137 		   err = __io_restrict_auth (port, &ports[nports],
138 					     0, 0, 0, 0);
139 		   if (! err)
140 		     nports++;
141 		   /* We pass the flags in the control data.  */
142 		   fds[i] = descriptor->flags;
143 		   err;
144 		 }));
145 
146 	      if (err)
147 		goto out;
148 	    }
149 	}
150     }
151 
152   if (addr)
153     {
154       if (addr->sun_family == AF_LOCAL)
155 	{
156 	  char *name = _hurd_sun_path_dupa (addr, addr_len);
157 	  /* For the local domain, we must look up the name as a file
158 	     and talk to it with the ifsock protocol.  */
159 	  file_t file = __file_name_lookup (name, 0, 0);
160 	  if (file == MACH_PORT_NULL)
161 	    {
162 	      err = errno;
163 	      goto out;
164 	    }
165 	  err = __ifsock_getsockaddr (file, &aport);
166 	  __mach_port_deallocate (__mach_task_self (), file);
167 	  if (err == MIG_BAD_ID || err == EOPNOTSUPP)
168 	    /* The file did not grok the ifsock protocol.  */
169 	    err = ENOTSOCK;
170 	  if (err)
171 	    goto out;
172 	}
173       else
174 	err = EIEIO;
175     }
176 
177   err = HURD_DPORT_USE_CANCEL (fd,
178 			({
179 			  if (err)
180 			    err = __socket_create_address (port,
181 							   addr->sun_family,
182 							   (char *) addr,
183 							   addr_len,
184 							   &aport);
185 			  if (! err)
186 			    {
187 			      /* Send the data.  */
188 			      int cancel_oldtype = LIBC_CANCEL_ASYNC();
189 			      err = __socket_send (port, aport,
190 						   flags, data.ptr, len,
191 						   ports,
192 						   MACH_MSG_TYPE_COPY_SEND,
193 						   nports,
194 						   message->msg_control,
195 						   message->msg_controllen,
196 						   &amount);
197 			      LIBC_CANCEL_RESET (cancel_oldtype);
198 			      __mach_port_deallocate (__mach_task_self (),
199 						      aport);
200 			    }
201 			  err;
202 			}));
203   socketrpc = 1;
204 
205  out:
206   for (i = 0; i < nports; i++)
207     __mach_port_deallocate (__mach_task_self (), ports[i]);
208 
209   if (dealloc)
210     __vm_deallocate (__mach_task_self (), data.addr, len);
211 
212   if (socketrpc)
213     return err ? __hurd_sockfail (fd, flags, err) : amount;
214   else
215     return __hurd_fail (err);
216 }
217 
218 weak_alias (__libc_sendmsg, sendmsg)
219 weak_alias (__libc_sendmsg, __sendmsg)
220