blob: 8bc019efd00d71508f978bfb66fabd4bdd211bde [file] [log] [blame]
/*
* 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.
*/
/**
* Data marshaling. Originally based on Jserv's ajp12 and other similar
* formats. Implements the jk_msg interface.
*
* Will be eventually replaced with XDR or CDR.
*
* @author: Gal Shachor <shachor@il.ibm.com>
* @author: Henri Gomez <hgomez@apache.org>
* @author: Costin Manolache
*/
#include "jk_pool.h"
#include "jk_msg.h"
#include "jk_logger.h"
#include "jk_endpoint.h"
#include "jk_channel.h"
#include "jk_requtil.h"
/* Signature for the messages sent from Apache to tomcat
*/
#define AJP13_WS_HEADER 0x1234
/* Size of the header ( signature + len )
*/
#define AJP_HEADER_LEN (4)
#define AJP_HEADER_SZ_LEN (2)
char *jk_HEX = "0123456789ABCDEFX";
/*
* Debugging - display the buffer.
*/
static void jk2_msg_ajp_dump(jk_env_t *env, struct jk_msg *_this, char *err)
{
unsigned int i = 0;
char line[80];
char *current;
unsigned int j;
unsigned int len = _this->len;
if (len > 1024)
len = 1024;
env->l->jkLog(env, env->l, JK_LOG_INFO,
"%s pos=%d len=%d max=%d \n",
err, _this->pos, _this->len, _this->maxlen);
for (i = 0; i < len; i += 16) {
current = line;
/* I can't believe I did this ! That's the %.4x :-)
*current++= jk_HEX[ ( i & 0xf000 ) >> 12 ]; */
/* *current++= jk_HEX[ ( i & 0x0f00 ) >> 8 ]; */
/* *current++= jk_HEX[ ( i & 0xf0 ) >> 4 ]; */
/* *current++= jk_HEX[ ( i & 0x0f ) ]; */
for (j = 0; j < 16; j++) {
unsigned char x = (_this->buf[i + j]);
*current++ = jk_HEX[x >> 4];
*current++ = jk_HEX[x & 0x0f];
*current++ = ' ';
}
*current++ = ' ';
*current++ = '-';
*current++ = ' ';
for (j = 0; j < 16; j++) {
unsigned char x = _this->buf[i + j];
if (x > 0x20 && x < 0x7F) {
*current++ = x;
}
else {
*current++ = '.';
}
}
*current++ = '\n';
*current++ = '\0';
env->l->jkLog(env, env->l, JK_LOG_INFO, "%.4x %s", i, line);
}
}
static void jk2_msg_ajp_reset(jk_env_t *env, jk_msg_t *_this)
{
_this->len = AJP_HEADER_LEN;
_this->pos = AJP_HEADER_LEN;
}
static void jk2_msg_ajp_end(jk_env_t *env, jk_msg_t *msg)
{
unsigned short len = msg->len - AJP_HEADER_LEN;
if (msg->serverSide) {
msg->buf[0] = 0x41;
msg->buf[1] = 0x42;
}
else {
msg->buf[0] = 0x12;
msg->buf[1] = 0x34;
}
msg->buf[2] = (unsigned char)((len >> 8) & 0xFF);
msg->buf[3] = (unsigned char)(len & 0xFF);
}
static int jk2_msg_ajp_appendLong(jk_env_t *env, jk_msg_t *msg,
const unsigned long val)
{
int len = msg->len;
if (len + AJP_HEADER_LEN > msg->maxlen) {
return JK_ERR;
}
msg->buf[len] = (unsigned char)((val >> 24) & 0xFF);
msg->buf[len + 1] = (unsigned char)((val >> 16) & 0xFF);
msg->buf[len + 2] = (unsigned char)((val >> 8) & 0xFF);
msg->buf[len + 3] = (unsigned char)(val & 0xFF);
msg->len += 4;
return JK_OK;
}
static int jk2_msg_ajp_appendInt(jk_env_t *env, jk_msg_t *msg,
const unsigned short val)
{
int len = msg->len;
if (len + 2 > msg->maxlen) {
return JK_ERR;
}
msg->buf[len] = (unsigned char)((val >> 8) & 0xFF);
msg->buf[len + 1] = (unsigned char)(val & 0xFF);
msg->len += 2;
return JK_OK;
}
static int jk2_msg_ajp_appendByte(jk_env_t *env, jk_msg_t *msg,
unsigned char val)
{
int len = msg->len;
if (len + 1 > msg->maxlen) {
return JK_ERR;
}
msg->buf[len] = val;
msg->len += 1;
return JK_OK;
}
static int jk2_msg_ajp_appendMap(jk_env_t *env, jk_msg_t *msg, jk_map_t *map)
{
int rc;
int i;
int size = map->size(env, map);
rc = msg->appendInt(env, msg, (short)size);
for (i = 0; i < size; i++) {
char *name = map->nameAt(env, map, i);
char *val = map->valueAt(env, map, i);
if (rc == JK_OK)
rc = msg->appendString(env, msg, name);
if (rc == JK_OK)
msg->appendString(env, msg, val);
}
return rc;
}
static int jk2_msg_ajp_getMap(jk_env_t *env, jk_msg_t *msg, jk_map_t *map)
{
int size = msg->getInt(env, msg);
int i;
if (size < 0) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"msg_ajp.getMap(): negative size %d\n", size);
return JK_ERR;
}
for (i = 0; i < size; i++) {
char *name = msg->getString(env, msg);
char *val = msg->getString(env, msg);
map->add(env, map, name, val);
}
return JK_OK;
}
static int jk2_msg_ajp_appendAString(jk_env_t *env, jk_msg_t *msg,
const char *param, int convert)
{
int len;
if (param == NULL) {
msg->appendInt(env, msg, 0xFFFF);
return JK_OK;
}
len = strlen(param);
if (msg->len + len + 2 > msg->maxlen) {
return JK_ERR;
}
/* ignore error - we checked once */
msg->appendInt(env, msg, (unsigned short)len);
/* We checked for space !! */
strncpy((char *)msg->buf + msg->len, param, len + 1); /* including \0 */
#if defined(AS400) || defined(_OSD_POSIX)
if (convert)
jk_xlate_to_ascii((char *)msg->buf + msg->len, len + 1); /* convert from EBCDIC if needed */
#endif
msg->len += len + 1;
return JK_OK;
}
static int jk2_msg_ajp_appendString(jk_env_t *env, jk_msg_t *msg,
const char *param)
{
return jk2_msg_ajp_appendAString(env, msg, param, 1);
}
static int jk2_msg_ajp_appendAsciiString(jk_env_t *env, jk_msg_t *msg,
const char *param)
{
return jk2_msg_ajp_appendAString(env, msg, param, 0);
}
static int jk2_msg_ajp_appendBytes(jk_env_t *env, jk_msg_t *msg,
const unsigned char *param, const int len)
{
if (!len) {
return JK_OK;
}
if (msg->len + len > msg->maxlen) {
return JK_ERR;
}
/* We checked for space !! */
memcpy((char *)msg->buf + msg->len, param, len);
msg->len += len;
return JK_OK;
}
static unsigned long jk2_msg_ajp_getLong(jk_env_t *env, jk_msg_t *msg)
{
unsigned long i;
if (msg->pos + 3 > msg->len) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"msg_ajp.getLong(): BufferOverflowException %d %d\n",
msg->pos, msg->len);
msg->dump(env, msg, "BUFFER OVERFLOW");
return -1;
}
i = ((msg->buf[(msg->pos++)] & 0xFF) << 24);
i |= ((msg->buf[(msg->pos++)] & 0xFF) << 16);
i |= ((msg->buf[(msg->pos++)] & 0xFF) << 8);
i |= ((msg->buf[(msg->pos++)] & 0xFF));
return i;
}
static unsigned short jk2_msg_ajp_getInt(jk_env_t *env, jk_msg_t *msg)
{
int i;
if (msg->pos + 1 > msg->len) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"msg_ajp.geInt(): BufferOverflowException %d %d\n",
msg->pos, msg->len);
msg->dump(env, msg, "BUFFER OVERFLOW");
return -1;
}
i = ((msg->buf[(msg->pos++)] & 0xFF) << 8);
i += ((msg->buf[(msg->pos++)] & 0xFF));
return i;
}
static unsigned short jk2_msg_ajp_peekInt(jk_env_t *env, jk_msg_t *msg)
{
int i;
if (msg->pos + 1 > msg->len) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"msg_ajp.peekInt(): BufferOverflowException %d %d\n",
msg->pos, msg->len);
msg->dump(env, msg, "BUFFER OVERFLOW");
return -1;
}
i = ((msg->buf[(msg->pos)] & 0xFF) << 8);
i += ((msg->buf[(msg->pos + 1)] & 0xFF));
return i;
}
static unsigned char jk2_msg_ajp_getByte(jk_env_t *env, jk_msg_t *msg)
{
unsigned char rc;
if (msg->pos > msg->len) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"msg_ajp.getByte(): BufferOverflowException %d %d\n",
msg->pos, msg->len);
msg->dump(env, msg, "BUFFER OVERFLOW");
return -1;
}
rc = msg->buf[msg->pos++];
return rc;
}
static char *jk2_msg_ajp_getString(jk_env_t *env, jk_msg_t *msg)
{
int size = jk2_msg_ajp_getInt(env, msg);
int start = msg->pos;
if ((size < 0) || (size + start > msg->maxlen)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"msg_ajp.getString(): BufferOverflowException %d %d\n",
msg->pos, msg->len);
msg->dump(env, msg, "BUFFER OVERFLOW");
return (unsigned char *)"ERROR"; /* XXX */
}
msg->pos += size;
msg->pos++; /* terminating NULL */
return (unsigned char *)(msg->buf + start);
}
static unsigned char *jk2_msg_ajp_getBytes(jk_env_t *env, jk_msg_t *msg,
int *lenP)
{
int size = jk2_msg_ajp_getInt(env, msg);
int start = msg->pos;
*lenP = size;
if ((size < 0) || (size + start > msg->maxlen)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"msg_ajp.getBytes(): BufferOverflowException %d %d\n",
msg->pos, msg->len);
msg->dump(env, msg, "BUFFER OVERFLOW");
return (unsigned char *)"ERROR"; /* XXX */
}
msg->pos += size;
msg->pos++; /* terminating NULL */
return (unsigned char *)(msg->buf + start);
}
/** Shame-less copy from somewhere.
assert (src != dst)
*/
static void jk2_swap_16(unsigned char *src, unsigned char *dst)
{
*dst++ = *(src + 1);
*dst = *src;
}
/** Process the request header. At least the header must be
available - the channel may get more data it it can, to
avoid multiple system calls.
*/
static int jk2_msg_ajp_checkHeader(jk_env_t *env, jk_msg_t *msg,
jk_endpoint_t *ae)
{
char *head = msg->buf;
int msglen;
if (!((head[0] == 0x41 && head[1] == 0x42) ||
(head[0] == 0x12 && head[1] == 0x34))) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"msgAjp.receive(): Bad signature %x%x\n",
head[0], head[1]);
msg->dump(env, msg, "BAD MESSAGE: ");
return -1;
}
msglen = ((head[2] & 0xff) << 8);
msglen += (head[3] & 0xFF);
if (msglen > msg->maxlen) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"msgAjp.receive(): Incoming message is too big %d\n",
msglen);
return -2;
}
msg->len = msglen + AJP_HEADER_LEN;
msg->pos = AJP_HEADER_LEN;
return msglen;
}
/**
* Copy the msg buf into another one, sort of clone.
*
* Returns -1 on error, else number of bytes copied
*/
static int jk2_msg_ajp_copy(jk_env_t *env, jk_msg_t *msg, jk_msg_t *dmsg)
{
if (dmsg == NULL)
return -1;
if (msg->len > dmsg->maxlen) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"msgAjp.copy(): destination buffer too small %d/%d\n",
msg->len, dmsg->maxlen);
return -2;
}
memcpy(dmsg->buf, msg->buf, msg->len);
dmsg->len = msg->len;
dmsg->pos = msg->pos;
return dmsg->len;
}
/**
* Special method. Will read data from the server and add them as
* bytes. It is equivalent with jk2_requtil_readFully() in a buffer
* and then jk_msg_appendBytes(), except that we use directly the
* internal buffer.
*
* Returns -1 on error, else number of bytes read
*/
static int jk2_msg_ajp_appendFromServer(jk_env_t *env, jk_msg_t *msg,
jk_ws_service_t *r,
jk_endpoint_t *ae, int len)
{
unsigned char *read_buf = msg->buf;
jk2_msg_ajp_reset(env, msg);
read_buf += AJP_HEADER_LEN; /* leave some space for the buffer headers */
read_buf += AJP_HEADER_SZ_LEN; /* leave some space for the read length */
/* Pick the max size since we don't know the content_length */
if (r->is_chunked && len == 0) {
len = AJP13_MAX_SEND_BODY_SZ;
}
if ((len = jk2_requtil_readFully(env, r, read_buf, len)) < 0) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"msgAjp.appendFromServer() error reading from server\n");
return -1;
}
if (!r->is_chunked) {
r->left_bytes_to_send -= len;
}
if (len > 0) {
/* Recipient recognizes empty packet as end of stream, not
an empty body packet */
if (0 != jk2_msg_ajp_appendInt(env, msg, (unsigned short)len)) {
env->l->jkLog(env, env->l, JK_LOG_ERROR,
"msg.appendFromServer(): appendInt failed\n");
return -1;
}
}
msg->len = msg->len + len;
return len;
}
static void jk2_msg_ajp_init(jk_env_t *env, jk_msg_t *msg, int buffSize)
{
msg->maxlen = buffSize;
msg->len = 0;
msg->headerLength = AJP_HEADER_LEN;
msg->reset = jk2_msg_ajp_reset;
msg->end = jk2_msg_ajp_end;
msg->dump = jk2_msg_ajp_dump;
msg->appendByte = jk2_msg_ajp_appendByte;
msg->appendBytes = jk2_msg_ajp_appendBytes;
msg->appendInt = jk2_msg_ajp_appendInt;
msg->appendLong = jk2_msg_ajp_appendLong;
msg->appendString = jk2_msg_ajp_appendString;
msg->appendAsciiString = jk2_msg_ajp_appendAsciiString;
msg->appendMap = jk2_msg_ajp_appendMap;
msg->appendFromServer = jk2_msg_ajp_appendFromServer;
msg->copy = jk2_msg_ajp_copy;
msg->getByte = jk2_msg_ajp_getByte;
msg->getInt = jk2_msg_ajp_getInt;
msg->peekInt = jk2_msg_ajp_peekInt;
msg->getLong = jk2_msg_ajp_getLong;
msg->getString = jk2_msg_ajp_getString;
msg->getMap = jk2_msg_ajp_getMap;
msg->getBytes = jk2_msg_ajp_getBytes;
msg->checkHeader = jk2_msg_ajp_checkHeader;
}
jk_msg_t *jk2_msg_ajp_create2(jk_env_t *env, jk_pool_t *pool, char *buf,
int buffSize)
{
jk_msg_t *msg = (jk_msg_t *)pool->calloc(env, pool, sizeof(jk_msg_t));
if (buffSize == 0)
buffSize = DEF_BUFFER_SZ;
if (!msg) {
return NULL;
}
msg->pool = pool;
msg->buf = buf;
if (msg->buf == NULL) {
return NULL;
}
jk2_msg_ajp_init(env, msg, buffSize);
msg->len = buffSize;
return msg;
}
jk_msg_t *jk2_msg_ajp_create(jk_env_t *env, jk_pool_t *pool, int buffSize)
{
jk_msg_t *msg = (jk_msg_t *)pool->calloc(env, pool, sizeof(jk_msg_t));
if (buffSize == 0)
buffSize = DEF_BUFFER_SZ;
if (!msg) {
return NULL;
}
msg->pool = pool;
msg->serverSide = JK_FALSE;
msg->buf = (unsigned char *)msg->pool->alloc(env, msg->pool, buffSize);
if (msg->buf == NULL) {
return NULL;
}
jk2_msg_ajp_init(env, msg, buffSize);
return msg;
}