support for php5.6.30
[php-ancillary.git] / php-ancillary.c
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #ifndef _XPG4_2 /* Solaris sucks */
6 # define _XPG4_2
7 #endif
8
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <sys/uio.h>
14 #include <assert.h>
15 #if defined(__FreeBSD__)
16 # include <sys/param.h> /* FreeBSD sucks */
17 #endif
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <stdio.h>
24
25 #include "php.h"
26 #include <php5/main/php_network.h>
27 #include <php5/ext/standard/file.h>
28 #include <ancillary.h>
29
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33  
34 #define PHP_ANCILLARY_VERSION "1.0"
35 #define PHP_ANCILLARY_EXTNAME "ancillary"
36  
37 extern zend_module_entry ancillary_module_entry;
38 #define phpext_ancillary_ptr &ancillary_module_entry
39
40 int
41 ancil_recv_fds_with_buffer(int sock, int *fds, unsigned n_fds, void *buffer)
42 {
43     struct msghdr msghdr;
44     char nothing;
45     struct iovec nothing_ptr;
46     struct cmsghdr *cmsg;
47     int i;
48
49     nothing_ptr.iov_base = &nothing;
50     nothing_ptr.iov_len = 1;
51     msghdr.msg_name = NULL;
52     msghdr.msg_namelen = 0;
53     msghdr.msg_iov = &nothing_ptr;
54     msghdr.msg_iovlen = 1;
55     msghdr.msg_flags = 0;
56     msghdr.msg_control = buffer;
57     msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds;
58     cmsg = CMSG_FIRSTHDR(&msghdr);
59     cmsg->cmsg_len = msghdr.msg_controllen;
60     cmsg->cmsg_level = SOL_SOCKET;
61     cmsg->cmsg_type = SCM_RIGHTS;
62     for(i = 0; i < n_fds; i++)
63         ((int *)CMSG_DATA(cmsg))[i] = -1;
64     
65     if(recvmsg(sock, &msghdr, 0) < 0)
66         return(-1);
67     for(i = 0; i < n_fds; i++)
68         fds[i] = ((int *)CMSG_DATA(cmsg))[i];
69     n_fds = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
70     return(n_fds);
71 }
72
73 int
74 ancil_recv_fds_with_buffer_ext(int sock, int *fds, unsigned n_fds, void *buffer, char *headers, int hsize)
75 {
76     struct msghdr msghdr;
77     char nothing;
78     struct iovec nothing_ptr;
79     struct cmsghdr *cmsg;
80     int i, mop_len;
81
82     nothing_ptr.iov_base = &nothing;
83     nothing_ptr.iov_len = 1;
84     msghdr.msg_name = NULL;
85     msghdr.msg_namelen = 0;
86     msghdr.msg_iov = &nothing_ptr;
87     msghdr.msg_iovlen = 1;
88     msghdr.msg_flags = 0;
89     msghdr.msg_control = buffer;
90     msghdr.msg_controllen = sizeof(struct cmsghdr) + (sizeof(int) * n_fds) + hsize;
91     cmsg = CMSG_FIRSTHDR(&msghdr);
92     cmsg->cmsg_len = msghdr.msg_controllen;
93     cmsg->cmsg_level = SOL_SOCKET;
94     cmsg->cmsg_type = SCM_RIGHTS;
95     for(i = 0; i < n_fds; i++)
96         ((int *)CMSG_DATA(cmsg))[i] = -1;
97     if((mop_len = recvmsg(sock, &msghdr, 0)) < 0)
98         return(-1);
99
100     if (0 == 1) {
101         int mop_fd;
102         char mop_bf[512];
103
104         mop_fd = open("/tmp/fd_recv.log", O_WRONLY | O_APPEND | O_CREAT);
105         sprintf(mop_bf, "LEN: [%d]\n", mop_len);
106         write(mop_fd, mop_bf, strlen(mop_bf));
107         close(mop_fd);
108
109     }
110     for(i = 0; i < n_fds; i++)
111         fds[i] = ((int *)CMSG_DATA(cmsg))[i];
112     // n_fds = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
113     memcpy(headers, (char *)(CMSG_DATA(cmsg) + (sizeof(int) * n_fds)), hsize);
114
115     return(n_fds);
116 }
117
118 #ifndef SPARE_RECV_FDS
119 int
120 ancil_recv_fds(int sock, int *fd, unsigned n_fds)
121 {
122     ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer;
123
124     assert(n_fds <= ANCIL_MAX_N_FDS);
125     return(ancil_recv_fds_with_buffer(sock, fd, n_fds, &buffer));
126 }
127 #endif /* SPARE_RECV_FDS */
128
129 #ifndef SPARE_RECV_FD
130 int
131 ancil_recv_fd(int sock, int *fd)
132 {
133     ANCIL_FD_BUFFER(1) buffer;
134
135     return(ancil_recv_fds_with_buffer(sock, fd, 1, &buffer) == 1 ? 0 : -1);
136 }
137 int
138 ancil_recv_fd_ext(int sock, int *fd, char *headers, int hsize)
139 {
140     ANCIL_FD_BUFFER(1) buffer;
141
142     return(ancil_recv_fds_with_buffer_ext(sock, fd, 1, &buffer, headers, hsize) == 1 ? 0 : -1);
143 }
144 #endif /* SPARE_RECV_FD */
145
146  
147 // declaration of a custom mop_function()
148 PHP_FUNCTION(ancillary_getstream);
149  
150 // list of custom PHP functions provided by this extension
151 // set {NULL, NULL, NULL} as the last record to mark the end of list
152 static
153 #if ZEND_MODULE_API_NO >= 20131226
154  zend_function_entry
155 #elif ZEND_MODULE_API_NO >= 20010901
156  function_entry
157 #endif
158  ancillary_getstream[] = {
159     PHP_FE(ancillary_getstream, NULL)
160     {NULL, NULL, NULL}
161 };
162  
163 // the following code creates an entry for the module and registers it with Zend.
164 zend_module_entry ancillary_module_entry = {
165 #if ZEND_MODULE_API_NO >= 20010901
166     STANDARD_MODULE_HEADER,
167 #endif
168     PHP_ANCILLARY_EXTNAME,
169     ancillary_getstream,
170     NULL, // name of the MINIT function or NULL if not applicable
171     NULL, // name of the MSHUTDOWN function or NULL if not applicable
172     NULL, // name of the RINIT function or NULL if not applicable
173     NULL, // name of the RSHUTDOWN function or NULL if not applicable
174     NULL, // name of the MINFO function or NULL if not applicable
175 #if ZEND_MODULE_API_NO >= 20010901
176     PHP_ANCILLARY_VERSION,
177 #endif
178     STANDARD_MODULE_PROPERTIES
179 };
180  
181 ZEND_GET_MODULE(ancillary)
182
183 #define CTRL_BUFF_MAX_SZ (8*1024)
184  
185 // starting from a unix socket receive a socket from an external process
186 PHP_FUNCTION(ancillary_getstream)
187 {
188     zval *zstream;
189     php_stream *stream;
190     int fd_in, fd_out[2], retrecv, curpos = 0;
191     char *headers;
192     zval *zheaders;
193     php_netstream_data_t *sock;
194
195     if ((headers = calloc(CTRL_BUFF_MAX_SZ, 1)) == NULL) {
196         return;
197     }
198
199     if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz", &zstream, &zheaders)) {
200         free(headers);
201         return;
202     }
203     
204     php_stream_from_zval(stream, &zstream);
205
206     if (php_stream_cast(stream, PHP_STREAM_AS_FD, (void*)&fd_in, REPORT_ERRORS) == FAILURE) {
207         free(headers);
208         RETURN_FALSE;
209     }
210
211     if(ancil_recv_fds(fd_in, fd_out, 2) == 0) {
212         free(headers);
213         RETURN_FALSE;
214     }
215
216     {
217         char bf[2048];
218
219         sprintf(bf, "zero: [%d]    uno: [%d]\n", fd_out[0], fd_out[1]);
220         write(1, bf, strlen(bf));
221     }
222     while(1) {
223         write(1, "LOOP\n", 5);
224         retrecv = recv(fd_out[1], &(headers[curpos]), CTRL_BUFF_MAX_SZ - curpos - 1, 0);
225         if (retrecv < 0) {
226             free(headers);
227             RETURN_FALSE;
228         }
229         else if (retrecv == 0) {
230             break;
231         }
232         else {
233             char bf[1024];
234             curpos += retrecv;
235             sprintf(bf, "CURPOS: %d\n", curpos);
236             write(1, bf, strlen(bf));
237             if (curpos == CTRL_BUFF_MAX_SZ - 1) {
238                 free(headers);
239                 RETURN_FALSE;
240             }
241         }
242     }
243     shutdown(fd_out[1], SHUT_RDWR);
244     close(fd_out[1]);
245     write(1, "HEADERS[", 8);
246     write(1, headers, curpos);
247     headers[curpos] = '\0';
248     write(1, "]\n", 2);
249     ZVAL_STRING(zheaders, headers, 1);
250     free(headers);
251
252     if (0 == 1) {
253         int fh;
254
255         if ((fh = open("/tmp/out_php-anc.txt", O_WRONLY | O_CREAT | O_APPEND)) > -1) {
256             write(fh, headers, curpos);
257             close(fh);
258         }
259     }
260
261     sock = pemalloc(sizeof(php_netstream_data_t), 0);
262     memset(sock, 0, sizeof(php_netstream_data_t));
263
264     sock->is_blocked = 1;
265     sock->timeout.tv_sec = FG(default_socket_timeout);
266     sock->timeout.tv_usec = 0;
267
268     /* we don't know the socket until we have determined if we are binding or
269      * connecting */
270     sock->socket = fd_out[0];
271
272     stream = php_stream_alloc_rel(&php_stream_socket_ops, sock, NULL, "r+");
273
274     if (stream == NULL) {
275         pefree(sock, 0);
276         return;
277     }
278     php_stream_to_zval(stream, return_value);
279 }
280