]> granicus.if.org Git - apache/blob - modules/proxy/mod_proxy_fdpass.c
Merge r1700317, r1700318, r1700319, r1700320, r1700321, r1700322, r1700326, r1700328...
[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 "mod_proxy_fdpass.h"
28
29 module AP_MODULE_DECLARE_DATA proxy_fdpass_module;
30
31 static int proxy_fdpass_canon(request_rec *r, char *url)
32 {
33     const char *path;
34
35     if (strncasecmp(url, "fd://", 5) == 0) {
36         url += 5;
37     }
38     else {
39         return DECLINED;
40     }
41
42     path = ap_server_root_relative(r->pool, url);
43
44     r->filename = apr_pstrcat(r->pool, "proxy:fd://", path, NULL);
45
46     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01151)
47                   "set r->filename to %s", r->filename);
48     return OK;
49 }
50
51 static apr_status_t get_socket_from_path(apr_pool_t *p,
52                                          const char* path,
53                                          apr_socket_t **out_sock)
54 {
55     apr_socket_t *s;
56     apr_status_t rv;
57     *out_sock = NULL;
58
59     rv = apr_socket_create(&s, AF_UNIX, SOCK_STREAM, 0, p);
60
61     if (rv != APR_SUCCESS) {
62         return rv;
63     }
64
65     rv = ap_proxy_connect_uds(s, path, p);
66     if (rv != APR_SUCCESS) {
67         return rv;
68     }
69
70     *out_sock = s;
71
72     return APR_SUCCESS;
73 }
74
75
76 static apr_status_t send_socket(apr_pool_t *p,
77                                 apr_socket_t *s,
78                                 apr_socket_t *outbound)
79 {
80     apr_status_t rv;
81     apr_os_sock_t rawsock;
82     apr_os_sock_t srawsock;
83     struct msghdr msg;
84     struct cmsghdr *cmsg;
85     struct iovec iov;
86     char b = '\0';
87
88     rv = apr_os_sock_get(&rawsock, outbound);
89     if (rv != APR_SUCCESS) {
90         return rv;
91     }
92
93     rv = apr_os_sock_get(&srawsock, s);
94     if (rv != APR_SUCCESS) {
95         return rv;
96     }
97
98     memset(&msg, 0, sizeof(msg));
99
100     msg.msg_iov = &iov;
101     msg.msg_iovlen = 1;
102
103     iov.iov_base = &b;
104     iov.iov_len = 1;
105
106     cmsg = apr_palloc(p, sizeof(*cmsg) + sizeof(rawsock));
107     cmsg->cmsg_len = sizeof(*cmsg) + sizeof(rawsock);
108     cmsg->cmsg_level = SOL_SOCKET;
109     cmsg->cmsg_type = SCM_RIGHTS;
110
111     memcpy(CMSG_DATA(cmsg), &rawsock, sizeof(rawsock));
112
113     msg.msg_control = cmsg;
114     msg.msg_controllen = cmsg->cmsg_len;
115
116     rv = sendmsg(srawsock, &msg, 0);
117
118     if (rv == -1) {
119         return errno;
120     }
121
122
123     return APR_SUCCESS;
124 }
125
126 static int proxy_fdpass_handler(request_rec *r, proxy_worker *worker,
127                               proxy_server_conf *conf,
128                               char *url, const char *proxyname,
129                               apr_port_t proxyport)
130 {
131     apr_status_t rv;
132     apr_socket_t *sock;
133     apr_socket_t *clientsock;
134
135     if (strncasecmp(url, "fd://", 5) == 0) {
136         url += 5;
137     }
138     else {
139         return DECLINED;
140     }
141
142     rv = get_socket_from_path(r->pool, url, &sock);
143
144     if (rv != APR_SUCCESS) {
145         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01152)
146                       "Failed to connect to '%s'", url);
147         return HTTP_INTERNAL_SERVER_ERROR;
148     }
149
150     {
151         int status;
152         const char *flush_method = worker->s->flusher ? worker->s->flusher : "flush";
153
154         proxy_fdpass_flush *flush = ap_lookup_provider(PROXY_FDPASS_FLUSHER,
155                                                        flush_method, "0");
156
157         if (!flush) {
158             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01153)
159                           "Unable to find configured flush provider '%s'",
160                           flush_method);
161             return HTTP_INTERNAL_SERVER_ERROR;
162         }
163
164         status = flush->flusher(r);
165         if (status) {
166             return status;
167         }
168     }
169
170     clientsock = ap_get_conn_socket(r->connection);
171
172     rv = send_socket(r->pool, sock, clientsock);
173     if (rv != APR_SUCCESS) {
174         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01154) "send_socket failed:");
175         return HTTP_INTERNAL_SERVER_ERROR;
176     }
177
178     {
179         apr_socket_t *dummy;
180         /* Create a dummy unconnected socket, and set it as the one we were
181          * connected to, so that when the core closes it, it doesn't close
182          * the tcp connection to the client.
183          */
184         rv = apr_socket_create(&dummy, APR_INET, SOCK_STREAM, APR_PROTO_TCP,
185                                r->connection->pool);
186         if (rv != APR_SUCCESS) {
187             ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01155)
188                           "failed to create dummy socket");
189             return HTTP_INTERNAL_SERVER_ERROR;
190         }
191         ap_set_core_module_config(r->connection->conn_config, dummy);
192     }
193
194
195     return OK;
196 }
197
198 static int standard_flush(request_rec *r)
199 {
200     int status;
201     apr_bucket_brigade *bb;
202     apr_bucket *e;
203
204     r->connection->keepalive = AP_CONN_CLOSE;
205
206     bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
207     e = apr_bucket_flush_create(r->connection->bucket_alloc);
208
209     APR_BRIGADE_INSERT_TAIL(bb, e);
210
211     status = ap_pass_brigade(r->output_filters, bb);
212
213     if (status != OK) {
214         ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01156)
215                       "ap_pass_brigade failed:");
216         return status;
217     }
218
219     return OK;
220 }
221
222
223 static const proxy_fdpass_flush builtin_flush =
224 {
225     "flush",
226     &standard_flush,
227     NULL
228 };
229
230 static void register_hooks(apr_pool_t *p)
231 {
232     ap_register_provider(p, PROXY_FDPASS_FLUSHER, "flush", "0", &builtin_flush);
233     proxy_hook_scheme_handler(proxy_fdpass_handler, NULL, NULL, APR_HOOK_FIRST);
234     proxy_hook_canon_handler(proxy_fdpass_canon, NULL, NULL, APR_HOOK_FIRST);
235 }
236
237 AP_DECLARE_MODULE(proxy_fdpass) = {
238     STANDARD20_MODULE_STUFF,
239     NULL,                       /* create per-directory config structure */
240     NULL,                       /* merge per-directory config structures */
241     NULL,                       /* create per-server config structure */
242     NULL,                       /* merge per-server config structures */
243     NULL,                       /* command apr_table_t */
244     register_hooks              /* register hooks */
245 };