diff options
Diffstat (limited to 'modules/proxy/ajp_header.c')
-rw-r--r-- | modules/proxy/ajp_header.c | 885 |
1 files changed, 885 insertions, 0 deletions
diff --git a/modules/proxy/ajp_header.c b/modules/proxy/ajp_header.c new file mode 100644 index 0000000..67353a7 --- /dev/null +++ b/modules/proxy/ajp_header.c @@ -0,0 +1,885 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ajp_header.h" +#include "ajp.h" + +APLOG_USE_MODULE(proxy_ajp); + +static const char *response_trans_headers[] = { + "Content-Type", + "Content-Language", + "Content-Length", + "Date", + "Last-Modified", + "Location", + "Set-Cookie", + "Set-Cookie2", + "Servlet-Engine", + "Status", + "WWW-Authenticate" +}; + +static const char *long_res_header_for_sc(int sc) +{ + const char *rc = NULL; + sc = sc & 0X00FF; + if (sc <= SC_RES_HEADERS_NUM && sc > 0) { + rc = response_trans_headers[sc - 1]; + } + + return rc; +} + +#define UNKNOWN_METHOD (-1) + +static int sc_for_req_header(const char *header_name) +{ + char header[16]; + apr_size_t len = strlen(header_name); + const char *p = header_name; + int i = 0; + + /* ACCEPT-LANGUAGE is the longest header + * that is of interest. + */ + if (len < 4 || len > 15) + return UNKNOWN_METHOD; + + while (*p) + header[i++] = apr_toupper(*p++); + header[i] = '\0'; + p = &header[1]; + + switch (header[0]) { + case 'A': + if (memcmp(p, "CCEPT", 5) == 0) { + if (!header[6]) + return SC_ACCEPT; + else if (header[6] == '-') { + p += 6; + if (strcmp(p, "CHARSET") == 0) + return SC_ACCEPT_CHARSET; + else if (strcmp(p, "ENCODING") == 0) + return SC_ACCEPT_ENCODING; + else if (strcmp(p, "LANGUAGE") == 0) + return SC_ACCEPT_LANGUAGE; + else + return UNKNOWN_METHOD; + } + else + return UNKNOWN_METHOD; + } + else if (strcmp(p, "UTHORIZATION") == 0) + return SC_AUTHORIZATION; + else + return UNKNOWN_METHOD; + break; + case 'C': + if (strcmp(p, "OOKIE2") == 0) + return SC_COOKIE2; + else if (strcmp(p, "OOKIE") == 0) + return SC_COOKIE; + else if (strcmp(p, "ONNECTION") == 0) + return SC_CONNECTION; + else if (strcmp(p, "ONTENT-TYPE") == 0) + return SC_CONTENT_TYPE; + else if (strcmp(p, "ONTENT-LENGTH") == 0) + return SC_CONTENT_LENGTH; + else + return UNKNOWN_METHOD; + break; + case 'H': + if (strcmp(p, "OST") == 0) + return SC_HOST; + else + return UNKNOWN_METHOD; + break; + case 'P': + if (strcmp(p, "RAGMA") == 0) + return SC_PRAGMA; + else + return UNKNOWN_METHOD; + break; + case 'R': + if (strcmp(p, "EFERER") == 0) + return SC_REFERER; + else + return UNKNOWN_METHOD; + break; + case 'U': + if (strcmp(p, "SER-AGENT") == 0) + return SC_USER_AGENT; + else + return UNKNOWN_METHOD; + break; + default: + return UNKNOWN_METHOD; + } + + /* NOTREACHED */ +} + +/* Apache method number to SC methods transform table */ +static const unsigned char sc_for_req_method_table[] = { + SC_M_GET, + SC_M_PUT, + SC_M_POST, + SC_M_DELETE, + 0, /* M_CONNECT */ + SC_M_OPTIONS, + SC_M_TRACE, + 0, /* M_PATCH */ + SC_M_PROPFIND, + SC_M_PROPPATCH, + SC_M_MKCOL, + SC_M_COPY, + SC_M_MOVE, + SC_M_LOCK, + SC_M_UNLOCK, + SC_M_VERSION_CONTROL, + SC_M_CHECKOUT, + SC_M_UNCHECKOUT, + SC_M_CHECKIN, + SC_M_UPDATE, + SC_M_LABEL, + SC_M_REPORT, + SC_M_MKWORKSPACE, + SC_M_MKACTIVITY, + SC_M_BASELINE_CONTROL, + SC_M_MERGE, + 0 /* M_INVALID */ +}; + +static int sc_for_req_method_by_id(request_rec *r) +{ + int method_id = r->method_number; + if (method_id < 0 || method_id > M_INVALID) { + return UNKNOWN_METHOD; + } + else if (r->header_only) { + return SC_M_HEAD; + } + else { + return sc_for_req_method_table[method_id] ? + sc_for_req_method_table[method_id] : UNKNOWN_METHOD; + } +} + +/* + * Message structure + * + * +AJPV13_REQUEST/AJPV14_REQUEST= + request_prefix (1) (byte) + method (byte) + protocol (string) + req_uri (string) + remote_addr (string) + remote_host (string) + server_name (string) + server_port (short) + is_ssl (boolean) + num_headers (short) + num_headers*(req_header_name header_value) + + ?context (byte)(string) + ?servlet_path (byte)(string) + ?remote_user (byte)(string) + ?auth_type (byte)(string) + ?query_string (byte)(string) + ?jvm_route (byte)(string) + ?ssl_cert (byte)(string) + ?ssl_cipher (byte)(string) + ?ssl_session (byte)(string) + ?ssl_key_size (byte)(int) via JkOptions +ForwardKeySize + request_terminator (byte) + ?body content_length*(var binary) + + */ + +static apr_status_t ajp_marshal_into_msgb(ajp_msg_t *msg, + request_rec *r, + apr_uri_t *uri) +{ + int method; + apr_uint32_t i, num_headers = 0; + apr_byte_t is_ssl; + char *remote_host; + const char *session_route, *envvar; + const apr_array_header_t *arr = apr_table_elts(r->subprocess_env); + const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts; + + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, "Into ajp_marshal_into_msgb"); + + if ((method = sc_for_req_method_by_id(r)) == UNKNOWN_METHOD) { + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, APLOGNO(02437) + "ajp_marshal_into_msgb - Sending unknown method %s as request attribute", + r->method); + method = SC_M_JK_STORED; + } + + is_ssl = (apr_byte_t) ap_proxy_conn_is_https(r->connection); + + if (r->headers_in && apr_table_elts(r->headers_in)) { + const apr_array_header_t *t = apr_table_elts(r->headers_in); + num_headers = t->nelts; + } + + remote_host = (char *)ap_get_useragent_host(r, REMOTE_HOST, NULL); + + ajp_msg_reset(msg); + + if (ajp_msg_append_uint8(msg, CMD_AJP13_FORWARD_REQUEST) || + ajp_msg_append_uint8(msg, (apr_byte_t) method) || + ajp_msg_append_string(msg, r->protocol) || + ajp_msg_append_string(msg, uri->path) || + ajp_msg_append_string(msg, r->useragent_ip) || + ajp_msg_append_string(msg, remote_host) || + ajp_msg_append_string(msg, ap_get_server_name(r)) || + ajp_msg_append_uint16(msg, (apr_uint16_t)r->connection->local_addr->port) || + ajp_msg_append_uint8(msg, is_ssl) || + ajp_msg_append_uint16(msg, (apr_uint16_t) num_headers)) { + + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00968) + "ajp_marshal_into_msgb: " + "Error appending the message beginning"); + return APR_EGENERAL; + } + + for (i = 0 ; i < num_headers ; i++) { + int sc; + const apr_array_header_t *t = apr_table_elts(r->headers_in); + const apr_table_entry_t *elts = (apr_table_entry_t *)t->elts; + + if ((sc = sc_for_req_header(elts[i].key)) != UNKNOWN_METHOD) { + if (ajp_msg_append_uint16(msg, (apr_uint16_t)sc)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00969) + "ajp_marshal_into_msgb: " + "Error appending the header name"); + return AJP_EOVERFLOW; + } + } + else { + if (ajp_msg_append_string(msg, elts[i].key)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00970) + "ajp_marshal_into_msgb: " + "Error appending the header name"); + return AJP_EOVERFLOW; + } + } + + if (ajp_msg_append_string(msg, elts[i].val)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00971) + "ajp_marshal_into_msgb: " + "Error appending the header value"); + return AJP_EOVERFLOW; + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, + "ajp_marshal_into_msgb: Header[%d] [%s] = [%s]", + i, elts[i].key, elts[i].val); + } + +/* XXXX need to figure out how to do this + if (s->secret) { + if (ajp_msg_append_uint8(msg, SC_A_SECRET) || + ajp_msg_append_string(msg, s->secret)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(03228) + "Error ajp_marshal_into_msgb - " + "Error appending secret"); + return APR_EGENERAL; + } + } + */ + + if (r->user) { + if (ajp_msg_append_uint8(msg, SC_A_REMOTE_USER) || + ajp_msg_append_string(msg, r->user)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00972) + "ajp_marshal_into_msgb: " + "Error appending the remote user"); + return AJP_EOVERFLOW; + } + } + if (r->ap_auth_type) { + if (ajp_msg_append_uint8(msg, SC_A_AUTH_TYPE) || + ajp_msg_append_string(msg, r->ap_auth_type)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00973) + "ajp_marshal_into_msgb: " + "Error appending the auth type"); + return AJP_EOVERFLOW; + } + } + /* XXXX ebcdic (args converted?) */ + if (uri->query) { + if (ajp_msg_append_uint8(msg, SC_A_QUERY_STRING) || + ajp_msg_append_string(msg, uri->query)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00974) + "ajp_marshal_into_msgb: " + "Error appending the query string"); + return AJP_EOVERFLOW; + } + } + if ((session_route = apr_table_get(r->notes, "session-route"))) { + if (ajp_msg_append_uint8(msg, SC_A_JVM_ROUTE) || + ajp_msg_append_string(msg, session_route)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00975) + "ajp_marshal_into_msgb: " + "Error appending the jvm route"); + return AJP_EOVERFLOW; + } + } +/* XXX: Is the subprocess_env a right place? + * <Location /examples> + * ProxyPass ajp://remote:8009/servlets-examples + * SetEnv SSL_SESSION_ID CUSTOM_SSL_SESSION_ID + * </Location> + */ + /* + * Only lookup SSL variables if we are currently running HTTPS. + * Furthermore ensure that only variables get set in the AJP message + * that are not NULL and not empty. + */ + if (is_ssl) { + if ((envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r, + AJP13_SSL_CLIENT_CERT_INDICATOR)) + && envvar[0]) { + if (ajp_msg_append_uint8(msg, SC_A_SSL_CERT) + || ajp_msg_append_string(msg, envvar)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00976) + "ajp_marshal_into_msgb: " + "Error appending the SSL certificates"); + return AJP_EOVERFLOW; + } + } + + if ((envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r, + AJP13_SSL_CIPHER_INDICATOR)) + && envvar[0]) { + if (ajp_msg_append_uint8(msg, SC_A_SSL_CIPHER) + || ajp_msg_append_string(msg, envvar)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00977) + "ajp_marshal_into_msgb: " + "Error appending the SSL ciphers"); + return AJP_EOVERFLOW; + } + } + + if ((envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r, + AJP13_SSL_SESSION_INDICATOR)) + && envvar[0]) { + if (ajp_msg_append_uint8(msg, SC_A_SSL_SESSION) + || ajp_msg_append_string(msg, envvar)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00978) + "ajp_marshal_into_msgb: " + "Error appending the SSL session"); + return AJP_EOVERFLOW; + } + } + + /* ssl_key_size is required by Servlet 2.3 API */ + if ((envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r, + AJP13_SSL_KEY_SIZE_INDICATOR)) + && envvar[0]) { + + if (ajp_msg_append_uint8(msg, SC_A_SSL_KEY_SIZE) + || ajp_msg_append_uint16(msg, (unsigned short) atoi(envvar))) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00979) + "ajp_marshal_into_msgb: " + "Error appending the SSL key size"); + return APR_EGENERAL; + } + } + } + /* If the method was unrecognized, encode it as an attribute */ + if (method == SC_M_JK_STORED) { + if (ajp_msg_append_uint8(msg, SC_A_STORED_METHOD) + || ajp_msg_append_string(msg, r->method)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02438) + "ajp_marshal_into_msgb: " + "Error appending the method '%s' as request attribute", + r->method); + return AJP_EOVERFLOW; + } + } + /* Forward the SSL protocol name. + * Modern Tomcat versions know how to retrieve + * the protocol name from this attribute. + */ + if (is_ssl) { + if ((envvar = ap_proxy_ssl_val(r->pool, r->server, r->connection, r, + AJP13_SSL_PROTOCOL_INDICATOR)) + && envvar[0]) { + const char *key = SC_A_SSL_PROTOCOL; + if (ajp_msg_append_uint8(msg, SC_A_REQ_ATTRIBUTE) || + ajp_msg_append_string(msg, key) || + ajp_msg_append_string(msg, envvar)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02830) + "ajp_marshal_into_msgb: " + "Error appending attribute %s=%s", + key, envvar); + return AJP_EOVERFLOW; + } + } + } + /* Forward the remote port information, which was forgotten + * from the builtin data of the AJP 13 protocol. + * Since the servlet spec allows to retrieve it via getRemotePort(), + * we provide the port to the Tomcat connector as a request + * attribute. Modern Tomcat versions know how to retrieve + * the remote port from this attribute. + */ + { + const char *key = SC_A_REQ_REMOTE_PORT; + char *val = apr_itoa(r->pool, r->useragent_addr->port); + if (ajp_msg_append_uint8(msg, SC_A_REQ_ATTRIBUTE) || + ajp_msg_append_string(msg, key) || + ajp_msg_append_string(msg, val)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00980) + "ajp_marshal_into_msgb: " + "Error appending attribute %s=%s", + key, val); + return AJP_EOVERFLOW; + } + } + /* Forward the local ip address information, which was forgotten + * from the builtin data of the AJP 13 protocol. + * Since the servlet spec allows to retrieve it via getLocalAddr(), + * we provide the address to the Tomcat connector as a request + * attribute. Modern Tomcat versions know how to retrieve + * the local address from this attribute. + */ + { + const char *key = SC_A_REQ_LOCAL_ADDR; + char *val = r->connection->local_ip; + if (ajp_msg_append_uint8(msg, SC_A_REQ_ATTRIBUTE) || + ajp_msg_append_string(msg, key) || + ajp_msg_append_string(msg, val)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02646) + "ajp_marshal_into_msgb: " + "Error appending attribute %s=%s", + key, val); + return AJP_EOVERFLOW; + } + } + /* Use the environment vars prefixed with AJP_ + * and pass it to the header striping that prefix. + */ + for (i = 0; i < (apr_uint32_t)arr->nelts; i++) { + if (!strncmp(elts[i].key, "AJP_", 4)) { + if (ajp_msg_append_uint8(msg, SC_A_REQ_ATTRIBUTE) || + ajp_msg_append_string(msg, elts[i].key + 4) || + ajp_msg_append_string(msg, elts[i].val)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00981) + "ajp_marshal_into_msgb: " + "Error appending attribute %s=%s", + elts[i].key, elts[i].val); + return AJP_EOVERFLOW; + } + } + } + + if (ajp_msg_append_uint8(msg, SC_A_ARE_DONE)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00982) + "ajp_marshal_into_msgb: " + "Error appending the message end"); + return AJP_EOVERFLOW; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE8, 0, r, + "ajp_marshal_into_msgb: Done"); + return APR_SUCCESS; +} + +/* +AJPV13_RESPONSE/AJPV14_RESPONSE:= + response_prefix (2) + status (short) + status_msg (short) + num_headers (short) + num_headers*(res_header_name header_value) + *body_chunk + terminator boolean <! -- recycle connection or not --> + +req_header_name := + sc_req_header_name | (string) + +res_header_name := + sc_res_header_name | (string) + +header_value := + (string) + +body_chunk := + length (short) + body length*(var binary) + + */ + +static int addit_dammit(void *v, const char *key, const char *val) +{ + apr_table_addn(v, key, val); + return 1; +} + +static apr_status_t ajp_unmarshal_response(ajp_msg_t *msg, + request_rec *r, + proxy_dir_conf *dconf) +{ + apr_uint16_t status; + apr_status_t rc; + const char *ptr; + apr_uint16_t num_headers; + int i; + + rc = ajp_msg_get_uint16(msg, &status); + + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00983) + "ajp_unmarshal_response: Null status"); + return rc; + } + r->status = status; + + rc = ajp_msg_get_string(msg, &ptr); + if (rc == APR_SUCCESS) { +#if APR_CHARSET_EBCDIC /* copy only if we have to */ + ptr = apr_pstrdup(r->pool, ptr); + ap_xlate_proto_from_ascii(ptr, strlen(ptr)); +#endif + r->status_line = apr_psprintf(r->pool, "%d %s", status, ptr); + } + else { + r->status_line = NULL; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, + "ajp_unmarshal_response: status = %d", status); + + rc = ajp_msg_get_uint16(msg, &num_headers); + if (rc == APR_SUCCESS) { + apr_table_t *save_table; + + /* First, tuck away all already existing cookies */ + /* + * Could optimize here, but just in case we want to + * also save other headers, keep this logic. + */ + save_table = apr_table_make(r->pool, num_headers + 2); + apr_table_do(addit_dammit, save_table, r->headers_out, + "Set-Cookie", NULL); + r->headers_out = save_table; + } + else { + r->headers_out = NULL; + num_headers = 0; + } + + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, + "ajp_unmarshal_response: Number of headers is = %d", + num_headers); + + for (i = 0; i < (int)num_headers; i++) { + apr_uint16_t name; + const char *stringname; + const char *value; + rc = ajp_msg_peek_uint16(msg, &name); + if (rc != APR_SUCCESS) { + return rc; + } + + if ((name & 0XFF00) == 0XA000) { + ajp_msg_get_uint16(msg, &name); + stringname = long_res_header_for_sc(name); + if (stringname == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00984) + "ajp_unmarshal_response: " + "No such sc (%08x)", + name); + return AJP_EBAD_HEADER; + } + } + else { + name = 0; + rc = ajp_msg_get_string(msg, &stringname); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00985) + "ajp_unmarshal_response: " + "Null header name"); + return rc; + } + ap_xlate_proto_from_ascii(stringname, strlen(stringname)); + } + + rc = ajp_msg_get_string(msg, &value); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00986) + "ajp_unmarshal_response: " + "Null header value"); + return rc; + } + + /* Set-Cookie need additional processing */ + if (!strcasecmp(stringname, "Set-Cookie")) { + value = ap_proxy_cookie_reverse_map(r, dconf, value); + } + /* Location, Content-Location, URI and Destination need additional + * processing */ + else if (!strcasecmp(stringname, "Location") + || !strcasecmp(stringname, "Content-Location") + || !strcasecmp(stringname, "URI") + || !strcasecmp(stringname, "Destination")) + { + value = ap_proxy_location_reverse_map(r, dconf, value); + } + + ap_xlate_proto_from_ascii(value, strlen(value)); + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, + "ajp_unmarshal_response: Header[%d] [%s] = [%s]", + i, stringname, value); + + apr_table_add(r->headers_out, stringname, value); + + /* Content-type needs an additional handling */ + if (strcasecmp(stringname, "Content-Type") == 0) { + /* add corresponding filter */ + ap_set_content_type(r, apr_pstrdup(r->pool, value)); + ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r, + "ajp_unmarshal_response: ap_set_content_type to '%s'", value); + } + } + + return APR_SUCCESS; +} + +/* + * Build the ajp header message and send it + */ +apr_status_t ajp_send_header(apr_socket_t *sock, + request_rec *r, + apr_size_t buffsize, + apr_uri_t *uri) +{ + ajp_msg_t *msg; + apr_status_t rc; + + rc = ajp_msg_create(r->pool, buffsize, &msg); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00987) + "ajp_send_header: ajp_msg_create failed"); + return rc; + } + + rc = ajp_marshal_into_msgb(msg, r, uri); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00988) + "ajp_send_header: ajp_marshal_into_msgb failed"); + return rc; + } + + rc = ajp_ilink_send(sock, msg); + ajp_msg_log(r, msg, "ajp_send_header: ajp_ilink_send packet dump"); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00989) + "ajp_send_header: ajp_ilink_send failed"); + return rc; + } + + return APR_SUCCESS; +} + +/* + * Read the ajp message and return the type of the message. + */ +apr_status_t ajp_read_header(apr_socket_t *sock, + request_rec *r, + apr_size_t buffsize, + ajp_msg_t **msg) +{ + apr_byte_t result; + apr_status_t rc; + + if (*msg) { + rc = ajp_msg_reuse(*msg); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00990) + "ajp_read_header: ajp_msg_reuse failed"); + return rc; + } + } + else { + rc = ajp_msg_create(r->pool, buffsize, msg); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00991) + "ajp_read_header: ajp_msg_create failed"); + return rc; + } + } + ajp_msg_reset(*msg); + rc = ajp_ilink_receive(sock, *msg); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00992) + "ajp_read_header: ajp_ilink_receive failed"); + return rc; + } + ajp_msg_log(r, *msg, "ajp_read_header: ajp_ilink_receive packet dump"); + rc = ajp_msg_peek_uint8(*msg, &result); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00993) + "ajp_read_header: ajp_msg_peek_uint8 failed"); + return rc; + } + ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, + "ajp_read_header: ajp_ilink_received %s (0x%02x)", + ajp_type_str(result), result); + return APR_SUCCESS; +} + +/* parse the msg to read the type */ +int ajp_parse_type(request_rec *r, ajp_msg_t *msg) +{ + apr_byte_t result; + ajp_msg_peek_uint8(msg, &result); + ap_log_rerror(APLOG_MARK, APLOG_TRACE6, 0, r, + "ajp_parse_type: got %s (0x%02x)", + ajp_type_str(result), result); + return (int) result; +} + +/* parse the header */ +apr_status_t ajp_parse_header(request_rec *r, proxy_dir_conf *conf, + ajp_msg_t *msg) +{ + apr_byte_t result; + apr_status_t rc; + + rc = ajp_msg_get_uint8(msg, &result); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00994) + "ajp_parse_headers: ajp_msg_get_byte failed"); + return rc; + } + if (result != CMD_AJP13_SEND_HEADERS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00995) + "ajp_parse_headers: wrong type %s (0x%02x) expecting %s (0x%02x)", + ajp_type_str(result), result, + ajp_type_str(CMD_AJP13_SEND_HEADERS), CMD_AJP13_SEND_HEADERS); + return AJP_EBAD_HEADER; + } + return ajp_unmarshal_response(msg, r, conf); +} + +/* parse the body and return data address and length */ +apr_status_t ajp_parse_data(request_rec *r, ajp_msg_t *msg, + apr_uint16_t *len, char **ptr) +{ + apr_byte_t result; + apr_status_t rc; + apr_uint16_t expected_len; + + rc = ajp_msg_get_uint8(msg, &result); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00996) + "ajp_parse_data: ajp_msg_get_byte failed"); + return rc; + } + if (result != CMD_AJP13_SEND_BODY_CHUNK) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00997) + "ajp_parse_data: wrong type %s (0x%02x) expecting %s (0x%02x)", + ajp_type_str(result), result, + ajp_type_str(CMD_AJP13_SEND_BODY_CHUNK), CMD_AJP13_SEND_BODY_CHUNK); + return AJP_EBAD_HEADER; + } + rc = ajp_msg_get_uint16(msg, len); + if (rc != APR_SUCCESS) { + return rc; + } + /* + * msg->len contains the complete length of the message including all + * headers. So the expected length for a CMD_AJP13_SEND_BODY_CHUNK is + * msg->len minus the sum of + * AJP_HEADER_LEN : The length of the header to every AJP message. + * AJP_HEADER_SZ_LEN : The header giving the size of the chunk. + * 1 : The CMD_AJP13_SEND_BODY_CHUNK indicator byte (0x03). + * 1 : The last byte of this message always seems to be + * 0x00 and is not part of the chunk. + */ + expected_len = msg->len - (AJP_HEADER_LEN + AJP_HEADER_SZ_LEN + 1 + 1); + if (*len != expected_len) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00998) + "ajp_parse_data: Wrong chunk length. Length of chunk is %i," + " expected length is %i.", *len, expected_len); + return AJP_EBAD_HEADER; + } + *ptr = (char *)&(msg->buf[msg->pos]); + return APR_SUCCESS; +} + +/* Check the reuse flag in CMD_AJP13_END_RESPONSE */ +apr_status_t ajp_parse_reuse(request_rec *r, ajp_msg_t *msg, + apr_byte_t *reuse) +{ + apr_byte_t result; + apr_status_t rc; + + rc = ajp_msg_get_uint8(msg, &result); + if (rc != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00999) + "ajp_parse_reuse: ajp_msg_get_byte failed"); + return rc; + } + if (result != CMD_AJP13_END_RESPONSE) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01000) + "ajp_parse_reuse: wrong type %s (0x%02x) expecting %s (0x%02x)", + ajp_type_str(result), result, + ajp_type_str(CMD_AJP13_END_RESPONSE), CMD_AJP13_END_RESPONSE); + return AJP_EBAD_HEADER; + } + return ajp_msg_get_uint8(msg, reuse); +} + +/* + * Allocate a msg to send data + */ +apr_status_t ajp_alloc_data_msg(apr_pool_t *pool, char **ptr, apr_size_t *len, + ajp_msg_t **msg) +{ + apr_status_t rc; + + if ((rc = ajp_msg_create(pool, *len, msg)) != APR_SUCCESS) + return rc; + ajp_msg_reset(*msg); + *ptr = (char *)&((*msg)->buf[6]); + *len = *len - 6; + + return APR_SUCCESS; +} + +/* + * Send the data message + */ +apr_status_t ajp_send_data_msg(apr_socket_t *sock, + ajp_msg_t *msg, apr_size_t len) +{ + + msg->buf[4] = (apr_byte_t)((len >> 8) & 0xFF); + msg->buf[5] = (apr_byte_t)(len & 0xFF); + + msg->len += len + 2; /* + 1 XXXX where is '\0' */ + + return ajp_ilink_send(sock, msg); + +} |