ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/IO-FDPass/FDPass.xs
Revision: 1.9
Committed: Tue Sep 6 10:49:13 2022 UTC (19 months, 4 weeks ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.8: +29 -7 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #ifdef __sun
2 #define _XOPEN_SOURCE 1
3 #define _XOPEN_SOURCE_EXTENDED 1
4 #define __EXTENSIONS__ 1
5 #endif
6
7 #include "EXTERN.h"
8 #include "perl.h"
9 #include "XSUB.h"
10
11 #if WIN32
12
13 /* perl probably did this already */
14 #include <windows.h>
15
16 #elif __CYGWIN__
17
18 #include <windows.h>
19 #include <io.h>
20 #include <sys/cygwin.h>
21
22 #define _open_osfhandle(h,m) cygwin_attach_handle_to_fd ("/dev/tcp", -1, (HANDLE)h, 1, GENERIC_READ | GENERIC_WRITE)
23 typedef int SOCKET;
24
25 #else
26
27 #include <stddef.h> // needed by broken bsds for NULL used in sys/uio.h
28 #include <stdlib.h>
29 #include <errno.h>
30
31 /* send_fd/recv_fd taken from libptytty */
32 #include <sys/types.h>
33 #include <sys/uio.h>
34 #include <sys/socket.h>
35
36 #ifndef CMSG_SPACE
37 # define CMSG_SPACE(len) (sizeof (struct cmsghdr) + len)
38 #endif
39
40 #ifndef CMSG_LEN
41 # define CMSG_LEN(len) (sizeof (struct cmsghdr) + len)
42 #endif
43
44 #endif
45
46 #if defined(WIN32)
47 /* the rub is this: win32 doesn't seem to have a way to query whether a socket */
48 /* is non-blocking or not. so we assume it is blocking, make it so if it isn't */
49 /* and reset it afterwards */
50 static int
51 rw (int wr, int fd, char *buf, int len)
52 {
53 u_long nbio = 0;
54 int got = 0;
55
56 while (got != len)
57 {
58 int sze = wr
59 ? send ((SOCKET)fd, buf, len - got, 0) /* we assume send and recv are macros with arguments */
60 : recv ((SOCKET)fd, buf, len - got, 0); /* to be on the safe side */
61
62 if (sze < 0)
63 {
64 if (errno == EAGAIN || errno == WSAEWOULDBLOCK)
65 {
66 ioctl (fd, FIONBIO, (void *)&nbio);
67 nbio = 1;
68 }
69 else
70 break;
71 }
72 else if (sze == 0)
73 break;
74 else
75 got += sze;
76 }
77
78 if (nbio)
79 ioctl (fd, FIONBIO, (void *)&nbio);
80
81 return got == len;
82 }
83 #endif
84
85 static int
86 fd_send (int socket, int fd)
87 {
88 #if defined(WIN32)
89 DWORD pid;
90 HANDLE hdl;
91
92 pid = GetCurrentProcessId ();
93
94 if (!rw (1, socket, (char *)&pid, sizeof (pid)))
95 return 0;
96
97 errno = EBADF;
98 if (!DuplicateHandle ((HANDLE)-1, (HANDLE)_get_osfhandle (fd), (HANDLE)-1, &hdl, 0, FALSE, DUPLICATE_SAME_ACCESS))
99 return 0;
100
101 if (!rw (1, socket, (char *)&hdl, sizeof (hdl)))
102 {
103 CloseHandle (hdl);
104 return 0;
105 }
106
107 return 1;
108
109 #else
110 void *buf = malloc (CMSG_SPACE (sizeof (int)));
111
112 if (!buf)
113 return 0;
114
115 struct msghdr msg;
116 struct iovec iov;
117 struct cmsghdr *cmsg;
118 char data = 0;
119
120 iov.iov_base = &data;
121 iov.iov_len = 1;
122
123 msg.msg_name = 0;
124 msg.msg_namelen = 0;
125 msg.msg_iov = &iov;
126 msg.msg_iovlen = 1;
127 msg.msg_control = buf;
128 msg.msg_controllen = CMSG_SPACE (sizeof (int));
129
130 cmsg = CMSG_FIRSTHDR (&msg);
131 cmsg->cmsg_level = SOL_SOCKET;
132 cmsg->cmsg_type = SCM_RIGHTS;
133 cmsg->cmsg_len = CMSG_LEN (sizeof (int));
134
135 *(int *)CMSG_DATA (cmsg) = fd;
136
137 ssize_t result = sendmsg (socket, &msg, 0);
138
139 free (buf);
140
141 return result >= 0;
142 #endif
143 }
144
145 static int
146 fd_recv (int socket)
147 {
148 #if defined(WIN32)
149 DWORD pid;
150 HANDLE source, rhd, lhd;
151
152 if (!rw (0, socket, (char *)&pid, sizeof (pid)))
153 return -1;
154
155 if (!rw (0, socket, (char *)&rhd, sizeof (rhd)))
156 return -1;
157
158 source = OpenProcess (PROCESS_DUP_HANDLE, FALSE, pid);
159 errno = EACCES;
160 if (!source)
161 return -1;
162
163 pid = DuplicateHandle (source, rhd, (HANDLE)-1, &lhd,
164 0, FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
165
166 CloseHandle (source);
167
168 errno = EBADF;
169 if (!pid)
170 return -1;
171
172 return _open_osfhandle ((intptr_t)lhd, 0);
173 #else
174 void *buf = malloc (CMSG_SPACE (sizeof (int)));
175
176 if (!buf)
177 return -1;
178
179 struct msghdr msg;
180 struct iovec iov;
181 char data = 1;
182
183 iov.iov_base = &data;
184 iov.iov_len = 1;
185
186 msg.msg_name = 0;
187 msg.msg_namelen = 0;
188 msg.msg_iov = &iov;
189 msg.msg_iovlen = 1;
190 msg.msg_control = buf;
191 msg.msg_controllen = CMSG_SPACE (sizeof (int));
192
193 if (recvmsg (socket, &msg, 0) <= 0)
194 {
195 free (buf);
196 return -1;
197 }
198
199 int fd = -1;
200
201 struct cmsghdr *cmsg = CMSG_FIRSTHDR (&msg);
202
203 if (cmsg)
204 {
205 // some operating systems (i.e. osx) allow msg->cmsg_len to be larger than
206 // msg.msg_controllen, so limit the size here
207 if (cmsg->cmsg_len > CMSG_SPACE (sizeof (int)))
208 cmsg->cmsg_len = CMSG_SPACE (sizeof (int));
209
210 if ( cmsg->cmsg_level == SOL_SOCKET
211 && cmsg->cmsg_type == SCM_RIGHTS
212 && cmsg->cmsg_len >= CMSG_LEN (sizeof (int)))
213 {
214 // close any extra fd's that might have been passed.
215 // this does not work around osx/freebsad bugs where a malicious sender
216 // can send usw more fds than we can receive, leaking the extra fds,
217 // which must be fixed in the kernel, really.
218 for (fd = 1; cmsg->cmsg_len >= CMSG_LEN (sizeof (int) * (fd + 1)); ++fd)
219 close (((int *)CMSG_DATA (cmsg))[fd]);
220
221 fd = *(int *)CMSG_DATA (cmsg);
222 }
223 }
224
225 free (buf);
226
227 if (data != 0)
228 {
229 close (fd);
230 fd = -1;
231 }
232
233 errno = EDOM;
234
235 return fd;
236 #endif
237 }
238
239 MODULE = IO::FDPass PACKAGE = IO::FDPass PREFIX = fd_
240
241 PROTOTYPES: DISABLE
242
243 int
244 fd_send (int socket, int fd)
245
246 int
247 fd_recv (int socket)
248