]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy_fdpass.c
Cleanup effort in prep for GA push:
[apache] / modules / proxy / mod_proxy_fdpass.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "mod_proxy.h"
18
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <sys/un.h>
22
23 #ifndef CMSG_DATA
24 #error This module only works on unix platforms with the correct OS support
25 #endif
26
27 #include "apr_version.h"
28 #if APR_MAJOR_VERSION < 2
29 /* for apr_wait_for_io_or_timeout */
30 #include "apr_support.h"
31 #endif
32
33 #include "mod_proxy_fdpass.h"
34
35 module AP_MODULE_DECLARE_DATA proxy_fdpass_module;
36
37 static int proxy_fdpass_canon(request_rec *r, char *url)
38 {
39     const char *path;
40
41     if (strncasecmp(url, "fd://", 5) == 0) {
42         url += 5;
43     }
44     else {
45         return DECLINED;
46     }
47
48     path = ap_server_root_relative(r->pool, url);
49
50     r->filename = apr_pstrcat(r->pool, "proxy:fd://", path, NULL);
51
52     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
53                   "proxy: FD: set r->filename to %s", r->filename);
54     return OK;
55 }
56
57 /* TODO: In APR 2.x: Extend apr_sockaddr_t to possibly be a path !!! */
58 static apr_status_t socket_connect_un(apr_socket_t *sock,
59                                       struct sockaddr_un *sa)
60 {
61     apr_status_t rv;
62     apr_os_sock_t rawsock;
63     apr_interval_time_t t;
64
65     rv = apr_os_sock_get(&rawsock, sock);
66     if (rv != APR_SUCCESS) {
67         return rv;
68     }
69
70     rv = apr_socket_timeout_get(sock, &t);
71     if (rv != APR_SUCCESS) {
72         return rv;
73     }
74
75     do {
76         rv = connect(rawsock, (struct sockaddr*)sa,
77                                sizeof(*sa) + strlen(sa->sun_path));
78     } while (rv == -1 && errno == EINTR);
79
80     if ((rv == -1) && (errno == EINPROGRESS || errno == EALREADY)
81         && (t > 0)) {
82 #if APR_MAJOR_VERSION < 2
83         rv = apr_wait_for_io_or_timeout(NULL, sock, 0);
84 #else
85         rv = apr_socket_wait(sock, APR_WAIT_WRITE);
86 #endif
87
88         if (rv != APR_SUCCESS) {
89             return rv;
90         }
91     }
92
93     if (rv == -1 && errno != EISCONN) {
94         return errno;
95     }
96
97     return APR_SUCCESS;
98 }
99
100 static apr_status_t get_socket_from_path(apr_pool_t *p,
101                                          const char* path,
102                                          apr_socket_t **out_sock)
103 {
104     struct sockaddr_un sa;
105     apr_socket_t *s;
106     apr_status_t rv;
107     *out_sock = NULL;
108
109     rv = apr_socket_create(&s, AF_UNIX, SOCK_STREAM, 0, p);
110
111     if (rv != APR_SUCCESS) {
112         return rv;
113     }
114
115     sa.sun_family = AF_UNIX;
116     apr_cpystrn(sa.sun_path, path, sizeof(sa.sun_path));
117
118     rv = socket_connect_un(s, &sa);
119     if (rv != APR_SUCCESS) {
120         return rv;
121     }
122
123     *out_sock = s;
124
125     return APR_SUCCESS;
126 }
127
128
129 static apr_status_t send_socket(apr_pool_t *p,
130                                 apr_socket_t *s,
131                                 apr_socket_t *outbound)
132 {
133     apr_status_t rv;
134     apr_os_sock_t rawsock;
135     apr_os_sock_t srawsock;
136     struct msghdr msg;
137     struct cmsghdr *cmsg;
138     struct iovec iov;
139     char b = '\0';
140
141     rv = apr_os_sock_get(&rawsock, outbound);
142     if (rv != APR_SUCCESS) {
143         return rv;
144     }
145
146     rv = apr_os_sock_get(&srawsock, s);
147     if (rv != APR_SUCCESS) {
148         return rv;
149     }
150
151     memset(&msg, 0, sizeof(msg));
152
153     msg.msg_iov = &iov;
154     msg.msg_iovlen = 1;
155
156     iov.iov_base = &b;
157     iov.iov_len = 1;
158
159     cmsg = apr_palloc(p, sizeof(*cmsg) + sizeof(rawsock));
160     cmsg->cmsg_len = sizeof(*cmsg) + sizeof(rawsock);
161     cmsg->cmsg_level = SOL_SOCKET;
162     cmsg->cmsg_type = SCM_RIGHTS;
163
164     memcpy(CMSG_DATA(cmsg), &rawsock, sizeof(rawsock));
165
166     msg.msg_control = cmsg;
167     msg.msg_controllen = cmsg->cmsg_len;
168
169     rv = sendmsg(srawsock, &msg, 0);
170
171     if (rv == -1) {
172         return errno;
173     }
174
175
176     return APR_SUCCESS;
177 }
178
179 static int proxy_fdpass_handler(request_rec *r, proxy_worker *worker,
180                               proxy_server_conf *conf,
181                               char *url, const char *proxyname,
182                               apr_port_t proxyport)
183 {
184     apr_status_t rv;
185     apr_socket_t *sock;
186     apr_socket_t *clientsock;
187
188     if (strncasecmp(url, "fd://", 5) == 0) {
189         url += 5;
190     }
191     else {
192         return DECLINED;
193     }
194
195     rv = get_socket_from_path(r->pool, url, &sock);
196
197     if (rv != APR_SUCCESS) {
198         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
199                       "proxy: FD: Failed to connect to '%s'",
200                       url);
201         return HTTP_INTERNAL_SERVER_ERROR;
202     }
203
204     {
205         int status;
206         const char *flush_method = worker->s->flusher ? worker->s->flusher : "flush";
207
208         proxy_fdpass_flush *flush = ap_lookup_provider(PROXY_FDPASS_FLUSHER,
209                                                        flush_method, "0");
210
211         if (!flush) {
212             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
213                           "proxy: FD: Unable to find configured flush "
214                           "provider '%s'", flush_method);
215             return HTTP_INTERNAL_SERVER_ERROR;
216         }
217
218         status = flush->flusher(r);
219         if (status) {
220             return status;
221         }
222     }
223
224     clientsock = ap_get_conn_socket(r->connection);
225
226     rv = send_socket(r->pool, sock, clientsock);
227     if (rv != APR_SUCCESS) {
228         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
229                       "proxy: FD: send_socket failed:");
230         return HTTP_INTERNAL_SERVER_ERROR;
231     }
232
233     {
234         apr_socket_t *dummy;
235         /* Create a dummy unconnected socket, and set it as the one we were
236          * connected to, so that when the core closes it, it doesn't close
237          * the tcp connection to the client.
238          */
239         rv = apr_socket_create(&dummy, APR_INET, SOCK_STREAM, APR_PROTO_TCP,
240                                r->connection->pool);
241         if (rv != APR_SUCCESS) {
242             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
243                           "proxy: FD: failed to create dummy socket");
244             return HTTP_INTERNAL_SERVER_ERROR;
245         }
246         ap_set_core_module_config(r->connection->conn_config, dummy);
247     }
248
249
250     return OK;
251 }
252
253 static int standard_flush(request_rec *r)
254 {
255     int status;
256     apr_bucket_brigade *bb;
257     apr_bucket *e;
258
259     r->connection->keepalive = AP_CONN_CLOSE;
260
261     bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
262     e = apr_bucket_flush_create(r->connection->bucket_alloc);
263
264     APR_BRIGADE_INSERT_TAIL(bb, e);
265
266     status = ap_pass_brigade(r->output_filters, bb);
267
268     if (status != OK) {
269         ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
270                       "proxy: FD: ap_pass_brigade failed:");
271         return status;
272     }
273
274     return OK;
275 }
276
277
278 static const proxy_fdpass_flush builtin_flush =
279 {
280     "flush",
281     &standard_flush,
282     NULL
283 };
284
285 static void register_hooks(apr_pool_t *p)
286 {
287     ap_register_provider(p, PROXY_FDPASS_FLUSHER, "flush", "0", &builtin_flush);
288     proxy_hook_scheme_handler(proxy_fdpass_handler, NULL, NULL, APR_HOOK_FIRST);
289     proxy_hook_canon_handler(proxy_fdpass_canon, NULL, NULL, APR_HOOK_FIRST);
290 }
291
292 AP_DECLARE_MODULE(proxy_fdpass) = {
293     STANDARD20_MODULE_STUFF,
294     NULL,                       /* create per-directory config structure */
295     NULL,                       /* merge per-directory config structures */
296     NULL,                       /* create per-server config structure */
297     NULL,                       /* merge per-server config structures */
298     NULL,                       /* command apr_table_t */
299     register_hooks              /* register hooks */
300 };