blob: 3f05d18b9d842ec954df0ed8e3885e747905453d [file] [log] [blame]
/** @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
**************************************************************************/
TSError
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 TS_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(0, "[socket_flush] (TS_ERR_NET_READ) %s\n", strerror(errno));
return TS_ERR_NET_READ;
}
if (ret == 0) {
Debug("ts_main", "[socket_read_n] returned 0 on reading: %s.\n", strerror(errno));
return TS_ERR_NET_EOF;
}
// we are all good here
byte_read += ret;
}
mgmt_elog(0, "[socket_flush] uh oh! didn't finish flushing socket!\n");
return TS_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
*************************************************************************/
TSError
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 TS_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(0, "[socket_read_n] (TS_ERR_NET_READ) %s\n", strerror(errno));
return TS_ERR_NET_READ;
}
if (ret == 0) {
Debug("ts_main", "[socket_read_n] returned 0 on reading: %s.\n", strerror(errno));
return TS_ERR_NET_EOF;
}
// we are all good here
byte_read += ret;
}
return TS_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: TS_ERR_xx (depends on num bytes written)
* note: socket_read is implemented in WebUtils.cc
*************************************************************************/
TSError
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 TS_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(0, "[socket_write_n] %s\n", strerror(errno));
if (errno == EAGAIN)
continue;
return TS_ERR_NET_WRITE;
}
if (ret == 0) {
mgmt_elog(0, "[socket_write_n] %s\n", strerror(errno));
return TS_ERR_NET_EOF;
}
// we are all good here
byte_wrote += ret;
}
return TS_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: TS_ERR_xx ( if TS_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
**********************************************************************/
TSError
preprocess_msg(struct SocketInfo sock_info, OpType * op_t, char **req)
{
TSError ret;
int req_len;
int16_t op;
// read operation type
ret = socket_read_n(sock_info, (char *) &op, SIZE_OP_T);
if (ret != TS_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 > UNDEFINED_OP) {
mgmt_elog(0, "[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)) != TS_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 != TS_ERR_OKAY) {
mgmt_elog(0, "[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 *)ats_malloc(sizeof(char) * (req_len + 1));
ret = socket_read_n(sock_info, *req, req_len);
if (ret != TS_ERR_OKAY) {
ats_free(*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 TS_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: TS_ERR_xx
* notes: request format = <TSFileNameT>
**********************************************************************/
TSError
parse_file_read_request(char *req, TSFileNameT * file)
{
int16_t file_t;
if (!req || !file)
return TS_ERR_PARAMS;
// get file type - copy first 2 bytes of request
memcpy(&file_t, req, SIZE_FILE_T);
*file = (TSFileNameT) file_t;
return TS_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: TS_ERR_xx
* notes: request format = <TSFileNameT> <version> <size> <text>
**********************************************************************/
TSError
parse_file_write_request(char *req, TSFileNameT * file, int *ver, int *size, char **text)
{
int16_t file_t, f_ver;
int32_t f_size;
// check input is non-NULL
if (!req || !file || !ver || !size || !text)
return TS_ERR_PARAMS;
// get file type - copy first 2 bytes of request
memcpy(&file_t, req, SIZE_FILE_T);
*file = (TSFileNameT) 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 *)ats_malloc(sizeof(char) * (f_size + 1));
memcpy(*text, req + SIZE_FILE_T + SIZE_VER + SIZE_LEN, f_size);
(*text)[f_size] = '\0'; // end buffer
return TS_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: TS_ERR_xx
* notes: format= <name_len> <val_len> <name> <val>
**********************************************************************/
TSError
parse_request_name_value(char *req, char **name_1, char **val_1)
{
int32_t name_len, val_len;
char *name, *val;
if (!req || !name_1 || !val_1)
return TS_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 *)ats_malloc(sizeof(char) * (name_len + 1));
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 *)ats_malloc(sizeof(char) * (val_len + 1));
memcpy(val, req + SIZE_LEN + SIZE_LEN + name_len, val_len);
val[val_len] = '\0'; // end string
*val_1 = val;
return TS_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: TS_ERR_xx
* notes: request format = <TSDiagsT> <diag_msg_len> <diag_msg>
**********************************************************************/
TSError
parse_diags_request(char *req, TSDiagsT * mode, char **diag_msg)
{
int16_t diag_t;
int32_t msg_len;
// check input is non-NULL
if (!req || !mode || !diag_msg)
return TS_ERR_PARAMS;
// get diags type - copy first 2 bytes of request
memcpy(&diag_t, req, SIZE_DIAGS_T);
*mode = (TSDiagsT) diag_t;
// get msg size - copy next 4 bytes
memcpy(&msg_len, req + SIZE_DIAGS_T, SIZE_LEN);
// get msg
*diag_msg = (char *)ats_malloc(sizeof(char) * (msg_len + 1));
memcpy(*diag_msg, req + SIZE_DIAGS_T + SIZE_LEN, msg_len);
(*diag_msg)[msg_len] = '\0'; // end buffer
return TS_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: TS_ERR_xx
* notes: request format = <TSProxyStateT> <TSCacheClearT>
**********************************************************************/
TSError
parse_proxy_state_request(char *req, TSProxyStateT * state, TSCacheClearT * clear)
{
int16_t state_t, cache_t;
// check input is non-NULL
if (!req || !state || !clear)
return TS_ERR_PARAMS;
// get proxy on/off
memcpy(&state_t, req, SIZE_PROXY_T);
*state = (TSProxyStateT) state_t;
// get cahce-clearing type
memcpy(&cache_t, req + SIZE_PROXY_T, SIZE_TS_ARG_T);
*clear = (TSCacheClearT) cache_t;
return TS_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 TS_ERR_* reply to the request made
* input: return value - could be extended to support more complex
* error codes but for now use only TS_ERR_FAIL, TS_ERR_OKAY
* int fd - socket fd to use.
* output: TS_ERR_*
* notes: this function does not need to go through the internal structure
* so no cleaning up is done.
**********************************************************************/
TSError
send_reply(struct SocketInfo sock_info, TSError retval)
{
TSError ret;
char msg[SIZE_ERR_T];
int16_t ret_val;
// write the return value
ret_val = (int16_t) 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 - TSError return type for the CoreAPI call
* list - string delimited list of string tokens
* output: TS_ERR_*
* notes:
* format: <TSError> <string_list_len> <delimited_string_list>
**********************************************************************/
TSError
send_reply_list(struct SocketInfo sock_info, TSError retval, char *list)
{
TSError ret;
int msg_pos = 0, total_len;
char *msg;
int16_t ret_val;
int32_t list_size; // to be safe, typecast
if (!list) {
return TS_ERR_PARAMS;
}
total_len = SIZE_ERR_T + SIZE_LEN + strlen(list);
msg = (char *)ats_malloc(sizeof(char) * total_len);
// write the return value
ret_val = (int16_t) retval;
memcpy(msg, (void *) &ret_val, SIZE_ERR_T);
msg_pos += SIZE_ERR_T;
// write the length of the string list
list_size = (int32_t) 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);
ats_free(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: TS_ERR_*
* notes: this function does not need to go through the internal structure
* so no cleaning up is done.
* format = <TSError> <rec_val_len> <name_size> <rec_type> <rec_val> <rec_name>
**********************************************************************/
TSError
send_record_get_reply(struct SocketInfo sock_info, TSError retval, void *val, int val_size,
TSRecordT rec_type, const char *rec_name)
{
TSError ret;
int msg_pos = 0, total_len;
char *msg;
int16_t record_t, ret_val;
int32_t v_size = (int32_t) val_size; // to be safe, typecast
int32_t n_size = rec_name ? (int32_t)strlen(rec_name) : 0;
total_len = SIZE_ERR_T + SIZE_LEN + SIZE_LEN + SIZE_REC_T + v_size + n_size;
msg = (char *)ats_malloc(sizeof(char) * total_len);
// write the return value
ret_val = (int16_t) retval;
memcpy(msg, (void *) &ret_val, SIZE_ERR_T);
msg_pos += SIZE_ERR_T;
// write the size of the record value
memcpy(msg + msg_pos, (void *) &v_size, SIZE_LEN);
msg_pos += SIZE_LEN;
// write the size of the record name
memcpy(msg + msg_pos, (void *) &n_size, SIZE_LEN);
msg_pos += SIZE_LEN;
// write the record type
record_t = (int16_t) rec_type;
memcpy(msg + msg_pos, (void *) &record_t, SIZE_REC_T);
msg_pos += SIZE_REC_T;
// write the record value
if (v_size) {
memcpy(msg + msg_pos, val, v_size);
msg_pos += v_size;
}
// write the record name
if (n_size) {
memcpy(msg + msg_pos, rec_name, n_size);
msg_pos += n_size;
}
// now push it to the socket
ret = socket_write_n(sock_info, msg, total_len);
ats_free(msg);
return ret;
}
/**********************************************************************
* send_record_set_reply
*
* purpose: sends reply to the record_set request made
* input:
* output: TS_ERR_*
* notes: this function does not need to go through the internal structure
* so no cleaning up is done.
* format =
**********************************************************************/
TSError
send_record_set_reply(struct SocketInfo sock_info, TSError retval, TSActionNeedT action_need)
{
TSError ret;
int total_len;
char *msg;
int16_t action_t, ret_val;
total_len = SIZE_ERR_T + SIZE_ACTION_T;
msg = (char *)ats_malloc(sizeof(char) * total_len);
// write the return value
ret_val = (int16_t) retval;
memcpy(msg, (void *) &ret_val, SIZE_ERR_T);
// write the action needed
action_t = (int16_t) 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);
ats_free(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 TS_ERR_FAIL, TS_ERR_OKAY
* int fd - socket fd to use.
* output: TS_ERR_*
* notes: this function does not need to go through the internal structure
* so no cleaning up is done.
* reply format = <TSError> <file_ver> <file_size> <file_text>
**********************************************************************/
TSError
send_file_read_reply(struct SocketInfo sock_info, TSError retval, int ver, int size, char *text)
{
TSError ret;
int msg_pos = 0, msg_len;
char *msg;
int16_t ret_val, f_ver;
int32_t f_size; // to be safe
if (!text)
return TS_ERR_PARAMS;
// allocate space for buffer
msg_len = SIZE_ERR_T + SIZE_VER + SIZE_LEN + size;
msg = (char *)ats_malloc(sizeof(char) * msg_len);
// write the return value
ret_val = (int16_t) retval;
memcpy(msg, (void *) &ret_val, SIZE_ERR_T);
msg_pos += SIZE_ERR_T;
// write file version
f_ver = (int16_t) ver;
memcpy(msg + msg_pos, (void *) &f_ver, SIZE_VER);
msg_pos += SIZE_VER;
// write file size
f_size = (int32_t) 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);
ats_free(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: TS_ERR_*
* notes: this function DOES NOT HAVE IT"S OWN TSError TO SEND!!!!
* reply format = <TSProxyStateT>
**********************************************************************/
TSError
send_proxy_state_get_reply(struct SocketInfo sock_info, TSProxyStateT state)
{
TSError ret;
char msg[SIZE_PROXY_T];
int16_t state_t;
// write the state
state_t = (int16_t) 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 - TSError return type for the EventIsActive core call
* active - is the requested event active or not?
* output: TS_ERR_*
* notes:
* format: <TSError> <bool>
**********************************************************************/
TSError
send_event_active_reply(struct SocketInfo sock_info, TSError retval, bool active)
{
TSError ret;
int total_len;
char *msg;
int16_t is_active, ret_val;
total_len = SIZE_ERR_T + SIZE_BOOL;
msg = (char *)ats_malloc(sizeof(char) * total_len);
// write the return value
ret_val = (int16_t) retval;
memcpy(msg, (void *) &ret_val, SIZE_ERR_T);
// write the boolean active state
is_active = (int16_t) 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);
ats_free(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: TS_ERR_xx
* note: format: <OpType> <event_name_len> <event_name> <desc_len> <desc>
**********************************************************************/
TSError
send_event_notification(struct SocketInfo sock_info, TSEvent * event)
{
TSError ret;
int total_len, name_len, desc_len;
char *msg;
int16_t op_t;
int32_t len;
if (!event || !event->name || !event->description)
return TS_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 *)ats_malloc(sizeof(char) * total_len);
// write the operation
op_t = (int16_t) EVENT_NOTIFY;
memcpy(msg, (void *) &op_t, SIZE_OP_T);
// write the size of the event name
len = (int32_t) 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 = (int32_t) 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);
ats_free(msg);
return ret;
}