/* * Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren This file is part of CLIXON. CLIXON is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. CLIXON is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with CLIXON; see the file LICENSE. If not, see . * * Protocol to communicate between clients (eg clicon_cli, clicon_netconf) * and server (clicon_backend) */ #ifdef HAVE_CONFIG_H #include "clixon_config.h" /* generated by config & autoconf */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* cligen */ #include /* clicon */ #include "clixon_err.h" #include "clixon_log.h" #include "clixon_queue.h" #include "clixon_chunk.h" #include "clixon_sig.h" #include "clixon_hash.h" #include "clixon_handle.h" #include "clixon_proto.h" #include "clixon_proto_encode.h" struct clicon_msg * clicon_msg_commit_encode(char *dbsrc, char *dbdst, uint32_t snapshot, uint32_t startup, const char *label) { struct clicon_msg *msg; uint16_t len; int hdrlen = sizeof(*msg); int p; uint32_t tmp; clicon_debug(2, "%s: snapshot: %d startup: %d dbsrc: %s dbdst: %s", __FUNCTION__, snapshot, startup, dbsrc, dbdst); p = 0; len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(dbsrc) + 1 + strlen(dbdst) + 1; if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return NULL; } memset(msg, 0, len); /* hdr */ msg->op_type = htons(CLICON_MSG_COMMIT); msg->op_len = htons(len); /* body */ tmp = htonl(snapshot); memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); p += sizeof(uint32_t); tmp = htonl(startup); memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); p += sizeof(uint32_t); strncpy(msg->op_body+p, dbsrc, len-p-hdrlen); p += strlen(dbsrc)+1; strncpy(msg->op_body+p, dbdst, len-p-hdrlen); p += strlen(dbdst)+1; return msg; } int clicon_msg_commit_decode(struct clicon_msg *msg, char **dbsrc, char **dbdst, uint32_t *snapshot, uint32_t *startup, const char *label) { int p; uint32_t tmp; p = 0; /* body */ memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); *snapshot = ntohl(tmp); p += sizeof(uint32_t); memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); *startup = ntohl(tmp); p += sizeof(uint32_t); if ((*dbsrc = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*dbsrc)+1; if ((*dbdst = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*dbdst)+1; clicon_debug(2, "%s: snapshot: %d startup: %d dbsrc: %s dbdst: %s", __FUNCTION__, *snapshot, *startup, *dbsrc, *dbdst); return 0; } struct clicon_msg * clicon_msg_validate_encode(char *db, const char *label) { struct clicon_msg *msg; uint16_t len; int hdrlen = sizeof(*msg); clicon_debug(2, "%s: db: %s", __FUNCTION__, db); len = sizeof(*msg) + strlen(db) + 1; if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return NULL; } memset(msg, 0, len); /* hdr */ msg->op_type = htons(CLICON_MSG_VALIDATE); msg->op_len = htons(len); /* body */ strncpy(msg->op_body, db, len-hdrlen); return msg; } int clicon_msg_validate_decode(struct clicon_msg *msg, char **db, const char *label) { /* body */ if ((*db = chunk_sprintf(label, "%s", msg->op_body)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } clicon_debug(2, "%s: db: %s", __FUNCTION__, *db); return 0; } struct clicon_msg * clicon_msg_change_encode(char *db, uint32_t op, char *key, char *str, uint32_t str_len, const char *label) { struct clicon_msg *msg; uint16_t len; int hdrlen = sizeof(*msg); int p; uint32_t tmp; clicon_debug(2, "%s: op: %d str_len: %d db: %s key: '%s'", __FUNCTION__, op, str_len, db, key); p = 0; len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(db) + 1 + strlen(key) + 1 + str_len; if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return NULL; } memset(msg, 0, len); /* hdr */ msg->op_type = htons(CLICON_MSG_CHANGE); msg->op_len = htons(len); /* body */ tmp = htonl(op); memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); p += sizeof(uint32_t); tmp = htonl(str_len); memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); p += sizeof(uint32_t); strncpy(msg->op_body+p, db, len-p-hdrlen); p += strlen(db)+1; strncpy(msg->op_body+p, key, len-p-hdrlen); p += strlen(key)+1; memcpy(msg->op_body+p, str, str_len); p += str_len; return msg; } int clicon_msg_change_decode(struct clicon_msg *msg, char **db, uint32_t *op, char **key, char **str, uint32_t *str_len, const char *label) { int p; uint32_t tmp; p = 0; /* body */ memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); *op = ntohl(tmp); p += sizeof(uint32_t); memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); *str_len = ntohl(tmp); p += sizeof(uint32_t); if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*db)+1; if ((*key = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*key)+1; if (*str_len){ if ((*str = chunk(*str_len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return -1; } memcpy(*str, msg->op_body+p, *str_len); p += *str_len; } else *str = NULL; clicon_debug(2, "%s: op: %d str_len: %d db: %s key: '%s'", __FUNCTION__, *op, *str_len, *db, *key); return 0; } /*! Encode xmlput / edit of database content * @param[in] db Name of database * @param[in] op set|merge|delete. See lv_op_t * @param[in] xml XML data string * @param[in] label Memory chunk label * @retval msg Encoded message * @retval NULL Error */ struct clicon_msg * clicon_msg_xmlput_encode(char *db, uint32_t op, char *xml, const char *label) { struct clicon_msg *msg; uint16_t len; int hdrlen = sizeof(*msg); int p; uint32_t tmp; clicon_debug(2, "%s: op: %d db: %s xml: %s", __FUNCTION__, op, db, xml); p = 0; hdrlen = sizeof(*msg); len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1 + strlen(xml) + 1; if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return NULL; } memset(msg, 0, len); /* hdr */ msg->op_type = htons(CLICON_MSG_XMLPUT); msg->op_len = htons(len); /* body */ tmp = htonl(op); memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); p += sizeof(uint32_t); strncpy(msg->op_body+p, db, len-p-hdrlen); p += strlen(db)+1; strncpy(msg->op_body+p, xml, len-p-hdrlen); p += strlen(xml)+1; return msg; } /*! Decode xmlput / edit of database content * @param[in] msg Incoming message to be decoded * @param[out] db Name of database * @param[out] op set|merge|delete. See lv_op_t * @param[out] xml XML data string * @param[in] label Memory chunk label * @retval 0 OK * @retval -1 Error */ int clicon_msg_xmlput_decode(struct clicon_msg *msg, char **db, uint32_t *op, char **xml, const char *label) { int p; uint32_t tmp; p = 0; /* body */ memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); *op = ntohl(tmp); p += sizeof(uint32_t); if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*db)+1; if ((*xml = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*xml)+1; clicon_debug(2, "%s: op: %d db: %s xml: %s", __FUNCTION__, *op, *db, *xml); return 0; } struct clicon_msg * clicon_msg_save_encode(char *db, uint32_t snapshot, char *filename, const char *label) { struct clicon_msg *msg; uint16_t len; int hdrlen = sizeof(*msg); int p; uint32_t tmp; clicon_debug(2, "%s: snapshot: %d db: %s filename: %s", __FUNCTION__, snapshot, db, filename); p = 0; hdrlen = sizeof(*msg); len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1; if (!snapshot) len += strlen(filename) + 1; if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return NULL; } memset(msg, 0, len); /* hdr */ msg->op_type = htons(CLICON_MSG_SAVE); msg->op_len = htons(len); /* body */ tmp = htonl(snapshot); memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); p += sizeof(uint32_t); strncpy(msg->op_body+p, db, len-p-hdrlen); p += strlen(db)+1; if (!snapshot){ strncpy(msg->op_body+p, filename, len-p-hdrlen); p += strlen(filename)+1; } return msg; } int clicon_msg_save_decode(struct clicon_msg *msg, char **db, uint32_t *snapshot, char **filename, const char *label) { int p; uint32_t tmp; p = 0; /* body */ memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); *snapshot = ntohl(tmp); p += sizeof(uint32_t); if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*db)+1; if (*snapshot == 0){ if ((*filename = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*filename)+1; } clicon_debug(2, "%s: snapshot: %d db: %s filename: %s", __FUNCTION__, *snapshot, *db, *filename); return 0; } struct clicon_msg * clicon_msg_load_encode(int replace, char *db, char *filename, const char *label) { struct clicon_msg *msg; int hdrlen = sizeof(*msg); uint16_t len; uint32_t tmp; int p; clicon_debug(2, "%s: replace: %d db: %s filename: %s", __FUNCTION__, replace, db, filename); p = 0; len = sizeof(*msg) + sizeof(uint32_t) + strlen(db) + 1 + strlen(filename) + 1; if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return NULL; } memset(msg, 0, len); /* hdr */ msg->op_type = htons(CLICON_MSG_LOAD); msg->op_len = htons(len); /* body */ tmp = htonl(replace); memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); p += sizeof(uint32_t); strncpy(msg->op_body+p, db, len-p-hdrlen); p += strlen(db)+1; strncpy(msg->op_body+p, filename, len-p-hdrlen); p += strlen(filename)+1; return msg; } int clicon_msg_load_decode(struct clicon_msg *msg, int *replace, char **db, char **filename, const char *label) { int p; uint32_t tmp; p = 0; /* body */ memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); *replace = ntohl(tmp); p += sizeof(uint32_t); if ((*db = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*db)+1; if ((*filename = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*filename)+1; clicon_debug(2, "%s: %d db: %s filename: %s", __FUNCTION__, ntohs(msg->op_type), *db, *filename); return 0; } struct clicon_msg * clicon_msg_kill_encode(uint32_t session_id, const char *label) { struct clicon_msg *msg; uint16_t len; int p; uint32_t tmp; clicon_debug(2, "%s: %d", __FUNCTION__, session_id); p = 0; len = sizeof(*msg) + sizeof(uint32_t); if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return NULL; } memset(msg, 0, len); /* hdr */ msg->op_type = htons(CLICON_MSG_KILL); msg->op_len = htons(len); /* body */ tmp = htonl(session_id); memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); p += sizeof(uint32_t); return msg; } int clicon_msg_kill_decode(struct clicon_msg *msg, uint32_t *session_id, const char *label) { int p; uint32_t tmp; p = 0; /* body */ memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); *session_id = ntohl(tmp); p += sizeof(uint32_t); clicon_debug(2, "%s: session-id: %u", __FUNCTION__, *session_id); return 0; } struct clicon_msg * clicon_msg_debug_encode(uint32_t level, const char *label) { struct clicon_msg *msg; uint16_t len; int p; uint32_t tmp; clicon_debug(2, "%s: %d", __FUNCTION__, label); p = 0; len = sizeof(*msg) + sizeof(uint32_t); if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return NULL; } memset(msg, 0, len); /* hdr */ msg->op_type = htons(CLICON_MSG_DEBUG); msg->op_len = htons(len); /* body */ tmp = htonl(level); memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); p += sizeof(uint32_t); return msg; } int clicon_msg_debug_decode(struct clicon_msg *msg, uint32_t *level, const char *label) { int p; uint32_t tmp; p = 0; /* body */ memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); *level = ntohl(tmp); p += sizeof(uint32_t); clicon_debug(2, "%s: session-id: %u", __FUNCTION__, *level); return 0; } struct clicon_msg * clicon_msg_call_encode(uint16_t op, char *plugin, char *func, uint16_t arglen, void *arg, const char *label) { struct clicon_msg *msg; struct clicon_msg_call_req *req; int hdrlen = sizeof(*msg); int len; clicon_debug(2, "%s: %d plugin: %s func: %s arglen: %d", __FUNCTION__, op, plugin, func, arglen); len = hdrlen + sizeof(struct clicon_msg_call_req) + strlen(plugin) + 1 + strlen(func) + 1 + arglen; if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return NULL; } memset(msg, 0, len); /* hdr */ msg->op_type = htons(CLICON_MSG_CALL); msg->op_len = htons(len); /* req */ req = (struct clicon_msg_call_req *)msg->op_body; req->cr_len = htons(len - hdrlen); req->cr_op = htons(op); req->cr_plugin = req->cr_data; strncpy(req->cr_plugin, plugin, strlen(plugin)); req->cr_func = req->cr_plugin + strlen(req->cr_plugin) + 1; strncpy(req->cr_func, func, strlen(func)); req->cr_arglen = htons(arglen); req->cr_arg = req->cr_func + strlen(req->cr_func) + 1; memcpy(req->cr_arg, arg, arglen); return msg; } int clicon_msg_call_decode(struct clicon_msg *msg, struct clicon_msg_call_req **req, const char *label) { uint16_t len; struct clicon_msg_call_req *r; r = (struct clicon_msg_call_req *)msg->op_body; len = ntohs(r->cr_len); if ((*req = chunk(len, label)) == NULL) { clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return -1; } memcpy(*req, r, len); (*req)->cr_len = ntohs(r->cr_len); (*req)->cr_op = ntohs(r->cr_op); (*req)->cr_arglen = ntohs(r->cr_arglen); (*req)->cr_plugin = (*req)->cr_data; (*req)->cr_func = (*req)->cr_plugin + strlen((*req)->cr_plugin) +1; (*req)->cr_arg = (*req)->cr_func + strlen((*req)->cr_func) +1; return 0; } struct clicon_msg * clicon_msg_subscription_encode(int status, char *stream, enum format_enum format, char *filter, const char *label) { struct clicon_msg *msg; uint16_t len; int hdrlen = sizeof(*msg); int p; int tmp; clicon_debug(2, "%s: %d %d %s %s", __FUNCTION__, status, format, stream, filter); p = 0; assert(filter); len = hdrlen + sizeof(uint32_t) + sizeof(uint32_t) + strlen(stream) + 1 + strlen(filter) + 1; if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return NULL; } memset(msg, 0, len); /* hdr */ msg->op_type = htons(CLICON_MSG_SUBSCRIPTION); msg->op_len = htons(len); /* body */ tmp = htonl(status); memcpy(msg->op_body+p, &tmp, sizeof(int)); p += sizeof(int); tmp = htonl(format); memcpy(msg->op_body+p, &tmp, sizeof(int)); p += sizeof(int); strncpy(msg->op_body+p, stream, len-p-hdrlen); p += strlen(stream)+1; strncpy(msg->op_body+p, filter, len-p-hdrlen); p += strlen(filter)+1; return msg; } int clicon_msg_subscription_decode(struct clicon_msg *msg, int *status, char **stream, enum format_enum *format, char **filter, const char *label) { int p; int tmp; p = 0; /* body */ memcpy(&tmp, msg->op_body+p, sizeof(int)); *status = ntohl(tmp); p += sizeof(int); memcpy(&tmp, msg->op_body+p, sizeof(int)); *format = ntohl(tmp); p += sizeof(int); if ((*stream = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*stream)+1; if ((*filter = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*filter)+1; clicon_debug(2, "%s: %d %s %d %s", __FUNCTION__, *status, *filter, *stream, *filter); return 0; } struct clicon_msg * clicon_msg_notify_encode(int level, char *event, const char *label) { struct clicon_msg *msg; uint16_t len; int hdrlen = sizeof(*msg); int p; int tmp; clicon_debug(2, "%s: %d %s", __FUNCTION__, level, event); p = 0; hdrlen = sizeof(*msg); len = sizeof(*msg) + sizeof(uint32_t) + strlen(event) + 1; if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return NULL; } memset(msg, 0, len); /* hdr */ msg->op_type = htons(CLICON_MSG_NOTIFY); msg->op_len = htons(len); /* body */ tmp = htonl(level); memcpy(msg->op_body+p, &tmp, sizeof(int)); p += sizeof(int); strncpy(msg->op_body+p, event, len-p-hdrlen); p += strlen(event)+1; return msg; } int clicon_msg_notify_decode(struct clicon_msg *msg, int *level, char **event, const char *label) { int p; int tmp; p = 0; /* body */ memcpy(&tmp, msg->op_body+p, sizeof(int)); *level = ntohl(tmp); p += sizeof(int); if ((*event = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*event)+1; clicon_debug(2, "%s: %d %s", __FUNCTION__, *level, *event); return 0; } struct clicon_msg * clicon_msg_err_encode(uint32_t err, uint32_t suberr, char *reason, const char *label) { struct clicon_msg *msg; uint16_t len; int hdrlen = sizeof(*msg); int p; uint32_t tmp; clicon_debug(2, "%s: %d %d %s", __FUNCTION__, err, suberr, reason); p = 0; hdrlen = sizeof(*msg); len = sizeof(*msg) + 2*sizeof(uint32_t) + strlen(reason) + 1; if ((msg = (struct clicon_msg *)chunk(len, label)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk", __FUNCTION__); return NULL; } memset(msg, 0, len); /* hdr */ msg->op_type = htons(CLICON_MSG_ERR); msg->op_len = htons(len); /* body */ tmp = htonl(err); memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); p += sizeof(uint32_t); tmp = htonl(suberr); memcpy(msg->op_body+p, &tmp, sizeof(uint32_t)); p += sizeof(uint32_t); strncpy(msg->op_body+p, reason, len-p-hdrlen); p += strlen(reason)+1; return msg; } int clicon_msg_err_decode(struct clicon_msg *msg, uint32_t *err, uint32_t *suberr, char **reason, const char *label) { int p; uint32_t tmp; p = 0; /* body */ memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); *err = ntohl(tmp); p += sizeof(uint32_t); memcpy(&tmp, msg->op_body+p, sizeof(uint32_t)); *suberr = ntohl(tmp); p += sizeof(uint32_t); if ((*reason = chunk_sprintf(label, "%s", msg->op_body+p)) == NULL){ clicon_err(OE_PROTO, errno, "%s: chunk_sprintf", __FUNCTION__); return -1; } p += strlen(*reason)+1; clicon_debug(2, "%s: %d %d %s", __FUNCTION__, *err, *suberr, *reason); return 0; }