summaryrefslogtreecommitdiff
path: root/school/node_modules/node-forge/mod_fsp/mod_fsp.c
diff options
context:
space:
mode:
authorMinteck <contact@minteck.org>2023-01-10 14:54:04 +0100
committerMinteck <contact@minteck.org>2023-01-10 14:54:04 +0100
commit99c1d9af689e5325f3cf535c4007b3aeb8325229 (patch)
treee663b3c2ebdbd67c818ac0c5147f0ce1d2463cda /school/node_modules/node-forge/mod_fsp/mod_fsp.c
parent9871b03912fc28ad38b4037ebf26a78aa937baba (diff)
downloadpluralconnect-99c1d9af689e5325f3cf535c4007b3aeb8325229.tar.gz
pluralconnect-99c1d9af689e5325f3cf535c4007b3aeb8325229.tar.bz2
pluralconnect-99c1d9af689e5325f3cf535c4007b3aeb8325229.zip
Update - This is an automated commit
Diffstat (limited to 'school/node_modules/node-forge/mod_fsp/mod_fsp.c')
-rw-r--r--school/node_modules/node-forge/mod_fsp/mod_fsp.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/school/node_modules/node-forge/mod_fsp/mod_fsp.c b/school/node_modules/node-forge/mod_fsp/mod_fsp.c
new file mode 100644
index 0000000..8beb824
--- /dev/null
+++ b/school/node_modules/node-forge/mod_fsp/mod_fsp.c
@@ -0,0 +1,415 @@
+/**
+ * Flash Socket Policy Apache Module.
+ *
+ * This module provides a flash socket policy file on the same port that
+ * serves HTTP on Apache. This can help simplify setting up a server that
+ * supports cross-domain communication with flash.
+ *
+ * Quick note about Apache memory handling: Data is allocated from pools and
+ * is not manually returned to those pools. The pools are typically considered
+ * short-lived and will be cleaned up automatically by Apache.
+ *
+ * @author Dave Longley
+ *
+ * Copyright (c) 2010 Digital Bazaar, Inc. All rights reserved.
+ */
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+
+#include "ap_compat.h"
+
+#include <string.h>
+
+// length of a policy file request
+#define PFR_LENGTH 23
+
+// declare main module
+module AP_MODULE_DECLARE_DATA fsp_module;
+
+// configuration for the module
+typedef struct fsp_config
+{
+ // the cross-domain policy to serve
+ char* policy;
+ apr_size_t policy_length;
+} fsp_config;
+
+// filter state for keeping track of detected policy file requests
+typedef struct filter_state
+{
+ fsp_config* cfg;
+ int checked;
+ int found;
+} filter_state;
+
+// for registering hooks, filters, etc.
+static void fsp_register_hooks(apr_pool_t *p);
+static int fsp_pre_connection(conn_rec *c, void *csd);
+
+// filter handler declarations
+static apr_status_t fsp_input_filter(
+ ap_filter_t* f, apr_bucket_brigade* bb,
+ ap_input_mode_t mode, apr_read_type_e block, apr_off_t nbytes);
+static int fsp_output_filter(ap_filter_t* f, apr_bucket_brigade* bb);
+
+/**
+ * Registers the hooks for this module.
+ *
+ * @param p the pool to allocate from, if necessary.
+ */
+static void fsp_register_hooks(apr_pool_t* p)
+{
+ // registers the pre-connection hook to handle adding filters
+ ap_hook_pre_connection(
+ fsp_pre_connection, NULL, NULL, APR_HOOK_MIDDLE);
+
+ // will parse a policy file request, to be added in pre_connection
+ ap_register_input_filter(
+ "fsp_request", fsp_input_filter,
+ NULL, AP_FTYPE_CONNECTION);
+
+ // will emit a cross-domain policy response to be added in pre_connection
+ ap_register_output_filter(
+ "fsp_response", fsp_output_filter,
+ NULL, AP_FTYPE_CONNECTION);
+}
+
+/**
+ * A hook that is called before a connection is handled. This function will
+ * get the module configuration and add the flash socket policy filters if
+ * a cross-domain policy has been specified in the configuration.
+ *
+ * @param c the connection.
+ * @param csd the connection socket descriptor.
+ *
+ * @return OK on success.
+ */
+static int fsp_pre_connection(conn_rec* c, void* csd)
+{
+ // only install filters if a policy was specified in the module config
+ fsp_config* cfg = ap_get_module_config(
+ c->base_server->module_config, &fsp_module);
+ if(cfg->policy != NULL)
+ {
+ // allocate filter state
+ filter_state* state = apr_palloc(c->pool, sizeof(filter_state));
+ if(state != NULL)
+ {
+ // initialize state
+ state->cfg = cfg;
+ state->checked = state->found = 0;
+
+ // add filters
+ ap_add_input_filter("fsp_request", state, NULL, c);
+ ap_add_output_filter("fsp_response", state, NULL, c);
+ }
+ }
+
+ return OK;
+}
+
+/**
+ * Searches the input request for a flash socket policy request. This request,
+ * unfortunately, does not follow the HTTP protocol and cannot be handled
+ * via a special HTTP handler. Instead, it is a short xml string followed by
+ * a null character:
+ *
+ * '<policy-file-request/>\0'
+ *
+ * A peek into the incoming data checks the first character of the stream to
+ * see if it is '<' (as opposed to typically something else for HTTP). If it
+ * is not, then this function returns and HTTP input is read normally. If it
+ * is, then the remaining bytes in the policy-file-request are read and
+ * checked. If a match is found, then the filter state will be updated to
+ * inform the output filter to send a cross-domain policy as a response. If
+ * no match is found, HTTP traffic will proceed as usual.
+ *
+ * @param f the input filter.
+ * @param state the filter state.
+ *
+ * @return APR_SUCCESS on success, some other status on failure.
+ */
+static apr_status_t find_policy_file_request(
+ ap_filter_t* f, filter_state* state)
+{
+ apr_status_t rval = APR_SUCCESS;
+
+ // create a temp buffer for speculative reads
+ apr_bucket_brigade* tmp = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
+
+ // FIXME: not sure how blocking mode works ... can it return fewer than
+ // the number of specified bytes?
+
+ // peek at the first PFR_LENGTH bytes
+ rval = ap_get_brigade(
+ f->next, tmp, AP_MODE_SPECULATIVE, APR_BLOCK_READ, PFR_LENGTH);
+ if(rval == APR_SUCCESS)
+ {
+ // quickly check the first bucket for the beginning of a pfr
+ const char* data;
+ apr_size_t length;
+ apr_bucket* b = APR_BRIGADE_FIRST(tmp);
+ rval = apr_bucket_read(b, &data, &length, APR_BLOCK_READ);
+ if(rval == APR_SUCCESS && length > 0 && data[0] == '<')
+ {
+ // possible policy file request, fill local buffer
+ char pfr[PFR_LENGTH];
+ char* ptr = pfr;
+ memcpy(ptr, data, length);
+ ptr += length;
+ memset(ptr, '\0', PFR_LENGTH - length);
+ b = APR_BUCKET_NEXT(b);
+ while(rval == APR_SUCCESS && b != APR_BRIGADE_SENTINEL(tmp))
+ {
+ rval = apr_bucket_read(b, &data, &length, APR_BLOCK_READ);
+ if(rval == APR_SUCCESS)
+ {
+ memcpy(ptr, data, length);
+ ptr += length;
+ b = APR_BUCKET_NEXT(b);
+ }
+ }
+
+ if(rval == APR_SUCCESS)
+ {
+ // see if pfr is a policy file request: '<policy-file-request/>\0'
+ if((ptr - pfr == PFR_LENGTH) && (pfr[PFR_LENGTH - 1] == '\0') &&
+ (strncmp(pfr, "<policy-file-request/>", PFR_LENGTH -1) == 0))
+ {
+ // pfr found
+ state->found = 1;
+ }
+ }
+ }
+ }
+
+ return rval;
+}
+
+/**
+ * Handles incoming data. If an attempt has not yet been made to look for
+ * a policy request (it is the beginning of the connection), then one is
+ * made. Otherwise this filter does nothing.
+ *
+ * If an attempt is made to find a policy request and one is not found, then
+ * reads proceed as normal. If one is found, then the filter state is modified
+ * to inform the output filter to send a policy request and the return value
+ * of this filter is EOF indicating that the connection should close after
+ * sending the cross-domain policy.
+ *
+ * @param f the input filter.
+ * @param bb the brigate to fill with input from the next filters in the chain
+ * and then process (look for a policy file request).
+ * @param mode the type of read requested (ie: AP_MODE_GETLINE means read until
+ * a CRLF is found, AP_MODE_GETBYTES means 'nbytes' of data, etc).
+ * @param block APR_BLOCK_READ or APR_NONBLOCK_READ, indicates the type of
+ * blocking to do when trying to read.
+ * @param nbytes used if the read mode is appropriate to specify the number of
+ * bytes to read (set to 0 for AP_MODE_GETLINE).
+ *
+ * @return the status of the input (ie: APR_SUCCESS for read success, APR_EOF
+ * for end of stream, APR_EAGAIN to read again when non-blocking).
+ */
+static apr_status_t fsp_input_filter(
+ ap_filter_t* f, apr_bucket_brigade* bb,
+ ap_input_mode_t mode, apr_read_type_e block, apr_off_t nbytes)
+{
+ apr_status_t rval = APR_SUCCESS;
+
+ filter_state* state = f->ctx;
+ if(state->checked == 1)
+ {
+ // already checked for policy file request, just read from other filters
+ rval = ap_get_brigade(f->next, bb, mode, block, nbytes);
+ }
+ else
+ {
+ // try to find a policy file request
+ rval = find_policy_file_request(f, state);
+ state->checked = 1;
+
+ if(rval == APR_SUCCESS)
+ {
+ if(state->found)
+ {
+ // do read of PFR_LENGTH bytes, consider end of stream
+ rval = ap_get_brigade(
+ f->next, bb, AP_MODE_READBYTES, APR_BLOCK_READ, PFR_LENGTH);
+ rval = APR_EOF;
+ }
+ else
+ {
+ // do normal read
+ rval = ap_get_brigade(f->next, bb, mode, block, nbytes);
+ }
+ }
+ }
+
+ return rval;
+}
+
+/**
+ * Handles outgoing data. If the filter state indicates that a cross-domain
+ * policy should be sent then it is added to the outgoing brigade of data. If
+ * a policy request was not detected, then this filter makes no changes to
+ * the outgoing data.
+ *
+ * @param f the output filter.
+ * @param bb the outgoing brigade of data.
+ *
+ * @return APR_SUCCESS on success, some other status on error.
+ */
+static int fsp_output_filter(ap_filter_t* f, apr_bucket_brigade* bb)
+{
+ apr_status_t rval = APR_SUCCESS;
+
+ filter_state* state = f->ctx;
+ if(state->found)
+ {
+ // found policy-file-request, add response bucket
+ // bucket is immortal because the data is stored in the configuration
+ // and doesn't need to be copied
+ apr_bucket* head = apr_bucket_immortal_create(
+ state->cfg->policy, state->cfg->policy_length, bb->bucket_alloc);
+ APR_BRIGADE_INSERT_HEAD(bb, head);
+ }
+
+ if(rval == APR_SUCCESS)
+ {
+ // pass brigade to next filter
+ rval = ap_pass_brigade(f->next, bb);
+ }
+
+ return rval;
+}
+
+/**
+ * Creates the configuration for this module.
+ *
+ * @param p the pool to allocate from.
+ * @param s the server the configuration is for.
+ *
+ * @return the configuration data.
+ */
+static void* fsp_create_config(apr_pool_t* p, server_rec* s)
+{
+ // allocate config
+ fsp_config* cfg = apr_palloc(p, sizeof(fsp_config));
+
+ // no default policy
+ cfg->policy = NULL;
+ cfg->policy_length = 0;
+ return cfg;
+}
+
+/**
+ * Sets the policy file to use from the configuration.
+ *
+ * @param parms the command directive parameters.
+ * @param userdata NULL, not used.
+ * @param arg the string argument to the command directive (the file with
+ * the cross-domain policy to serve as content).
+ *
+ * @return NULL on success, otherwise an error string to display.
+ */
+static const char* fsp_set_policy_file(
+ cmd_parms* parms, void* userdata, const char* arg)
+{
+ const char* rval = NULL;
+
+ apr_pool_t* pool = (apr_pool_t*)parms->pool;
+ fsp_config* cfg = ap_get_module_config(
+ parms->server->module_config, &fsp_module);
+
+ // ensure command is in the correct context
+ rval = ap_check_cmd_context(parms, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
+ if(rval == NULL)
+ {
+ // get canonical file name
+ char* fname = ap_server_root_relative(pool, arg);
+ if(fname == NULL)
+ {
+ rval = (const char*)apr_psprintf(
+ pool, "%s: Invalid policy file '%s'",
+ parms->cmd->name, arg);
+ }
+ else
+ {
+ // try to open the file
+ apr_status_t rv;
+ apr_file_t* fd;
+ apr_finfo_t finfo;
+ rv = apr_file_open(&fd, fname, APR_READ, APR_OS_DEFAULT, pool);
+ if(rv == APR_SUCCESS)
+ {
+ // stat file
+ rv = apr_file_info_get(&finfo, APR_FINFO_NORM, fd);
+ if(rv == APR_SUCCESS)
+ {
+ // ensure file is not empty
+ apr_size_t length = (apr_size_t)finfo.size;
+ if(length <= 0)
+ {
+ rval = (const char*)apr_psprintf(
+ pool, "%s: policy file '%s' is empty",
+ parms->cmd->name, fname);
+ }
+ // read file
+ else
+ {
+ char* buf = (char*)apr_palloc(pool, length + 1);
+ buf[length] = '\0';
+ rv = apr_file_read_full(fd, buf, length, NULL);
+ if(rv == APR_SUCCESS)
+ {
+ // TODO: validate file
+ // save policy string
+ cfg->policy = buf;
+ cfg->policy_length = length + 1;
+ }
+ }
+
+ // close the file
+ apr_file_close(fd);
+ }
+ }
+
+ // handle error case
+ if(rv != APR_SUCCESS)
+ {
+ char errmsg[120];
+ rval = (const char*)apr_psprintf(
+ pool, "%s: Invalid policy file '%s' (%s)",
+ parms->cmd->name, fname,
+ apr_strerror(rv, errmsg, sizeof(errmsg)));
+ }
+ }
+ }
+
+ return rval;
+}
+
+// table of configuration directives
+static const command_rec fsp_cmds[] =
+{
+ AP_INIT_TAKE1(
+ "FSPPolicyFile", /* the directive */
+ fsp_set_policy_file, /* function to call when directive is found */
+ NULL, /* user data to pass to function, not used */
+ RSRC_CONF, /* indicates the directive appears outside of <Location> */
+ "FSPPolicyFile (string) The cross-domain policy file to use"), /* docs */
+ {NULL}
+};
+
+// module setup
+module AP_MODULE_DECLARE_DATA fsp_module =
+{
+ STANDARD20_MODULE_STUFF, /* stuff declared in every 2.0 mod */
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ fsp_create_config, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ fsp_cmds, /* command apr_table_t */
+ fsp_register_hooks /* register hooks */
+};