blob: 76c24e404e8fd0955f8b87e7c01a4b2d9ea3c71c [file]
/** @file
A brief file description
@section license License
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.
*/
/***************************************************************************
* NetworkUtilsLocal.cc
*
* contains implementation of local networking utility functions, such as
* unmarshalling requests from a remote client and marshalling replies
*
*
***************************************************************************/
#include "ink_platform.h"
#include "ink_sock.h"
#include "Diags.h"
#include "MgmtUtils.h"
#include "CoreAPIShared.h"
#include "NetworkUtilsLocal.h"
#ifndef MAX_BUF_SIZE
#define MAX_BUF_SIZE 4096
#endif
/**************************************************************************
* socket_flush
*
* flushes the socket by reading the entire message out of the socket
* and then gets rid of the msg
**************************************************************************/
INKError
socket_flush(struct SocketInfo sock_info)
{
int ret, byte_read = 0;
char buf[MAX_BUF_SIZE];
// check to see if anything to read; wait only for specified time
if (socket_read_timeout(sock_info.fd, MAX_TIME_WAIT, 0) <= 0) {
return INK_ERR_NET_TIMEOUT;
}
// read entire message
while (byte_read < MAX_BUF_SIZE) {
ret = socket_read(sock_info, buf + byte_read, MAX_BUF_SIZE - byte_read);
if (ret < 0) {
if (errno == EAGAIN)
continue;
Debug("ts_main", "[socket_read_n] socket read for version byte failed.\n");
mgmt_elog("[socket_flush] (INK_ERR_NET_READ) %s\n", strerror(errno));
return INK_ERR_NET_READ;
}
if (ret == 0) {
Debug("ts_main", "[socket_read_n] returned 0 on reading: %s.\n", strerror(errno));
mgmt_log("[socket_flush] (INK_ERR_NET_EOF) %s\n", strerror(errno));
return INK_ERR_NET_EOF;
}
// we are all good here
byte_read += ret;
}
mgmt_elog("[socket_flush] uh oh! didn't finish flushing socket!\n");
return INK_ERR_FAIL;
}
/**************************************************************************
* socket_read_n
*
* purpose: guarantees reading of n bytes or return error.
* input: socket info struct, buffer to read into and number of bytes to read
* output: number of bytes read
* note: socket_read is implemented in WebUtils.cc
*************************************************************************/
INKError
socket_read_n(struct SocketInfo sock_info, char *buf, int bytes)
{
int ret, byte_read = 0;
// check to see if anything to read; wait for specified time
if (socket_read_timeout(sock_info.fd, MAX_TIME_WAIT, 0) <= 0) {
return INK_ERR_NET_TIMEOUT;
}
// read until we fulfill the number
while (byte_read < bytes) {
ret = socket_read(sock_info, buf + byte_read, bytes - byte_read);
// error!
if (ret < 0) {
if (errno == EAGAIN)
continue;
Debug("ts_main", "[socket_read_n] socket read for version byte failed.\n");
mgmt_elog("[socket_read_n] (INK_ERR_NET_READ) %s\n", strerror(errno));
return INK_ERR_NET_READ;
}
if (ret == 0) {
Debug("ts_main", "[socket_read_n] returned 0 on reading: %s.\n", strerror(errno));
mgmt_log("[socket_read_n] (INK_ERR_NET_EOF) %s\n", strerror(errno));
return INK_ERR_NET_EOF;
}
// we are all good here
byte_read += ret;
}
return INK_ERR_OKAY;
}
/**************************************************************************
* socket_write_n
*
* purpose: guarantees writing of n bytes or return error
* input: socket info struct, buffer to write from & number of bytes to write
* output: INK_ERR_xx (depends on num bytes written)
* note: socket_read is implemented in WebUtils.cc
*************************************************************************/
INKError
socket_write_n(struct SocketInfo sock_info, const char *buf, int bytes)
{
int ret, byte_wrote = 0;
// makes sure the socket descriptor is writable
if (socket_write_timeout(sock_info.fd, MAX_TIME_WAIT, 0) <= 0) {
return INK_ERR_NET_TIMEOUT;
}
// read until we fulfill the number
while (byte_wrote < bytes) {
ret = socket_write(sock_info, buf + byte_wrote, bytes - byte_wrote);
if (ret < 0) {
Debug("ts_main", "[socket_write_n] return error %s \n", strerror(errno));
mgmt_elog("[socket_write_n] %s\n", strerror(errno));
if (errno == EAGAIN)
continue;
return INK_ERR_NET_WRITE;
}
if (ret == 0) {
mgmt_elog("[socket_write_n] %s\n", strerror(errno));
return INK_ERR_NET_EOF;
}
// we are all good here
byte_wrote += ret;
}
return INK_ERR_OKAY;
}
/**********************************************************************
* preprocess_msg
*
* purpose: reads in all the message; parses the message into header info
* (OpType + msg_len) and the request portion (used by the handle_xx fns)
* input: sock_info - socket msg is read from
* op_t - the operation type specified in the msg
* msg - the data from the network message (no OpType or msg_len)
* output: INK_ERR_xx ( if INK_ERR_OKAY, then parameters set successfully)
* notes: Since preprocess_msg already removes the OpType and msg_len, this part o
* the message is not dealt with by the other parsing functions
**********************************************************************/
INKError
preprocess_msg(struct SocketInfo sock_info, OpType * op_t, char **req)
{
INKError ret;
int req_len;
ink16 op;
// read operation type
ret = socket_read_n(sock_info, (char *) &op, SIZE_OP_T);
if (ret != INK_ERR_OKAY) {
Debug("ts_main", "[preprocess_msg] ERROR %d reading op type\n", ret);
goto Lerror;
}
Debug("ts_main", "[preprocess_msg] operation = %d", op);
*op_t = (OpType) op; // convert to proper format
// check if invalid op type
if ((int) op >= TOTAL_NUM_OP_TYPES) {
mgmt_elog("[preprocess_msg] ERROR: %d is invalid op type\n", op);
// need to flush the invalid message from the socket
if ((ret = socket_flush(sock_info)) != INK_ERR_NET_EOF)
mgmt_log("[preprocess_msg] unsuccessful socket flushing\n");
else
mgmt_log("[preprocess_msg] successfully flushed the socket\n");
goto Lerror;
}
// now read the request msg size
ret = socket_read_n(sock_info, (char *) &req_len, SIZE_LEN);
if (ret != INK_ERR_OKAY) {
mgmt_elog("[preprocess_msg] ERROR %d reading msg size\n", ret);
Debug("ts_main", "[preprocess_msg] ERROR %d reading msg size\n", ret);
goto Lerror;
}
Debug("ts_main", "[preprocess_msg] length = %d\n", req_len);
// use req msg length to fetch the rest of the message
// first check that there is a "rest of the msg", some msgs just
// have the op specified
if (req_len == 0) {
*req = NULL;
Debug("ts_main", "[preprocess_msg] request message = NULL\n");
} else {
*req = (char *) xmalloc(sizeof(char) * (req_len + 1));
if (!(*req)) {
ret = INK_ERR_SYS_CALL;
goto Lerror;
}
ret = socket_read_n(sock_info, *req, req_len);
if (ret != INK_ERR_OKAY) {
xfree(*req);
goto Lerror;
}
// add end of string to end of msg
(*req)[req_len] = '\0';
Debug("ts_main", "[preprocess_msg] request message = %s\n", *req);
}
return INK_ERR_OKAY;
Lerror:
return ret;
}
/**********************************************************************
* Unmarshal Requests
**********************************************************************/
/**********************************************************************
* parse_file_read_request
*
* purpose: parses a file read request from a remote API client
* input: req - data that needs to be parsed
* file - the file type sent in the request
* output: INK_ERR_xx
* notes: request format = <INKFileNameT>
**********************************************************************/
INKError
parse_file_read_request(char *req, INKFileNameT * file)
{
ink16 file_t;
if (!req || !file)
return INK_ERR_PARAMS;
// get file type - copy first 2 bytes of request
memcpy(&file_t, req, SIZE_FILE_T);
*file = (INKFileNameT) file_t;
return INK_ERR_OKAY;
}
/**********************************************************************
* parse_file_write_request
*
* purpose: parses a file write request from a remote API client
* input: socket info
* file - the file type to write that was sent in the request
* text - the text that needs to be written
* size - length of the text
* ver - version of the file that is to be written
* output: INK_ERR_xx
* notes: request format = <INKFileNameT> <version> <size> <text>
**********************************************************************/
INKError
parse_file_write_request(char *req, INKFileNameT * file, int *ver, int *size, char **text)
{
ink16 file_t, f_ver;
ink32 f_size;
// check input is non-NULL
if (!req || !file || !ver || !size || !text)
return INK_ERR_PARAMS;
// get file type - copy first 2 bytes of request
memcpy(&file_t, req, SIZE_FILE_T);
*file = (INKFileNameT) file_t;
// get file version - copy next 2 bytes
memcpy(&f_ver, req + SIZE_FILE_T, SIZE_VER);
*ver = (int) f_ver;
// get file size - copy next 4 bytes
memcpy(&f_size, req + SIZE_FILE_T + SIZE_VER, SIZE_LEN);
*size = (int) f_size;
// get file text
*text = (char *) xmalloc(sizeof(char) * (f_size + 1));
if (!(*text))
return INK_ERR_SYS_CALL;
memcpy(*text, req + SIZE_FILE_T + SIZE_VER + SIZE_LEN, f_size);
(*text)[f_size] = '\0'; // end buffer
return INK_ERR_OKAY;
}
/**********************************************************************
* parse_request_name_value
*
* purpose: parses a request w/ 2 args from a remote API client
* input: req - request info from requestor
* name - first arg
* val - second arg
* output: INK_ERR_xx
* notes: format= <name_len> <val_len> <name> <val>
**********************************************************************/
INKError
parse_request_name_value(char *req, char **name_1, char **val_1)
{
ink32 name_len, val_len;
char *name, *val;
if (!req || !name_1 || !val_1)
return INK_ERR_PARAMS;
// get record name length
memcpy(&name_len, req, SIZE_LEN);
// get record value length
memcpy(&val_len, req + SIZE_LEN, SIZE_LEN);
// get record name
name = (char *) xmalloc(sizeof(char) * (name_len + 1));
if (!name)
return INK_ERR_SYS_CALL;
memcpy(name, req + SIZE_LEN + SIZE_LEN, name_len);
name[name_len] = '\0'; // end string
*name_1 = name;
// get record value - can be a MgmtInt, MgmtCounter ...
val = (char *) xmalloc(sizeof(char) * (val_len + 1));
if (!val)
return INK_ERR_SYS_CALL;
memcpy(val, req + SIZE_LEN + SIZE_LEN + name_len, val_len);
val[val_len] = '\0'; // end string
*val_1 = val;
return INK_ERR_OKAY;
}
/**********************************************************************
* parse_diags_request
*
* purpose: parses a diags request
* input: diag_msg - the diag msg to be outputted
* mode - indicates what type of diag message
* output: INK_ERR_xx
* notes: request format = <INKDiagsT> <diag_msg_len> <diag_msg>
**********************************************************************/
INKError
parse_diags_request(char *req, INKDiagsT * mode, char **diag_msg)
{
ink16 diag_t;
ink32 msg_len;
// check input is non-NULL
if (!req || !mode || !diag_msg)
return INK_ERR_PARAMS;
// get diags type - copy first 2 bytes of request
memcpy(&diag_t, req, SIZE_DIAGS_T);
*mode = (INKDiagsT) diag_t;
// get msg size - copy next 4 bytes
memcpy(&msg_len, req + SIZE_DIAGS_T, SIZE_LEN);
// get msg
*diag_msg = (char *) xmalloc(sizeof(char) * (msg_len + 1));
if (!(*diag_msg))
return INK_ERR_SYS_CALL;
memcpy(*diag_msg, req + SIZE_DIAGS_T + SIZE_LEN, msg_len);
(*diag_msg)[msg_len] = '\0'; // end buffer
return INK_ERR_OKAY;
}
/**********************************************************************
* parse_proxy_state_request
*
* purpose: parses a request to set the proxy state
* input: diag_msg - the diag msg to be outputted
* mode - indicates what type of diag message
* output: INK_ERR_xx
* notes: request format = <INKProxyStateT> <INKCacheClearT>
**********************************************************************/
INKError
parse_proxy_state_request(char *req, INKProxyStateT * state, INKCacheClearT * clear)
{
ink16 state_t, cache_t;
// check input is non-NULL
if (!req || !state || !clear)
return INK_ERR_PARAMS;
// get proxy on/off
memcpy(&state_t, req, SIZE_PROXY_T);
*state = (INKProxyStateT) state_t;
// get cahce-clearing type
memcpy(&cache_t, req + SIZE_PROXY_T, SIZE_TS_ARG_T);
*clear = (INKCacheClearT) cache_t;
return INK_ERR_OKAY;
}
/**********************************************************************
* Marshal Replies
**********************************************************************/
/* NOTE: if the send function "return"s before writing to the socket
then that means that an error occurred, and so the calling function
must send_reply with the error that occurred. */
/**********************************************************************
* send_reply
*
* purpose: sends a simple INK_ERR_* reply to the request made
* input: return value - could be extended to support more complex
* error codes but for now use only INK_ERR_FAIL, INK_ERR_OKAY
* int fd - socket fd to use.
* output: INK_ERR_*
* notes: this function does not need to go through the internal structure
* so no cleaning up is done.
**********************************************************************/
INKError
send_reply(struct SocketInfo sock_info, INKError retval)
{
INKError ret;
char msg[SIZE_ERR_T];
ink16 ret_val;
// write the return value
ret_val = (ink16) retval;
memcpy(msg, (void *) &ret_val, SIZE_ERR_T);
// now push it to the socket
ret = socket_write_n(sock_info, msg, SIZE_ERR_T);
return ret;
}
/**********************************************************************
* send_reply_list
*
* purpose: sends the reply in response to a request to get list of string
* tokens (delimited by REMOTE_DELIM_STR)
* input: sock_info -
* retval - INKError return type for the CoreAPI call
* list - string delimited list of string tokens
* output: INK_ERR_*
* notes:
* format: <INKError> <string_list_len> <delimited_string_list>
**********************************************************************/
INKError
send_reply_list(struct SocketInfo sock_info, INKError retval, char *list)
{
INKError ret;
int msg_pos = 0, total_len;
char *msg;
ink16 ret_val;
ink32 list_size; // to be safe, typecast
if (!list) {
return INK_ERR_PARAMS;
}
total_len = SIZE_ERR_T + SIZE_LEN + strlen(list);
msg = (char *) xmalloc(sizeof(char) * total_len);
if (!msg)
return INK_ERR_SYS_CALL; // ERROR - malloc failed
// write the return value
ret_val = (ink16) retval;
memcpy(msg, (void *) &ret_val, SIZE_ERR_T);
msg_pos += SIZE_ERR_T;
// write the length of the string list
list_size = (ink32) strlen(list);
memcpy(msg + msg_pos, (void *) &list_size, SIZE_LEN);
msg_pos += SIZE_LEN;
// write the event string list
memcpy(msg + msg_pos, list, list_size);
// now push it to the socket
ret = socket_write_n(sock_info, msg, total_len);
xfree(msg);
return ret;
}
/**********************************************************************
* send_record_get_reply
*
* purpose: sends reply to the record_get request made
* input: retval - result of the record get request
* int fd - socket fd to use.
* val - the value of the record requested
* val_size - num bytes the value occupies
* rec_type - the type of the record value requested
* output: INK_ERR_*
* notes: this function does not need to go through the internal structure
* so no cleaning up is done.
* format = <INKError> <rec_val_len> <rec_type> <rec_val>
**********************************************************************/
INKError
send_record_get_reply(struct SocketInfo sock_info, INKError retval, void *val, int val_size, INKRecordT rec_type)
{
INKError ret;
int msg_pos = 0, total_len;
char *msg;
ink16 record_t, ret_val;
ink32 v_size; // to be safe, typecast
if (!val) {
return INK_ERR_PARAMS;
}
total_len = SIZE_ERR_T + SIZE_LEN + SIZE_REC_T + val_size;
msg = (char *) xmalloc(sizeof(char) * total_len);
if (!msg)
return INK_ERR_SYS_CALL; // ERROR - malloc failed
// write the return value
ret_val = (ink16) retval;
memcpy(msg, (void *) &ret_val, SIZE_ERR_T);
msg_pos += SIZE_ERR_T;
// write the size of the record value
v_size = (ink32) val_size;
memcpy(msg + msg_pos, (void *) &v_size, SIZE_LEN);
msg_pos += SIZE_LEN;
// write the record type
record_t = (ink16) rec_type;
memcpy(msg + msg_pos, (void *) &record_t, SIZE_REC_T);
msg_pos += SIZE_REC_T;
// write the record value
memcpy(msg + msg_pos, val, val_size);
// now push it to the socket
ret = socket_write_n(sock_info, msg, total_len);
xfree(msg);
return ret;
}
/**********************************************************************
* send_record_set_reply
*
* purpose: sends reply to the record_set request made
* input:
* output: INK_ERR_*
* notes: this function does not need to go through the internal structure
* so no cleaning up is done.
* format =
**********************************************************************/
INKError
send_record_set_reply(struct SocketInfo sock_info, INKError retval, INKActionNeedT action_need)
{
INKError ret;
int total_len;
char *msg;
ink16 action_t, ret_val;
total_len = SIZE_ERR_T + SIZE_ACTION_T;
msg = (char *) xmalloc(sizeof(char) * total_len);
if (!msg)
return INK_ERR_SYS_CALL; // ERROR - malloc failed!
// write the return value
ret_val = (ink16) retval;
memcpy(msg, (void *) &ret_val, SIZE_ERR_T);
// write the action needed
action_t = (ink16) action_need;
memcpy(msg + SIZE_ERR_T, (void *) &action_t, SIZE_ACTION_T);
// now push it to the socket
ret = socket_write_n(sock_info, msg, total_len);
xfree(msg);
return ret;
}
/**********************************************************************
* send_file_read_reply
*
* purpose: sends the reply in response to a file read request
* input: return value - could be extended to support more complex
* error codes but for now use only INK_ERR_FAIL, INK_ERR_OKAY
* int fd - socket fd to use.
* output: INK_ERR_*
* notes: this function does not need to go through the internal structure
* so no cleaning up is done.
* reply format = <INKError> <file_ver> <file_size> <file_text>
**********************************************************************/
INKError
send_file_read_reply(struct SocketInfo sock_info, INKError retval, int ver, int size, char *text)
{
INKError ret;
int msg_pos = 0, msg_len;
char *msg;
ink16 ret_val, f_ver;
ink32 f_size; // to be safe
if (!text)
return INK_ERR_PARAMS;
// allocate space for buffer
msg_len = SIZE_ERR_T + SIZE_VER + SIZE_LEN + size;
msg = (char *) xmalloc(sizeof(char) * msg_len);
if (!msg)
return INK_ERR_SYS_CALL;
// write the return value
ret_val = (ink16) retval;
memcpy(msg, (void *) &ret_val, SIZE_ERR_T);
msg_pos += SIZE_ERR_T;
// write file version
f_ver = (ink16) ver;
memcpy(msg + msg_pos, (void *) &f_ver, SIZE_VER);
msg_pos += SIZE_VER;
// write file size
f_size = (ink32) size;
memcpy(msg + msg_pos, (void *) &f_size, SIZE_LEN);
msg_pos += SIZE_LEN;
// write the file text
memcpy(msg + msg_pos, text, size);
// now push it to the socket
ret = socket_write_n(sock_info, msg, msg_len);
xfree(msg);
return ret;
}
/**********************************************************************
* send_proxy_state_get_reply
*
* purpose: sends the reply in response to a request to get state of proxy
* input:
* int fd - socket fd to use.
* output: INK_ERR_*
* notes: this function DOES NOT HAVE IT"S OWN INKError TO SEND!!!!
* reply format = <INKProxyStateT>
**********************************************************************/
INKError
send_proxy_state_get_reply(struct SocketInfo sock_info, INKProxyStateT state)
{
INKError ret;
char msg[SIZE_PROXY_T];
ink16 state_t;
// write the state
state_t = (ink16) state;
memcpy(msg, (void *) &state_t, SIZE_PROXY_T);
// now push it to the socket
ret = socket_write_n(sock_info, msg, SIZE_PROXY_T);
return ret;
}
/**********************************************************************
* send_event_active_reply
*
* purpose: sends the reply in response to a request check if event is active
* input: sock_info -
* retval - INKError return type for the EventIsActive core call
* active - is the requested event active or not?
* output: INK_ERR_*
* notes:
* format: <INKError> <bool>
**********************************************************************/
INKError
send_event_active_reply(struct SocketInfo sock_info, INKError retval, bool active)
{
INKError ret;
int total_len;
char *msg;
ink16 is_active, ret_val;
total_len = SIZE_ERR_T + SIZE_BOOL;
msg = (char *) xmalloc(sizeof(char) * total_len);
if (!msg)
return INK_ERR_SYS_CALL; // ERROR - malloc failed!
// write the return value
ret_val = (ink16) retval;
memcpy(msg, (void *) &ret_val, SIZE_ERR_T);
// write the boolean active state
is_active = (ink16) active;
memcpy(msg + SIZE_ERR_T, (void *) &is_active, SIZE_BOOL);
// now push it to the socket
ret = socket_write_n(sock_info, msg, total_len);
xfree(msg);
return ret;
}
/**********************************************************************
* send_event_notification
*
* purpose: sends to the client a msg indicating that a certain event
* has occurred (this msg will be received by the event_poll_thread)
* input: fd - file descriptor to use for writing
* event - the event that was signalled on TM side
* output: INK_ERR_xx
* note: format: <OpType> <event_name_len> <event_name> <desc_len> <desc>
**********************************************************************/
INKError
send_event_notification(struct SocketInfo sock_info, INKEvent * event)
{
INKError ret;
int total_len, name_len, desc_len;
char *msg;
ink16 op_t;
ink32 len;
if (!event || !event->name || !event->description)
return INK_ERR_PARAMS;
name_len = strlen(event->name);
desc_len = strlen(event->description);
total_len = SIZE_OP_T + (SIZE_LEN * 2) + name_len + desc_len;
msg = (char *) xmalloc(sizeof(char) * total_len);
if (!msg)
return INK_ERR_SYS_CALL; // ERROR - malloc failed!
// write the operation
op_t = (ink16) EVENT_NOTIFY;
memcpy(msg, (void *) &op_t, SIZE_OP_T);
// write the size of the event name
len = (ink32) name_len;
memcpy(msg + SIZE_OP_T, (void *) &len, SIZE_LEN);
// write the event name
memcpy(msg + SIZE_OP_T + SIZE_LEN, event->name, name_len);
// write size of description
len = (ink32) desc_len;
memcpy(msg + SIZE_OP_T + SIZE_LEN + name_len, (void *) &len, SIZE_LEN);
// write the description
memcpy(msg + SIZE_OP_T + SIZE_LEN + name_len + SIZE_LEN, event->description, desc_len);
// now push it to the socket
ret = socket_write_n(sock_info, msg, total_len);
xfree(msg);
return ret;
}