/* * Copyright (c) 2001-2002 Packet Design, LLC. * All rights reserved. * * Subject to the following obligations and disclaimer of warranty, * use and redistribution of this software, in source or object code * forms, with or without modifications are expressly permitted by * Packet Design; provided, however, that: * * (i) Any and all reproductions of the source or object code * must include the copyright notice above and the following * disclaimer of warranties; and * (ii) No rights are granted, in any manner or form, to use * Packet Design trademarks, including the mark "PACKET DESIGN" * on advertising, endorsements, or otherwise except as such * appears in the above copyright notice or in the software. * * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE, * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * Author: Archie Cobbs */ #include #include #include #include #include #include #include #include #include #include #include #include "structs/structs.h" #include "structs/type/array.h" #include "structs/type/union.h" #include "structs/xml.h" #include "structs/xmlrpc.h" #include "io/boundary_fp.h" #include "io/string_fp.h" #include "sys/alog.h" #include "util/typed_mem.h" #include "http/http_defs.h" #include "http/http_server.h" #include "http/xml.h" #define MEM_TYPE "http_xml_send_xmlrpc" /* Context for http_xml_send_xmlrpc() */ struct http_xml_send_xmlrpc_ctx { struct xmlrpc_request *xreq; struct xmlrpc_response_union *xrep; }; /* * Internal functions */ static void http_xml_send_xmlrpc_cleanup(void *arg); static void http_xml_send_cleanup(void *arg); /* * Send an XML-RPC message * * Returns: * 0 Success * -1 System error, errno is set * -2 XML-RPC fault received * * The reply 'rep' may be NULL to ignore any reply value, otherwise * it should already be initialized. * * If an XML-RPC fault is received, and "faultp" is not NULL, then * it will be initialized with the fault received. * * This properly handles the calling thread's being canceled. */ int http_xml_send_xmlrpc(struct http_client *client, struct in_addr ip, u_int16_t port, int https, const char *username, const char *password, const char *methodName, u_int nparams, const struct structs_type **ptypes, const void **pdatas, const struct structs_type *rep_type, void *rep, struct xmlrpc_compact_fault *faultp, structs_xmllog_t *rlogger) { const struct structs_type *const xreq_type = &structs_type_xmlrpc_request; const struct structs_type *const xrep_type = &structs_type_xmlrpc_response; const struct structs_type *const ftype = &structs_type_xmlrpc_compact_fault; struct http_xml_send_xmlrpc_ctx *ctx; struct xmlrpc_compact_fault fault; char ebuf[128]; int ret = -1; int r; /* Get context */ if ((ctx = MALLOC(MEM_TYPE, sizeof(*ctx))) == NULL) { alogf(LOG_ERR, "%s: %m", "malloc"); return (-1); } memset(ctx, 0, sizeof(*ctx)); pthread_cleanup_push(http_xml_send_xmlrpc_cleanup, ctx); /* Build XML-RPC request and reply */ if ((ctx->xreq = structs_xmlrpc_build_request(MEM_TYPE, methodName, nparams, ptypes, pdatas)) == NULL) { alogf(LOG_ERR, "%s: %m", "structs_xmlrpc_build_request"); goto fail; } if ((ctx->xrep = MALLOC(MEM_TYPE, xrep_type->size)) == NULL) { alogf(LOG_ERR, "%s: %m", "malloc"); goto fail; } if (structs_init(xrep_type, NULL, ctx->xrep) == -1) { alogf(LOG_ERR, "%s: %m", "structs_init"); FREE(MEM_TYPE, ctx->xrep); ctx->xrep = NULL; goto fail; } #ifdef XML_RPC_DEBUG printf("%s: sending this XML-RPC request:\n", __FUNCTION__); (void)structs_xml_output(xreq_type, XML_RPC_REQUEST_TAG, NULL, ctx->xreq, stdout, NULL, 0); #endif /* Send request and get reply; note: we could get canceled here. */ r = http_xml_send(client, ip, port, https, XML_RPC_URL, username, password, XML_RPC_REQUEST_TAG, NULL, xreq_type, ctx->xreq, STRUCTS_XML_FULL, XML_RPC_REPLY_TAG, NULL, NULL, xrep_type, ctx->xrep, 0, rlogger); #ifdef XML_RPC_DEBUG printf("%s: got this XML-RPC reply (error=%s):\n", __FUNCTION__, r == -1 ? strerror(errno) : "none"); (void)structs_xml_output(xrep_type, XML_RPC_REPLY_TAG, NULL, ctx->xrep, stdout, NULL, 0); #endif /* Check error */ if (r == -1) goto fail; /* Check for fault */ if (structs_init(ftype, NULL, &fault) == -1) { alogf(LOG_ERR, "%s: %m", "structs_init"); goto fail; } if (structs_xmlrpc2struct(xrep_type, ctx->xrep, "fault.value", ftype, &fault, NULL, NULL, 0) == 0) { if (faultp != NULL) *faultp = fault; else structs_free(ftype, NULL, &fault); ret = -2; /* -2 indicates fault */ goto fail; } structs_free(ftype, NULL, &fault); /* Extract response (if desired) */ if (rep != NULL) { if (rep_type != NULL) { /* return compact type */ if (structs_xmlrpc2struct(xrep_type, ctx->xrep, "params.0.value", rep_type, rep, NULL, ebuf, sizeof(ebuf)) == -1) { (*rlogger)(LOG_ERR, "error decoding XML-RPC" " response: %s", ebuf); goto fail; } } else { /* return exploded type */ if (structs_get(&structs_type_xmlrpc_value, "params.0.value", ctx->xrep, rep) == -1) { alogf(LOG_ERR, "structs_get: %m"); goto fail; } } } /* OK */ ret = 0; fail:; /* Done */ pthread_cleanup_pop(1); return (ret); } /* * Cleanup for http_xml_send_xmlrpc() */ static void http_xml_send_xmlrpc_cleanup(void *arg) { struct http_xml_send_xmlrpc_ctx *const ctx = arg; const struct structs_type *const xreq_type = &structs_type_xmlrpc_request; const struct structs_type *const xrep_type = &structs_type_xmlrpc_response; if (ctx->xreq != NULL) { structs_free(xreq_type, NULL, ctx->xreq); FREE(MEM_TYPE, ctx->xreq); } if (ctx->xrep != NULL) { structs_free(xrep_type, NULL, ctx->xrep); FREE(MEM_TYPE, ctx->xrep); } FREE(MEM_TYPE, ctx); } /* * Send a copy of the message, wait for a reply, and return the reply. * * The "reply" should already be initialized. * * This properly handles the calling thread's being canceled. */ int http_xml_send(struct http_client *client, struct in_addr ip, u_int16_t port, int https, const char *urlpath, const char *username, const char *password, const char *ptag, const char *pattrs, const struct structs_type *ptype, const void *payload, int pflags, const char *rtag, char **rattrsp, const char *rattrs_mtype, const struct structs_type *rtype, void *reply, int rflags, structs_xmllog_t *rlogger) { struct http_client_connection *cc; struct http_request *req; struct http_response *resp; int ret = -1; u_int code; FILE *fp; /* Get HTTP connection */ if ((cc = http_client_connect(client, ip, port, https)) == NULL) { alogf(LOG_ERR, "can't %s for %s:%u: %m", "get HTTP client", inet_ntoa(ip), port); return -1; } /* Push cleanup hook */ pthread_cleanup_push(http_xml_send_cleanup, cc); /* Set up request */ req = http_client_get_request(cc); if (http_request_set_method(req, payload != NULL ? HTTP_METHOD_POST : HTTP_METHOD_GET) == -1) { alogf(LOG_ERR, "can't %s for %s:%u: %m", "set method", inet_ntoa(ip), port); goto fail; } if (http_request_set_path(req, urlpath) == -1) { alogf(LOG_ERR, "can't %s for %s:%u: %m", "set path", inet_ntoa(ip), port); goto fail; } if (http_request_set_header(req, 0, "Content-Type", "text/xml") == -1) { alogf(LOG_ERR, "can't %s for %s:%u: %m", "set content-type", inet_ntoa(ip), port); goto fail; } if (username != NULL && password != NULL) { char *auth; if ((auth = http_request_encode_basic_auth(TYPED_MEM_TEMP, username, password)) == NULL) { alogf(LOG_ERR, "can't %s for %s:%u: %m", "encode authorization", inet_ntoa(ip), port); goto fail; } if (http_request_set_header(req, 0, "Authorization", "Basic %s", auth) == -1) { alogf(LOG_ERR, "can't %s for %s:%u: %m", "set authorization header", inet_ntoa(ip), port); FREE(TYPED_MEM_TEMP, auth); goto fail; } FREE(TYPED_MEM_TEMP, auth); } /* Write XML data to HTTP client output stream */ if (payload != NULL) { if ((fp = http_request_get_output(req, 1)) == NULL) { alogf(LOG_ERR, "can't %s for %s:%u: %m", "get output", inet_ntoa(ip), port); goto fail; } if (structs_xml_output(ptype, ptag, pattrs, payload, fp, NULL, pflags) == -1) { alogf(LOG_ERR, "can't %s for %s:%u: %m", "write XML", inet_ntoa(ip), port); goto fail; } } /* Get response */ if ((resp = http_client_get_response(cc)) == NULL) { alogf(LOG_ERR, "can't %s for %s:%u: %m", "get response", inet_ntoa(ip), port); goto fail; } if ((code = http_response_get_code(resp)) != HTTP_STATUS_OK) { alogf(LOG_ERR, "rec'd HTTP error code %d from" "http%s://%s:%u%s: %s", code, https ? "s" : "", inet_ntoa(ip), port, urlpath, http_response_status_msg(code)); goto fail; } /* Read XML reply from client input stream */ if ((fp = http_response_get_input(resp)) == NULL) { alogf(LOG_ERR, "can't %s for %s:%u: %m", "get input", inet_ntoa(ip), port); goto fail; } if (structs_xml_input(rtype, rtag, rattrsp, rattrs_mtype, fp, reply, rflags, rlogger) == -1) { alogf(LOG_ERR, "can't %s for %s:%u: %m", "read XML reply", inet_ntoa(ip), port); goto fail; } /* OK */ ret = 0; fail:; /* Done */ pthread_cleanup_pop(1); return (ret); } /* * Cleanup for http_xml_send() */ static void http_xml_send_cleanup(void *arg) { struct http_client_connection *cc = arg; http_client_close(&cc); }