From: Jean-Frederic Clere Date: Wed, 18 Aug 2004 16:09:27 +0000 (+0000) Subject: jakarta-tomcat-connectors AJP files. X-Git-Tag: STRIKER_2_1_0_RC1~91 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5e63bc4a9061055eaab28ed11cfaec226a878dc9;p=apache jakarta-tomcat-connectors AJP files. The original location of the files in jakarta is: jakarta-tomcat-connectors/ajp/ajplib/test (That was not a good idea) git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@104711 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/proxy/ajp.h b/modules/proxy/ajp.h new file mode 100644 index 0000000000..19203fe85f --- /dev/null +++ b/modules/proxy/ajp.h @@ -0,0 +1,447 @@ +/* Copyright 1999-2004 The Apache Software Foundation + * + * Licensed 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. + */ + +#ifndef AJP_H +#define AJP_H + +#include "apr_version.h" +#include "apr.h" + +#include "apr_hooks.h" +#include "apr_lib.h" +#include "apr_strings.h" +#include "apr_buckets.h" +#include "apr_md5.h" +#include "apr_network_io.h" +#include "apr_pools.h" +#include "apr_strings.h" +#include "apr_uri.h" +#include "apr_date.h" +#include "apr_fnmatch.h" +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#if APR_HAVE_NETINET_IN_H +#include +#endif +#if APR_HAVE_ARPA_INET_H +#include +#endif + +#define AJP13_DEF_HOST "127.0.0.1" +#ifdef NETWARE +#define AJP13_DEF_PORT 9009 /* default to 9009 since 8009 is used by OS */ +#else +#define AJP13_DEF_PORT 8009 +#endif + + +#if APR_CHARSET_EBCDIC + +#define USE_CHARSET_EBCDIC +#define ajp_xlate_to_ascii(b, l) ap_xlate_proto_to_ascii(b, l) +#define ajp_xlate_from_ascii(b, l) ap_xlate_proto_from_ascii(b, l) + +#else /* APR_CHARSET_EBCDIC */ + +#define ajp_xlate_to_ascii(b, l) +#define ajp_xlate_from_ascii(b, l) + +#endif + +#ifdef AJP_USE_HTTPD_WRAP +#include "httpd_wrap.h" +#else +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_main.h" +#include "http_log.h" +#endif + + +/** AJP Specific error codes + */ +/** Buffer overflow exception */ +#define AJP_EOVERFLOW (APR_OS_START_USERERR + 1) +/** Destination Buffer is to small */ +#define AJP_ETOSMALL (APR_OS_START_USERERR + 2) +/** Invalid input parameters */ +#define AJP_EINVAL (APR_OS_START_USERERR + 3) +/** Bad message signature */ +#define AJP_EBAD_SIGNATURE (APR_OS_START_USERERR + 4) +/** Incoming message too bg */ +#define AJP_ETOBIG (APR_OS_START_USERERR + 5) +/** Missing message header */ +#define AJP_ENO_HEADER (APR_OS_START_USERERR + 6) +/** Bad message header */ +#define AJP_EBAD_HEADER (APR_OS_START_USERERR + 7) +/** Bad message */ +#define AJP_EBAD_MESSAGE (APR_OS_START_USERERR + 8) +/** Cant log via AJP14 */ +#define AJP_ELOGFAIL (APR_OS_START_USERERR + 9) + +/** A structure that represents ajp message */ +typedef struct ajp_msg ajp_msg_t; + +/** A structure that represents ajp message */ +struct ajp_msg +{ + /** The buffer holding a AJP message */ + apr_byte_t *buf; + /** The length of AJP message header (defaults to AJP_HEADER_LEN) */ + apr_size_t header_len; + /** The length of AJP message */ + apr_size_t len; + /** The current read position */ + apr_size_t pos; + /** Flag indicating the origing of the message */ + int server_side; +}; + +/** + * @defgroup AJP_defines AJP definitions + * @{ + */ +/** + * Signature for the messages sent from Apache to tomcat + */ +#define AJP13_WS_HEADER 0x1234 +#define AJP_HEADER_LEN 4 +#define AJP_HEADER_SZ_LEN 2 +#define AJP_MSG_BUFFER_SZ (8*1024) +#define AJP13_MAX_SEND_BODY_SZ (AJP_MSG_BUFFER_SZ - 6) + +/** Send a request from web server to container*/ +#define CMD_AJP13_FORWARD_REQUEST (unsigned char)2 +/** Write a body chunk from the servlet container to the web server */ +#define CMD_AJP13_SEND_BODY_CHUNK (unsigned char)3 +/** Send response headers from the servlet container to the web server. */ +#define CMD_AJP13_SEND_HEADERS (unsigned char)4 +/** Marks the end of response. */ +#define CMD_AJP13_END_RESPONSE (unsigned char)5 +/** Get further data from the web server if it hasn't all been transferred yet. */ +#define CMD_AJP13_GET_BODY_CHUNK (unsigned char)6 +/** The web server asks the container to shut itself down. */ +#define CMD_AJP13_SHUTDOWN (unsigned char)7 +/** Webserver ask container to take control (logon phase) */ +#define CMD_AJP13_PING (unsigned char)8 +/** Container response to cping request */ +#define CMD_AJP13_CPONG (unsigned char)9 +/** Webserver check if container is alive, since container should respond by cpong */ +#define CMD_AJP13_CPING (unsigned char)10 + +/** @} */ + +/** + * @defgroup AJP_api AJP API functions + * @{ + */ +/** + * Check a new AJP Message by looking at signature and return its size + * + * @param msg AJP Message to check + * @param len Pointer to returned len + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_check_header(ajp_msg_t *msg, apr_size_t *len); + +/** + * Reset an AJP Message + * + * @param msg AJP Message to reset + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_reset(ajp_msg_t *msg); + +/** + * Mark the end of an AJP Message + * + * @param msg AJP Message to end + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_end(ajp_msg_t *msg); + +/** + * Add an unsigned 32bits value to AJP Message + * + * @param msg AJP Message to get value from + * @param value value to add to AJP Message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_uint32(ajp_msg_t *msg, apr_uint32_t value); + +/** + * Add an unsigned 16bits value to AJP Message + * + * @param msg AJP Message to get value from + * @param value value to add to AJP Message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_uint16(ajp_msg_t *msg, apr_uint16_t value); + +/** + * Add an unsigned 8bits value to AJP Message + * + * @param msg AJP Message to get value from + * @param value value to add to AJP Message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_uint8(ajp_msg_t *msg, apr_byte_t value); + +/** + * Add a String in AJP message, and transform the String in ASCII + * if convert is set and we're on an EBCDIC machine + * + * @param msg AJP Message to get value from + * @param value Pointer to String + * @param convert When set told to convert String to ASCII + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_string_ex(ajp_msg_t *msg, const char *value, + int convert); +/** + * Add a String in AJP message, and transform + * the String in ASCII if we're on an EBCDIC machine + */ +#define ajp_msg_append_string(m, v) ajp_msg_append_string_ex(m, v, 1) + +/** + * Add a String in AJP message. + */ +#define ajp_msg_append_string_ascii(m, v) ajp_msg_append_string_ex(m, v, 0) + +/** + * Add a Byte array to AJP Message + * + * @param msg AJP Message to get value from + * @param value Pointer to Byte array + * @param valuelen Byte array len + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_bytes(ajp_msg_t *msg, const apr_byte_t *value, + apr_size_t valuelen); + +/** + * Get a 32bits unsigned value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_uint32(ajp_msg_t *msg, apr_uint32_t *rvalue); + +/** + * Get a 16bits unsigned value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_uint16(ajp_msg_t *msg, apr_uint16_t *rvalue); + +/** + * Peek a 16bits unsigned value from AJP Message, position in message + * is not updated + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_peek_uint16(ajp_msg_t *msg, apr_uint16_t *rvalue); + +/** + * Get a 8bits unsigned value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_uint8(ajp_msg_t *msg, apr_byte_t *rvalue); + +/** + * Peek a 8bits unsigned value from AJP Message, position in message + * is not updated + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_peek_uint8(ajp_msg_t *msg, apr_byte_t *rvalue); + +/** + * Get a String value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_string(ajp_msg_t *msg, char **rvalue); + + +/** + * Get a Byte array from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @param rvalueLen Pointer where Byte array len will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_bytes(ajp_msg_t *msg, apr_byte_t **rvalue, + apr_size_t *rvalue_len); + +/** + * Create an AJP Message from pool + * + * @param pool memory pool to allocate AJP message from + * @param rmsg Pointer to newly created AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_create(apr_pool_t *pool, ajp_msg_t **rmsg); + +/** + * Recopy an AJP Message to another + * + * @param smsg source AJP message + * @param dmsg destination AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_copy(ajp_msg_t *smsg, ajp_msg_t *dmsg); + +/** + * Serialize in an AJP Message a PING command + * + * +-----------------------+ + * | PING CMD (1 byte) | + * +-----------------------+ + * + * @param smsg AJP message to put serialized message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_serialize_ping(ajp_msg_t *msg); + +/** + * Serialize in an AJP Message a CPING command + * + * +-----------------------+ + * | CPING CMD (1 byte) | + * +-----------------------+ + * + * @param smsg AJP message to put serialized message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_serialize_cping(ajp_msg_t *msg); + +/** + * Dump up to the first 1024 bytes on an AJP Message + * + * @param msg AJP Message to dump + * @param err error string to display + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_dump(ajp_msg_t *msg, char *err); + +/** + * Send an AJP message to backend + * + * @param soct backend socket + * @param smsg AJP message to put serialized message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_ilink_send(apr_socket_t *sock, ajp_msg_t *msg); + +/** + * Receive an AJP message from backend + * + * @param sock backend socket + * @param smsg AJP message to put serialized message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_ilink_receive(apr_socket_t *sock, ajp_msg_t *msg); + +/** + * Build the ajp header message and send it + * @param sock backend socket + * @param r current request + * @return APR_SUCCESS or error + */ +apr_status_t ajp_send_header(apr_socket_t *sock, request_rec *r); + +/** + * Read the ajp message and return the type of the message. + * @param sock backend socket + * @param r current request + * @param msg returned AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_read_header(apr_socket_t *sock, + request_rec *r, + ajp_msg_t **msg); + +/** + * Allocate a msg to send data + * @param r current request + * @param ptr data buffer + * @param len the length of allocated data buffer + * @param msg returned AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_alloc_data_msg(request_rec *r, char **ptr, apr_size_t *len, + ajp_msg_t **msg); + +/** + * Send the data message + * @param sock backend socket + * @param r current request + * @param msg AJP message to send + * @param len AJP message length + * @return APR_SUCCESS or error + */ +apr_status_t ajp_send_data_msg(apr_socket_t *sock, request_rec *r, + ajp_msg_t *msg, apr_size_t len); + +/** + * Parse the message type + * @param r current request + * @param msg AJP message + * @return AJP message type. + */ +int ajp_parse_type(request_rec *r, ajp_msg_t *msg); + +/** + * Parse the header message from container + * @param r current request + * @param msg AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_parse_header(request_rec *r, ajp_msg_t *msg); + +/** + * Parse the message body and return data address and length + * @param r current request + * @param msg AJP message + * @param len returned AJP message length + * @param ptr returned data + * @return APR_SUCCESS or error + */ +apr_status_t ajp_parse_data(request_rec *r, ajp_msg_t *msg, + apr_uint16_t *len, char **ptr); + +/** @} */ + +#endif /* AJP_H */ + diff --git a/modules/proxy/ajp_header.c b/modules/proxy/ajp_header.c new file mode 100644 index 0000000000..02704a6c26 --- /dev/null +++ b/modules/proxy/ajp_header.c @@ -0,0 +1,715 @@ +/* + * Copyright 1999-2004 The Apache Software Foundation + * + * Licensed 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" + +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 headeer + * 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 (memcmp(p, "CHARSET", 7) == 0) + return SC_ACCEPT_CHARSET; + else if (memcmp(p, "ENCODING", 8) == 0) + return SC_ACCEPT_ENCODING; + else if (memcmp(p, "LANGUAGE", 8) == 0) + return SC_ACCEPT_LANGUAGE; + else + return UNKNOWN_METHOD; + } + else + return UNKNOWN_METHOD; + } + else if (memcmp(p, "UTHORIZATION", 12) == 0) + return SC_AUTHORIZATION; + else + return UNKNOWN_METHOD; + break; + case 'C': + if (memcmp(p, "OOKIE", 5) == 0) + return SC_COOKIE; + else if(memcmp(p, "ONNECTION", 9) == 0) + return SC_CONNECTION; + else if(memcmp(p, "ONTENT-TYPE", 11) == 0) + return SC_CONTENT_TYPE; + else if(memcmp(p, "ONTENT-LENGTH", 13) == 0) + return SC_CONTENT_LENGTH; + else if(memcmp(p, "OOKIE2", 6) == 0) + return SC_COOKIE2; + else + return UNKNOWN_METHOD; + break; + case 'H': + if(memcmp(p, "OST", 3) == 0) + return SC_HOST; + else + return UNKNOWN_METHOD; + break; + case 'P': + if(memcmp(p, "RAGMA", 5) == 0) + return SC_PRAGMA; + else + return UNKNOWN_METHOD; + break; + case 'R': + if(memcmp(p, "EFERER", 6) == 0) + return SC_REFERER; + else + return UNKNOWN_METHOD; + break; + case 'U': + if(memcmp(p, "SER-AGENT", 9) == 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_DELETE */ + 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(int method_id) +{ + if (method_id < 0 || method_id > M_INVALID) + return UNKNOWN_METHOD; + 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) +{ + int method; + apr_uint32_t i, num_headers = 0; + apr_byte_t is_ssl; + char *remote_host; + char *uri; + + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Into ajp_marshal_into_msgb"); + + if ((method = sc_for_req_method_by_id(r->method_number)) == UNKNOWN_METHOD) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - No such method %s", + r->method); + return APR_EGENERAL; + } + + /* XXXX need something */ + is_ssl = (apr_byte_t) 0; /* s->is_ssl */ + + 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_remote_host(r->connection, r->per_dir_config, REMOTE_HOST, NULL); + + uri = apr_pstrdup(r->pool, r->uri); + if (uri != NULL) { + char *query_str = strchr(uri, '?'); + if (query_str != NULL) { + *query_str = 0; + } + } + + + ajp_msg_reset(msg); + + if (ajp_msg_append_uint8(msg, CMD_AJP13_FORWARD_REQUEST) || + ajp_msg_append_uint8(msg, method) || + ajp_msg_append_string(msg, r->protocol) || + ajp_msg_append_string(msg, uri) || + ajp_msg_append_string(msg, r->connection->remote_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_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the message begining"); + 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_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the header name"); + return APR_EGENERAL; + } + } + else { + if (ajp_msg_append_string(msg, elts[i].key)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the header name"); + return APR_EGENERAL; + } + } + + if (ajp_msg_append_string(msg, elts[i].val)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the header value"); + return APR_EGENERAL; + } + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "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_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "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_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the remote user"); + return APR_EGENERAL; + } + } + 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_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the auth type"); + return APR_EGENERAL; + } + } + /* XXXX ebcdic (args converted?) */ + if (r->args) { + if (ajp_msg_append_uint8(msg, SC_A_QUERY_STRING) || + ajp_msg_append_string(msg, r->args)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the query string"); + return APR_EGENERAL; + } + } +/* XXXX ignored for the moment + if (s->jvm_route) { + if (ajp_msg_append_uint8(msg, SC_A_JVM_ROUTE) || + ajp_msg_append_string(msg, s->jvm_route)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the jvm route"); + return APR_EGENERAL; + } + } + if (s->ssl_cert_len) { + if (ajp_msg_append_uint8(msg, SC_A_SSL_CERT) || + ajp_msg_append_string(msg, s->ssl_cert)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the SSL certificates"); + return APR_EGENERAL; + } + } + + if (s->ssl_cipher) { + if (ajp_msg_append_uint8(msg, SC_A_SSL_CIPHER) || + ajp_msg_append_string(msg, s->ssl_cipher)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the SSL ciphers"); + return APR_EGENERAL; + } + } + if (s->ssl_session) { + if (ajp_msg_append_uint8(msg, SC_A_SSL_SESSION) || + ajp_msg_append_string(msg, s->ssl_session)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the SSL session"); + return APR_EGENERAL; + } + } + */ + + /* + * ssl_key_size is required by Servlet 2.3 API + * added support only in ajp14 mode + * JFC removed: ae->proto == AJP14_PROTO + */ + /* XXXX ignored for the moment + if (s->ssl_key_size != -1) { + if (ajp_msg_append_uint8(msg, SC_A_SSL_KEY_SIZE) || + ajp_msg_append_uint16(msg, (unsigned short) s->ssl_key_size)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the SSL key size"); + return APR_EGENERAL; + } + } + */ + + /* XXXX ignored for the moment + if (s->num_attributes > 0) { + for (i = 0 ; i < s->num_attributes ; i++) { + if (ajp_msg_append_uint8(msg, SC_A_REQ_ATTRIBUTE) || + ajp_msg_append_string(msg, s->attributes_names[i]) || + ajp_msg_append_string(msg, s->attributes_values[i])) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending attribute %s=%s", + s->attributes_names[i], s->attributes_values[i]); + return APR_EGENERAL; + } + } + } + */ + + if (ajp_msg_append_uint8(msg, SC_A_ARE_DONE)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_marshal_into_msgb - " + "Error appending the message end"); + return APR_EGENERAL; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "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 + +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 apr_status_t ajp_unmarshal_response(ajp_msg_t *msg, + request_rec *r) +{ + apr_uint16_t status; + apr_status_t rc; + char *ptr; + apr_uint16_t num_headers; + int i; + + rc = ajp_msg_get_uint16(msg, &status); + + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_unmarshal_response - Null status"); + return APR_EGENERAL; + } + r->status = status; + + rc = ajp_msg_get_string(msg, &ptr); + if (rc == APR_SUCCESS) { + r->status_line = apr_psprintf(r->pool, "%d %s", status, ptr); +#if defined(AS400) || defined(_OSD_POSIX) + ap_xlate_proto_from_ascii(r->status_line, strlen(r->status_line)); +#endif + } else { + r->status_line = NULL; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "ajp_unmarshal_response: status = %d", status); + + rc = ajp_msg_get_uint16(msg, &num_headers); + if (rc == APR_SUCCESS) { + r->headers_out = apr_table_make(r->pool, num_headers); + } else { + r->headers_out = NULL; + num_headers = 0; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "ajp_unmarshal_response: Number of headers is = %d", + num_headers); + + for(i = 0 ; i < (int) num_headers ; i++) { + apr_uint16_t name; + char *stringname; + char *value; + rc = ajp_msg_peek_uint16(msg, &name); + if (rc != APR_SUCCESS) { + return APR_EGENERAL; + } + + if ((name & 0XFF00) == 0XA000) { + ajp_msg_peek_uint16(msg, &name); + stringname = (char *)long_res_header_for_sc(name); + if (stringname == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_unmarshal_response - " + "No such sc (%08x)", + name); + return APR_EGENERAL; + } + } else { + name = 0; + rc = ajp_msg_get_string(msg, &stringname); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_unmarshal_response - " + "Null header name"); + return APR_EGENERAL; + } +#if defined(AS400) || defined(_OSD_POSIX) + ap_xlate_proto_from_ascii(stringname, strlen(stringname)); +#endif + } + + rc = ajp_msg_get_string(msg, &value); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "Error ajp_unmarshal_response - " + "Null header value"); + return APR_EGENERAL; + } + +#if defined(AS400) || defined(_OSD_POSIX) + ap_xlate_proto_from_ascii(value, strlen(value)); +#endif + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "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 (memcmp(stringname, "Content-Type", 12) == 0) { + /* add corresponding filter */ + ap_set_content_type(r, apr_pstrdup(r->pool, value)); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "ajp_unmarshal_response: ap_set_content_type done"); + } + } + + return APR_SUCCESS; +} + +/* + * Build the ajp header message and send it + */ +apr_status_t ajp_send_header(apr_socket_t *sock, + request_rec *r) +{ + ajp_msg_t *msg; + apr_status_t rc; + + rc = ajp_msg_create(r->pool, &msg); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_send_header: ajp_msg_create failed"); + return rc; + } + + rc = ajp_marshal_into_msgb(msg, r); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_send_header: ajp_marshal_into_msgb failed"); + return rc; + } + + rc = ajp_ilink_send(sock, msg); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "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, + ajp_msg_t **msg) +{ + apr_byte_t result; + apr_status_t rc; + + rc = ajp_msg_create(r->pool, msg); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "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_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_read_header: ajp_ilink_receive failed"); + return rc; + } + rc = ajp_msg_peek_uint8(*msg, &result); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "ajp_read_header: ajp_ilink_received %02x", 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_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "ajp_parse_type: got %02x", result); + return (int) result; +} + +/* parse the header */ +apr_status_t ajp_parse_header(request_rec *r, 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_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_parse_headers: ajp_msg_get_byte failed"); + return rc; + } + if (result != CMD_AJP13_SEND_HEADERS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_parse_headers: wrong type %02x expecting 0x04", result); + return APR_EGENERAL; + } + return ajp_unmarshal_response(msg, r); +} + +/* 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; + + rc = ajp_msg_get_uint8(msg, &result); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_parse_data: ajp_msg_get_byte failed"); + return rc; + } + if (result != CMD_AJP13_SEND_BODY_CHUNK) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_parse_data: wrong type %02x expecting 0x03", result); + return APR_EGENERAL; + } + rc = ajp_msg_get_uint16(msg, len); + if (rc != APR_SUCCESS) { + return APR_EGENERAL; + } + *ptr = (char *)&(msg->buf[msg->pos]); + return APR_SUCCESS; +} + +/* + * Allocate a msg to send data + */ +apr_status_t ajp_alloc_data_msg(request_rec *r, char **ptr, apr_size_t *len, + ajp_msg_t **msg) +{ + apr_status_t rc; + + rc = ajp_msg_create(r->pool, msg); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_alloc_data_msg: ajp_msg_create failed"); + return rc; + } + ajp_msg_reset(*msg); + *ptr = (char *)&((*msg)->buf[6]); + *len = AJP_MSG_BUFFER_SZ-6; + + return APR_SUCCESS; +} + +/* + * Send the data message + */ +apr_status_t ajp_send_data_msg(apr_socket_t *sock, request_rec *r, + ajp_msg_t *msg, apr_size_t len) +{ + apr_status_t rc; + + 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' */ + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "ajp_send_data_msg: sending %d", len); + + rc = ajp_ilink_send(sock, msg); + if (rc != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "ajp_send_data_msg: ajp_ilink_send failed"); + return rc; + } + + return APR_SUCCESS; +} diff --git a/modules/proxy/ajp_header.h b/modules/proxy/ajp_header.h new file mode 100644 index 0000000000..ff2b3f4c01 --- /dev/null +++ b/modules/proxy/ajp_header.h @@ -0,0 +1,165 @@ +/* + * Copyright 1999-2004 The Apache Software Foundation + * + * Licensed 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. + */ +#ifndef AJP_HEADER_H +#define AJP_HEADER_H + +/* + * Conditional request attributes + * + */ +#define SC_A_CONTEXT (unsigned char)1 +#define SC_A_SERVLET_PATH (unsigned char)2 +#define SC_A_REMOTE_USER (unsigned char)3 +#define SC_A_AUTH_TYPE (unsigned char)4 +#define SC_A_QUERY_STRING (unsigned char)5 +#define SC_A_JVM_ROUTE (unsigned char)6 +#define SC_A_SSL_CERT (unsigned char)7 +#define SC_A_SSL_CIPHER (unsigned char)8 +#define SC_A_SSL_SESSION (unsigned char)9 +#define SC_A_REQ_ATTRIBUTE (unsigned char)10 +#define SC_A_SSL_KEY_SIZE (unsigned char)11 /* only in if JkOptions +ForwardKeySize */ +#define SC_A_SECRET (unsigned char)12 +#define SC_A_ARE_DONE (unsigned char)0xFF + +/* + * Request methods, coded as numbers instead of strings. + * The list of methods was taken from Section 5.1.1 of RFC 2616, + * RFC 2518, the ACL IETF draft, and the DeltaV IESG Proposed Standard. + * Method = "OPTIONS" + * | "GET" + * | "HEAD" + * | "POST" + * | "PUT" + * | "DELETE" + * | "TRACE" + * | "PROPFIND" + * | "PROPPATCH" + * | "MKCOL" + * | "COPY" + * | "MOVE" + * | "LOCK" + * | "UNLOCK" + * | "ACL" + * | "REPORT" + * | "VERSION-CONTROL" + * | "CHECKIN" + * | "CHECKOUT" + * | "UNCHECKOUT" + * | "SEARCH" + * | "MKWORKSPACE" + * | "UPDATE" + * | "LABEL" + * | "MERGE" + * | "BASELINE-CONTROL" + * | "MKACTIVITY" + * + */ +#define SC_M_OPTIONS (unsigned char)1 +#define SC_M_GET (unsigned char)2 +#define SC_M_HEAD (unsigned char)3 +#define SC_M_POST (unsigned char)4 +#define SC_M_PUT (unsigned char)5 +#define SC_M_DELETE (unsigned char)6 +#define SC_M_TRACE (unsigned char)7 +#define SC_M_PROPFIND (unsigned char)8 +#define SC_M_PROPPATCH (unsigned char)9 +#define SC_M_MKCOL (unsigned char)10 +#define SC_M_COPY (unsigned char)11 +#define SC_M_MOVE (unsigned char)12 +#define SC_M_LOCK (unsigned char)13 +#define SC_M_UNLOCK (unsigned char)14 +#define SC_M_ACL (unsigned char)15 +#define SC_M_REPORT (unsigned char)16 +#define SC_M_VERSION_CONTROL (unsigned char)17 +#define SC_M_CHECKIN (unsigned char)18 +#define SC_M_CHECKOUT (unsigned char)19 +#define SC_M_UNCHECKOUT (unsigned char)20 +#define SC_M_SEARCH (unsigned char)21 +#define SC_M_MKWORKSPACE (unsigned char)22 +#define SC_M_UPDATE (unsigned char)23 +#define SC_M_LABEL (unsigned char)24 +#define SC_M_MERGE (unsigned char)25 +#define SC_M_BASELINE_CONTROL (unsigned char)26 +#define SC_M_MKACTIVITY (unsigned char)27 + + +/* + * Frequent request headers, these headers are coded as numbers + * instead of strings. + * + * Accept + * Accept-Charset + * Accept-Encoding + * Accept-Language + * Authorization + * Connection + * Content-Type + * Content-Length + * Cookie + * Cookie2 + * Host + * Pragma + * Referer + * User-Agent + * + */ + +#define SC_ACCEPT (unsigned short)0xA001 +#define SC_ACCEPT_CHARSET (unsigned short)0xA002 +#define SC_ACCEPT_ENCODING (unsigned short)0xA003 +#define SC_ACCEPT_LANGUAGE (unsigned short)0xA004 +#define SC_AUTHORIZATION (unsigned short)0xA005 +#define SC_CONNECTION (unsigned short)0xA006 +#define SC_CONTENT_TYPE (unsigned short)0xA007 +#define SC_CONTENT_LENGTH (unsigned short)0xA008 +#define SC_COOKIE (unsigned short)0xA009 +#define SC_COOKIE2 (unsigned short)0xA00A +#define SC_HOST (unsigned short)0xA00B +#define SC_PRAGMA (unsigned short)0xA00C +#define SC_REFERER (unsigned short)0xA00D +#define SC_USER_AGENT (unsigned short)0xA00E + +/* + * Frequent response headers, these headers are coded as numbers + * instead of strings. + * + * Content-Type + * Content-Language + * Content-Length + * Date + * Last-Modified + * Location + * Set-Cookie + * Servlet-Engine + * Status + * WWW-Authenticate + * + */ + +#define SC_RESP_CONTENT_TYPE (unsigned short)0xA001 +#define SC_RESP_CONTENT_LANGUAGE (unsigned short)0xA002 +#define SC_RESP_CONTENT_LENGTH (unsigned short)0xA003 +#define SC_RESP_DATE (unsigned short)0xA004 +#define SC_RESP_LAST_MODIFIED (unsigned short)0xA005 +#define SC_RESP_LOCATION (unsigned short)0xA006 +#define SC_RESP_SET_COOKIE (unsigned short)0xA007 +#define SC_RESP_SET_COOKIE2 (unsigned short)0xA008 +#define SC_RESP_SERVLET_ENGINE (unsigned short)0xA009 +#define SC_RESP_STATUS (unsigned short)0xA00A +#define SC_RESP_WWW_AUTHENTICATE (unsigned short)0xA00B +#define SC_RES_HEADERS_NUM 11 + +#endif /* AJP_HEADER_H */ diff --git a/modules/proxy/ajp_link.c b/modules/proxy/ajp_link.c new file mode 100644 index 0000000000..5314be7ab6 --- /dev/null +++ b/modules/proxy/ajp_link.c @@ -0,0 +1,132 @@ +/* Copyright 1999-2004 The Apache Software Foundation + * + * Licensed 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.h" + + +apr_status_t ajp_ilink_send(apr_socket_t *sock, ajp_msg_t *msg) +{ + char *buf; + apr_status_t status; + apr_size_t length; + + if (sock == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_ilink_send(): NULL socket provided"); + return AJP_EINVAL; + } + + ajp_msg_end(msg); + + length = msg->len; + buf = (char *)msg->buf; + + do { + apr_size_t written = length; + + status = apr_socket_send(sock, buf, &written); + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, NULL, + "ajp_ilink_send(): send failed"); + return status; + } + length -= written; + buf += written; + } while (length); + + return APR_SUCCESS; +} + + +static apr_status_t ilink_read(apr_socket_t *sock, char * buf, + apr_size_t len) +{ + apr_size_t length; + apr_status_t status; + apr_size_t rdlen; + + if (sock == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_ilink_readN(): NULL socket provided"); + return AJP_EINVAL; + } + + rdlen = 0; + length = len; + + while (rdlen < len) { + + status = apr_socket_recv(sock, buf + rdlen, &length); + + if (status == APR_EOF) + return status; /* socket closed. */ + else if (APR_STATUS_IS_EAGAIN(status)) + continue; + else if (status != APR_SUCCESS) + return status; /* any error. */ + + rdlen += length; + length = len - rdlen; + } + return APR_SUCCESS; +} + + +apr_status_t ajp_ilink_receive(apr_socket_t *sock, ajp_msg_t *msg) +{ + apr_status_t status; + apr_size_t hlen; + apr_size_t blen; + + if (sock == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_ilink_receive(): NULL socket provided"); + return AJP_EINVAL; + } + + hlen = msg->header_len; + + status = ilink_read(sock, msg->buf, hlen); + + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, NULL, + "ajp_ilink_receive() can't receive header\n"); + return AJP_ENO_HEADER; + } + + status = ajp_msg_check_header(msg, &blen); + + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_ilink_receive() received bad header\n"); + return AJP_EBAD_HEADER; + } + + status = ilink_read(sock, msg->buf + hlen, blen); + + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, status, NULL, + "ajp_ilink_receive() error while receiving message body %of length %d\n", + hlen); + return AJP_EBAD_MESSAGE; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, + "ajp_ilink_receive() received packet len=%d type=%d\n", + blen, (int)msg->buf[hlen]); + + return APR_SUCCESS; +} + diff --git a/modules/proxy/ajp_msg.c b/modules/proxy/ajp_msg.c new file mode 100644 index 0000000000..3128d933b0 --- /dev/null +++ b/modules/proxy/ajp_msg.c @@ -0,0 +1,607 @@ +/* Copyright 1999-2004 The Apache Software Foundation + * + * Licensed 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.h" + + +static char *hex_table = "0123456789ABCDEF"; + +/** + * Dump up to the first 1024 bytes on an AJP Message + * + * @param msg AJP Message to dump + * @param err error string to display + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_dump(ajp_msg_t *msg, char *err) +{ + apr_size_t i, j; + char line[80]; + char *current; + apr_byte_t x; + apr_size_t len = msg->len; + + /* Display only first 1024 bytes */ + if (len > 1024) + len = 1024; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, + "ajp_msg_dump(): %s pos=%d len=%d max=%d", + err, msg->pos, msg->len, AJP_MSG_BUFFER_SZ); + + for (i = 0; i < len; i += 16) { + current = line; + + for (j = 0; j < 16; j++) { + x = msg->buf[i + j]; + + *current++ = hex_table[x >> 4]; + *current++ = hex_table[x & 0x0f]; + *current++ = ' '; + } + *current++ = ' '; + *current++ = '-'; + *current++ = ' '; + for (j = 0; j < 16; j++) { + x = msg->buf[i + j]; + + if (x > 0x20 && x < 0x7F) { + *current++ = x; + } + else { + *current++ = '.'; + } + } + + *current++ = '\0'; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, + "ajp_msg_dump(): %.4x %s", + i, line); + } + + return APR_SUCCESS; +} + + +/** + * Check a new AJP Message by looking at signature and return its size + * + * @param msg AJP Message to check + * @param len Pointer to returned len + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_check_header(ajp_msg_t *msg, apr_size_t *len) +{ + apr_byte_t *head = msg->buf; + apr_size_t msglen; + + if (!((head[0] == 0x41 && head[1] == 0x42) || + (head[0] == 0x12 && head[1] == 0x34))) { + + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_check_msg_header() got bad signature %x%x", + head[0], head[1]); + + return AJP_EBAD_SIGNATURE; + } + + msglen = ((head[2] & 0xff) << 8); + msglen += (head[3] & 0xFF); + + if (msglen > AJP_MSG_BUFFER_SZ) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_check_msg_header() incoming message is too big %d, max is %d", + msglen, AJP_MSG_BUFFER_SZ); + return AJP_ETOBIG; + } + + msg->len = msglen + AJP_HEADER_LEN; + msg->pos = AJP_HEADER_LEN; + *len = msglen; + + return APR_SUCCESS; +} + +/** + * Reset an AJP Message + * + * @param msg AJP Message to reset + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_reset(ajp_msg_t *msg) +{ + msg->len = AJP_HEADER_LEN; + msg->pos = AJP_HEADER_LEN; + + return APR_SUCCESS; +} + +/** + * Mark the end of an AJP Message + * + * @param msg AJP Message to end + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_end(ajp_msg_t *msg) +{ + apr_size_t len = msg->len - AJP_HEADER_LEN; + + if (msg->server_side) { + msg->buf[0] = 0x41; + msg->buf[1] = 0x42; + } + else { + msg->buf[0] = 0x12; + msg->buf[1] = 0x34; + } + + msg->buf[2] = (apr_byte_t)((len >> 8) & 0xFF); + msg->buf[3] = (apr_byte_t)(len & 0xFF); + + return APR_SUCCESS; +} + +/** + * Add an unsigned 32bits value to AJP Message + * + * @param msg AJP Message to get value from + * @param value value to add to AJP Message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_uint32(ajp_msg_t *msg, apr_uint32_t value) +{ + apr_size_t len = msg->len; + + if ((len + 4) > AJP_MSG_BUFFER_SZ) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_append_uint32(): BufferOverflowException %d %d", + msg->pos, msg->len); + return AJP_EOVERFLOW; + } + + msg->buf[len] = (apr_byte_t)((value >> 24) & 0xFF); + msg->buf[len + 1] = (apr_byte_t)((value >> 16) & 0xFF); + msg->buf[len + 2] = (apr_byte_t)((value >> 8) & 0xFF); + msg->buf[len + 3] = (apr_byte_t)(value & 0xFF); + + msg->len += 4; + + return APR_SUCCESS; +} + +/** + * Add an unsigned 16bits value to AJP Message + * + * @param msg AJP Message to get value from + * @param value value to add to AJP Message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_uint16(ajp_msg_t *msg, apr_uint16_t value) +{ + apr_size_t len = msg->len; + + if ((len + 2) > AJP_MSG_BUFFER_SZ) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_append_uint16(): BufferOverflowException %d %d", + msg->pos, msg->len); + return AJP_EOVERFLOW; + } + + msg->buf[len] = (apr_byte_t)((value >> 8) & 0xFF); + msg->buf[len + 1] = (apr_byte_t)(value & 0xFF); + + msg->len += 2; + + return APR_SUCCESS; +} + +/** + * Add an unsigned 8bits value to AJP Message + * + * @param msg AJP Message to get value from + * @param value value to add to AJP Message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_uint8(ajp_msg_t *msg, apr_byte_t value) +{ + apr_size_t len = msg->len; + + if ((len + 1) > AJP_MSG_BUFFER_SZ) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_append_uint8(): BufferOverflowException %d %d", + msg->pos, msg->len); + return AJP_EOVERFLOW; + } + + msg->buf[len] = value; + msg->len += 1; + + return APR_SUCCESS; +} + +/** + * Add a String in AJP message, and transform the String in ASCII + * if convert is set and we're on an EBCDIC machine + * + * @param msg AJP Message to get value from + * @param value Pointer to String + * @param convert When set told to convert String to ASCII + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_string_ex(ajp_msg_t *msg, const char *value, + int convert) +{ + size_t len; + + if (value == NULL) { + return(ajp_msg_append_uint16(msg, 0xFFFF)); + } + + len = strlen(value); + if ((msg->len + len + 2) > AJP_MSG_BUFFER_SZ) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_append_cvt_string(): BufferOverflowException %d %d", + msg->pos, msg->len); + return AJP_EOVERFLOW; + } + + /* ignore error - we checked once */ + ajp_msg_append_uint16(msg, (apr_uint16_t)len); + + /* We checked for space !! */ + memcpy(msg->buf + msg->len, value, len + 1); /* including \0 */ + + if (convert) /* convert from EBCDIC if needed */ + ajp_xlate_to_ascii((char *)msg->buf + msg->len, len + 1); + + msg->len += len + 1; + + return APR_SUCCESS; +} + +/** + * Add a Byte array to AJP Message + * + * @param msg AJP Message to get value from + * @param value Pointer to Byte array + * @param valuelen Byte array len + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_append_bytes(ajp_msg_t *msg, const apr_byte_t *value, + apr_size_t valuelen) +{ + if (! valuelen) { + return APR_SUCCESS; /* Shouldn't we indicate an error ? */ + } + + if ((msg->len + valuelen) > AJP_MSG_BUFFER_SZ) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_append_bytes(): BufferOverflowException %d %d", + msg->pos, msg->len); + return AJP_EOVERFLOW; + } + + /* We checked for space !! */ + memcpy(msg->buf + msg->len, value, valuelen); + msg->len += valuelen; + + return APR_SUCCESS; +} + +/** + * Get a 32bits unsigned value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_uint32(ajp_msg_t *msg, apr_uint32_t *rvalue) +{ + apr_uint32_t value; + + if ((msg->pos + 3) > msg->len) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_get_long(): BufferOverflowException %d %d", + msg->pos, msg->len); + + return AJP_EOVERFLOW; + } + + value = ((msg->buf[(msg->pos++)] & 0xFF) << 24); + value |= ((msg->buf[(msg->pos++)] & 0xFF) << 16); + value |= ((msg->buf[(msg->pos++)] & 0xFF) << 8); + value |= ((msg->buf[(msg->pos++)] & 0xFF)); + + *rvalue = value; + return APR_SUCCESS; +} + + +/** + * Get a 16bits unsigned value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_uint16(ajp_msg_t *msg, apr_uint16_t *rvalue) +{ + apr_uint16_t value; + + if ((msg->pos + 1) > msg->len) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_get_int(): BufferOverflowException %d %d", + msg->pos, msg->len); + + return AJP_EOVERFLOW; + } + + value = ((msg->buf[(msg->pos++)] & 0xFF) << 8); + value += ((msg->buf[(msg->pos++)] & 0xFF)); + + *rvalue = value; + return APR_SUCCESS; +} + +/** + * Peek a 16bits unsigned value from AJP Message, position in message + * is not updated + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_peek_uint16(ajp_msg_t *msg, apr_uint16_t *rvalue) +{ + apr_uint16_t value; + + if ((msg->pos + 1) > msg->len) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_peek_int(): BufferOverflowException %d %d", + msg->pos, msg->len); + + return AJP_EOVERFLOW; + } + + value = ((msg->buf[(msg->pos)] & 0xFF) << 8); + value += ((msg->buf[(msg->pos + 1)] & 0xFF)); + + *rvalue = value; + return APR_SUCCESS; +} + +/** + * Peek a 8bits unsigned value from AJP Message, position in message + * is not updated + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_peek_uint8(ajp_msg_t *msg, apr_byte_t *rvalue) +{ + if (msg->pos > msg->len) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_peek_uint8(): BufferOverflowException %d %d", + msg->pos, msg->len); + + return AJP_EOVERFLOW; + } + + *rvalue = msg->buf[msg->pos]; + return APR_SUCCESS; +} + +/** + * Get a 8bits unsigned value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_uint8(ajp_msg_t *msg, apr_byte_t *rvalue) +{ + + if (msg->pos > msg->len) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_get_uint8(): BufferOverflowException %d %d", + msg->pos, msg->len); + + return AJP_EOVERFLOW; + } + + *rvalue = msg->buf[msg->pos++]; + return APR_SUCCESS; +} + + +/** + * Get a String value from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_string(ajp_msg_t *msg, char **rvalue) +{ + apr_uint16_t size; + apr_size_t start; + apr_status_t status; + + status = ajp_msg_get_uint16(msg, &size); + start = msg->pos; + + if ((status != APR_SUCCESS) || (size + start > AJP_MSG_BUFFER_SZ)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_get_string(): BufferOverflowException %d %d", + msg->pos, msg->len); + + return AJP_EOVERFLOW; + } + + msg->pos += (apr_size_t)size; + msg->pos++; /* a String in AJP is NULL terminated */ + + *rvalue = (char *)(msg->buf + start); + return APR_SUCCESS; +} + + +/** + * Get a Byte array from AJP Message + * + * @param msg AJP Message to get value from + * @param rvalue Pointer where value will be returned + * @param rvalueLen Pointer where Byte array len will be returned + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_get_bytes(ajp_msg_t *msg, apr_byte_t **rvalue, + apr_size_t *rvalue_len) +{ + apr_uint16_t size; + apr_size_t start; + apr_status_t status; + + status = ajp_msg_get_uint16(msg, &size); + /* save the current position */ + start = msg->pos; + + if ((status != APR_SUCCESS) || (size + start > AJP_MSG_BUFFER_SZ)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_get_bytes(): BufferOverflowException %d %d", + msg->pos, msg->len); + return AJP_EOVERFLOW; + } + msg->pos += (apr_size_t)size; /* only bytes, no trailer */ + + *rvalue = msg->buf + start; + *rvalue_len = size; + + return APR_SUCCESS; +} + + +/** + * Create an AJP Message from pool + * + * @param pool memory pool to allocate AJP message from + * @param rmsg Pointer to newly created AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_create(apr_pool_t *pool, ajp_msg_t **rmsg) +{ + ajp_msg_t *msg = (ajp_msg_t *)apr_pcalloc(pool, sizeof(ajp_msg_t)); + + if (!msg) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_create(): can't allocate AJP message memory"); + return APR_ENOPOOL; + } + + msg->server_side = 0; + + msg->buf = (apr_byte_t *)apr_palloc(pool, AJP_MSG_BUFFER_SZ); + + /* XXX: This should never happen + * In case if the OS cannont allocate 8K of data + * we are in serious trouble + * No need to check the alloc return value, cause the + * core dump is probably the best solution anyhow. + */ + if (msg->buf == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_create(): can't allocate AJP message memory"); + return APR_ENOPOOL; + } + + msg->len = 0; + msg->header_len = AJP_HEADER_LEN; + *rmsg = msg; + + return APR_SUCCESS; +} + +/** + * Recopy an AJP Message to another + * + * @param smsg source AJP message + * @param dmsg destination AJP message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_copy(ajp_msg_t *smsg, ajp_msg_t *dmsg) +{ + if (dmsg == NULL) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_copy(): destination msg is null"); + return AJP_EINVAL; + } + + if (smsg->len > AJP_MSG_BUFFER_SZ) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, + "ajp_msg_copy(): destination buffer too small %d, max size is %d", + smsg->len, AJP_MSG_BUFFER_SZ); + return AJP_ETOSMALL; + } + + memcpy(dmsg->buf, smsg->buf, smsg->len); + dmsg->len = smsg->len; + dmsg->pos = smsg->pos; + + return APR_SUCCESS; +} + + +/** + * Serialize in an AJP Message a PING command + * + * +-----------------------+ + * | PING CMD (1 byte) | + * +-----------------------+ + * + * @param smsg AJP message to put serialized message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_serialize_ping(ajp_msg_t *msg) +{ + apr_status_t rc; + ajp_msg_reset(msg); + + if ((rc = ajp_msg_append_uint8(msg, CMD_AJP13_PING)) != APR_SUCCESS) + return rc; + + return APR_SUCCESS; +} + +/** + * Serialize in an AJP Message a CPING command + * + * +-----------------------+ + * | CPING CMD (1 byte) | + * +-----------------------+ + * + * @param smsg AJP message to put serialized message + * @return APR_SUCCESS or error + */ +apr_status_t ajp_msg_serialize_cping(ajp_msg_t *msg) +{ + apr_status_t rc; + ajp_msg_reset(msg); + + if ((rc = ajp_msg_append_uint8(msg, CMD_AJP13_CPING)) != APR_SUCCESS) + return rc; + + return APR_SUCCESS; +}