/** Conversion table for method config values.
*
* HTTP verb strings for http_method_t enum values. Used by libcurl in the
- * status line of the outgoing HTTP header, by rest_write_header for decoding
+ * status line of the outgoing HTTP header, by rest_response_header for decoding
* incoming HTTP responses, and by the configuration parser.
*
* @see http_method_t
/** Conversion table for "Content-Type" header values.
*
- * Used by rest_write_header for parsing incoming headers.
+ * Used by rest_response_header for parsing incoming headers.
*
* Values we expect to see in the 'Content-Type:' header of the incoming
* response.
ctx = talloc_zero(randle, rlm_rest_curl_context_t);
ctx->headers = NULL; /* CURL needs this to be NULL */
- ctx->read.instance = inst;
+ ctx->request.instance = inst;
randle->ctx = ctx;
randle->handle = candle;
* @param[out] out Char buffer to write encoded data to.
* @param[in] size Multiply by nmemb to get the length of ptr.
* @param[in] nmemb Multiply by size to get the length of ptr.
- * @param[in] userdata rlm_rest_read_t to keep encoding state between calls.
+ * @param[in] userdata rlm_rest_request_t to keep encoding state between calls.
* @return length of data (including NULL) written to ptr, or 0 if no more
* data to write.
*/
static size_t rest_encode_post(void *out, size_t size, size_t nmemb, void *userdata)
{
- rlm_rest_read_t *ctx = userdata;
- REQUEST *request = ctx->request; /* Used by RDEBUG */
- VALUE_PAIR *vp;
+ rlm_rest_request_t *ctx = userdata;
+ REQUEST *request = ctx->request; /* Used by RDEBUG */
+ VALUE_PAIR *vp;
char *p = out; /* Position in buffer */
char *encoded = p; /* Position in buffer of last fully encoded attribute or value */
* @param[out] out Char buffer to write encoded data to.
* @param[in] size Multiply by nmemb to get the length of ptr.
* @param[in] nmemb Multiply by size to get the length of ptr.
- * @param[in] userdata rlm_rest_read_t to keep encoding state between calls.
+ * @param[in] userdata rlm_rest_request_t to keep encoding state between calls.
* @return length of data (including NULL) written to ptr, or 0 if no more
* data to write.
*/
static size_t rest_encode_json(void *out, size_t size, size_t nmemb, void *userdata)
{
- rlm_rest_read_t *ctx = userdata;
+ rlm_rest_request_t *ctx = userdata;
REQUEST *request = ctx->request; /* Used by RDEBUG */
VALUE_PAIR *vp, *next;
* be written.
* @param[in] func Stream function.
* @param[in] limit Maximum buffer size to alloc.
- * @param[in] userdata rlm_rest_read_t to keep encoding state between calls to
+ * @param[in] userdata rlm_rest_request_t to keep encoding state between calls to
* stream function.
* @return the length of the data written to the buffer (excluding NULL) or -1
* if alloc >= limit.
*/
-static ssize_t rest_read_wrapper(char **buffer, rest_read_t func, size_t limit, void *userdata)
+static ssize_t rest_request_encode_wrapper(char **buffer, rest_read_t func, size_t limit, void *userdata)
{
char *previous = NULL;
char *current;
return -1;
}
-/** (Re-)Initialises the data in a rlm_rest_read_t.
+/** (Re-)Initialises the data in a rlm_rest_request_t.
*
- * Resets the values of a rlm_rest_read_t to their defaults.
+ * Resets the values of a rlm_rest_request_t to their defaults.
*
* @param[in] request Current request.
* @param[in] ctx to initialise.
* @param[in] sort If true VALUE_PAIRs will be sorted within the VALUE_PAIR
* pointer array.
*/
-static void rest_read_ctx_init(REQUEST *request, rlm_rest_read_t *ctx, bool sort)
+static void rest_request_init(REQUEST *request, rlm_rest_request_t *ctx, bool sort)
{
/*
* Setup stream read data
* @param[in] in Char buffer where inbound header data is written.
* @param[in] size Multiply by nmemb to get the length of ptr.
* @param[in] nmemb Multiply by size to get the length of ptr.
- * @param[in] userdata rlm_rest_write_t to keep parsing state between calls.
+ * @param[in] userdata rlm_rest_response_t to keep parsing state between calls.
* @return Length of data processed, or 0 on error.
*/
-static size_t rest_write_header(void *in, size_t size, size_t nmemb, void *userdata)
+static size_t rest_response_header(void *in, size_t size, size_t nmemb, void *userdata)
{
- rlm_rest_write_t *ctx = userdata;
+ rlm_rest_response_t *ctx = userdata;
REQUEST *request = ctx->request; /* Used by RDEBUG */
char const *p = in, *q;
* @param[in] ptr Char buffer where inbound header data is written
* @param[in] size Multiply by nmemb to get the length of ptr.
* @param[in] nmemb Multiply by size to get the length of ptr.
- * @param[in] userdata rlm_rest_write_t to keep parsing state between calls.
+ * @param[in] userdata rlm_rest_response_t to keep parsing state between calls.
* @return length of data processed, or 0 on error.
*/
-static size_t rest_write_body(void *ptr, size_t size, size_t nmemb, void *userdata)
+static size_t rest_response_body(void *ptr, size_t size, size_t nmemb, void *userdata)
{
- rlm_rest_write_t *ctx = userdata;
+ rlm_rest_response_t *ctx = userdata;
REQUEST *request = ctx->request; /* Used by RDEBUG */
char const *p = ptr, *q;
return t;
}
-/** (Re-)Initialises the data in a rlm_rest_write_t.
+/** (Re-)Initialises the data in a rlm_rest_response_t.
*
- * This resets the values of the a rlm_rest_write_t to their defaults.
+ * This resets the values of the a rlm_rest_response_t to their defaults.
* Must be called between encoding sessions.
*
- * @see rest_write_body
- * @see rest_write_header
+ * @see rest_response_body
+ * @see rest_response_header
*
* @param[in] request Current request.
* @param[in] ctx data to initialise.
* @param[in] type Default http_body_type to use when decoding raw data, may be
- * overwritten by rest_write_header.
+ * overwritten by rest_response_header.
*/
-static void rest_write_ctx_init(REQUEST *request, rlm_rest_write_t *ctx, http_body_type_t type)
+static void rest_response_init(REQUEST *request, rlm_rest_response_t *ctx, http_body_type_t type)
{
ctx->request = request;
ctx->type = type;
*
* @param[in] ctx data to be freed.
*/
-static void rest_write_free(rlm_rest_write_t *ctx)
+static void rest_response_free(rlm_rest_response_t *ctx)
{
if (ctx->buffer != NULL) {
free(ctx->buffer);
* multiple parts.
*/
if (section->chunk > 0) {
- SET_OPTION(CURLOPT_READDATA, &ctx->read);
+ SET_OPTION(CURLOPT_READDATA, &ctx->request);
SET_OPTION(CURLOPT_READFUNCTION, func);
return 0;
* If were not doing chunked encoding then we read the entire
* body into a buffer, and send it in one go.
*/
- len = rest_read_wrapper(&ctx->body, func, REST_BODY_MAX_LEN, &ctx->read);
+ len = rest_request_encode_wrapper(&ctx->body, func, REST_BODY_MAX_LEN, &ctx->request);
if (len <= 0) {
REDEBUG("Failed creating HTTP body content");
return -1;
/*
* Tell CURL how to get HTTP body content, and how to process incoming data.
*/
- rest_write_ctx_init(request, &ctx->write, type);
+ rest_response_init(request, &ctx->response, type);
- SET_OPTION(CURLOPT_HEADERFUNCTION, rest_write_header);
- SET_OPTION(CURLOPT_HEADERDATA, &ctx->write);
- SET_OPTION(CURLOPT_WRITEFUNCTION, rest_write_body);
- SET_OPTION(CURLOPT_WRITEDATA, &ctx->write);
+ SET_OPTION(CURLOPT_HEADERFUNCTION, rest_response_header);
+ SET_OPTION(CURLOPT_HEADERDATA, &ctx->response);
+ SET_OPTION(CURLOPT_WRITEFUNCTION, rest_response_body);
+ SET_OPTION(CURLOPT_WRITEDATA, &ctx->response);
switch (method) {
case HTTP_METHOD_GET :
case HTTP_METHOD_PUT :
case HTTP_METHOD_CUSTOM :
if (section->chunk > 0) {
- ctx->read.chunk = section->chunk;
+ ctx->request.chunk = section->chunk;
ctx->headers = curl_slist_append(ctx->headers, "Expect:");
if (!ctx->headers) goto error_header;
#ifdef HAVE_JSON
case HTTP_BODY_JSON:
- rest_read_ctx_init(request, &ctx->read, 1);
+ rest_request_init(request, &ctx->request, 1);
if (rest_request_config_body(instance, section, request, handle,
rest_encode_json) < 0) {
#endif
case HTTP_BODY_POST:
- rest_read_ctx_init(request, &ctx->read, 0);
+ rest_request_init(request, &ctx->request, 0);
if (rest_request_config_body(instance, section, request, handle,
rest_encode_post) < 0) {
/** Sends the response to the correct decode function.
*
- * Uses the Content-Type information written in rest_write_header to
+ * Uses the Content-Type information written in rest_response_header to
* determine the correct decode function to use. The decode function will
* then convert the raw received data into VALUE_PAIRs.
*
* @param[in] handle to use.
* @return 0 on success or -1 on error.
*/
-int rest_request_decode(rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
- REQUEST *request, void *handle)
+int rest_response_decode(rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
+ REQUEST *request, void *handle)
{
rlm_rest_handle_t *randle = handle;
rlm_rest_curl_context_t *ctx = randle->ctx;
int ret = -1; /* -Wsometimes-uninitialized */
- if (!ctx->write.buffer) {
+ if (!ctx->response.buffer) {
RDEBUG2("Skipping attribute processing, no valid body data received");
return ret;
}
RDEBUG3("Processing body");
- switch (ctx->write.type) {
+ switch (ctx->response.type) {
case HTTP_BODY_NONE:
return 0;
case HTTP_BODY_POST:
- ret = rest_decode_post(instance, section, request, handle, ctx->write.buffer, ctx->write.used);
+ ret = rest_decode_post(instance, section, request, handle, ctx->response.buffer, ctx->response.used);
break;
#ifdef HAVE_JSON
case HTTP_BODY_JSON:
- ret = rest_decode_json(instance, section, request, handle, ctx->write.buffer, ctx->write.used);
+ ret = rest_decode_json(instance, section, request, handle, ctx->response.buffer, ctx->response.used);
break;
#endif
* Resets all options associated with a CURL handle, and frees any headers
* associated with it.
*
- * Calls rest_read_ctx_free and rest_write_free to free any memory used by
+ * Calls rest_read_ctx_free and rest_response_free to free any memory used by
* context data.
*
* @param[in] instance configuration data.
/*
* Free other context info
*/
- rest_write_free(&ctx->write);
+ rest_response_free(&ctx->response);
}
/** URL encodes a string.
*
* @param[out] out Where to write the pointer to the new buffer containing the escaped URI.
* @param[in] instance configuration data.
- * @param[in] section configuration data.
+ * @param[in] uri configuration data.
* @param[in] request Current request
* @return length of data written to buffer (excluding NULL) or < 0 if an error
* occurred.
*/
-ssize_t rest_uri_build(char **out, UNUSED rlm_rest_t *instance, rlm_rest_section_t *section, REQUEST *request)
+ssize_t rest_uri_build(char **out, UNUSED rlm_rest_t *instance, REQUEST *request, char const *uri)
{
char const *p;
char *path_exp = NULL;
ssize_t len, outlen;
- p = section->uri;
+ p = uri;
/*
* All URLs must contain at least <scheme>://<server>/
goto malformed;
}
- len = (p - section->uri);
+ len = (p - uri);
/*
* Allocate a temporary buffer to hold the first part of the URI
*/
scheme = talloc_array(request, char, len + 1);
- strlcpy(scheme, section->uri, len + 1);
+ strlcpy(scheme, uri, len + 1);
- path = (section->uri + len);
+ path = (uri + len);
len = radius_axlat(out, request, scheme, NULL, NULL);
talloc_free(scheme);
* Build xlat'd URI, this allows REST servers to be specified by
* request attributes.
*/
- uri_len = rest_uri_build(&uri, instance, section, request);
+ uri_len = rest_uri_build(&uri, instance, request, section->uri);
if (uri_len <= 0) return -1;
RDEBUG("Sending HTTP %s to \"%s\"", fr_int2str(http_method_table, section->method, NULL), uri);
rest_request_cleanup(instance, section, handle);
};
-static int parse_sub_section(CONF_SECTION *parent, rlm_rest_section_t *config, rlm_components_t comp)
-{
- CONF_SECTION *cs;
-
- char const *name = section_type_value[comp].section;
-
- cs = cf_section_sub_find(parent, name);
- if (!cs) {
- /* TODO: Should really setup section with default values */
- return 0;
- }
-
- if (cf_section_parse(cs, config, section_config) < 0) {
- return -1;
- }
-
- /*
- * Add section name (Maybe add to headers later?).
- */
- config->name = name;
-
- /*
- * Sanity check
- */
- if ((config->username && !config->password) || (!config->username && config->password)) {
- cf_log_err_cs(cs, "'username' and 'password' must both be set or both be absent");
-
- return -1;
- }
-
- /*
- * Convert HTTP method auth and body type strings into their integer equivalents.
- */
- config->auth = fr_str2int(http_auth_table, config->auth_str, HTTP_AUTH_UNKNOWN);
- if (config->auth == HTTP_AUTH_UNKNOWN) {
- cf_log_err_cs(cs, "Unknown HTTP auth type '%s'", config->auth_str);
- return -1;
- } else if ((config->auth != HTTP_AUTH_NONE) && !http_curl_auth[config->auth]) {
- cf_log_err_cs(cs, "Unsupported HTTP auth type \"%s\", check libcurl version, OpenSSL build "
- "configuration, then recompile this module", config->auth_str);
-
- return -1;
- }
-
- config->method = fr_str2int(http_method_table, config->method_str, HTTP_METHOD_CUSTOM);
- config->body = fr_str2int(http_body_type_table, config->body_str, HTTP_BODY_UNKNOWN);
-
- if (config->body == HTTP_BODY_UNKNOWN) {
- cf_log_err_cs(cs, "Unknown HTTP body type '%s'", config->body_str);
- return -1;
- }
-
- if (http_body_type_supported[config->body] == HTTP_BODY_UNSUPPORTED) {
- cf_log_err_cs(cs, "Unsupported HTTP body type \"%s\", please submit patches", config->body_str);
- return -1;
- }
-
- return 1;
-}
-
-/*
- * Do any per-module initialization that is separate to each
- * configured instance of the module. e.g. set up connections
- * to external databases, read configuration files, set up
- * dictionary entries, etc.
- *
- * If configuration information is given in the config section
- * that must be referenced in later calls, store a handle to it
- * in *instance otherwise put a null pointer there.
- */
-static int mod_instantiate(CONF_SECTION *conf, void *instance)
-{
- rlm_rest_t *inst = instance;
- char const *xlat_name;
-
- xlat_name = cf_section_name2(conf);
- if (!xlat_name) {
- xlat_name = cf_section_name1(conf);
- }
-
- inst->xlat_name = xlat_name;
-
- /*
- * Parse sub-section configs.
- */
- if (
- (parse_sub_section(conf, &inst->authorize, RLM_COMPONENT_AUTZ) < 0) ||
- (parse_sub_section(conf, &inst->authenticate, RLM_COMPONENT_AUTH) < 0) ||
- (parse_sub_section(conf, &inst->accounting, RLM_COMPONENT_ACCT) < 0) ||
- (parse_sub_section(conf, &inst->checksimul, RLM_COMPONENT_SESS) < 0) ||
- (parse_sub_section(conf, &inst->postauth, RLM_COMPONENT_POST_AUTH) < 0))
- {
- return -1;
- }
-
- /*
- * Initialise REST libraries.
- */
- if (rest_init(inst) < 0) {
- return -1;
- }
-
- inst->conn_pool = fr_connection_pool_init(conf, inst, mod_conn_create, mod_conn_alive, mod_conn_delete, NULL);
- if (!inst->conn_pool) {
- return -1;
- }
-
- return 0;
-}
-
/*
* Find the named user in this modules database. Create the set
* of attribute-value pairs to check and reply with for this user
/*
* Attempt to parse content if there was any.
*/
- ret = rest_request_decode(inst, section, request, handle);
+ ret = rest_response_decode(inst, section, request, handle);
if (ret < 0) {
rcode = RLM_MODULE_FAIL;
break;
* Attempt to parse content if there was any.
*/
if ((hcode >= 200) && (hcode < 300)) {
- ret = rest_request_decode(inst, section, request, handle);
+ ret = rest_response_decode(inst, section, request, handle);
if (ret < 0) rcode = RLM_MODULE_FAIL;
else if (ret == 0) rcode = RLM_MODULE_OK;
else rcode = RLM_MODULE_UPDATED;
/*
* Attempt to parse content if there was any.
*/
- ret = rest_request_decode(inst, section, request, handle);
+ ret = rest_response_decode(inst, section, request, handle);
if (ret < 0) {
rcode = RLM_MODULE_FAIL;
break;
* Attempt to parse content if there was any.
*/
if ((hcode >= 200) && (hcode < 300)) {
- ret = rest_request_decode(inst, section, request, handle);
+ ret = rest_response_decode(inst, section, request, handle);
if (ret < 0) rcode = RLM_MODULE_FAIL;
else if (ret == 0) rcode = RLM_MODULE_OK;
else rcode = RLM_MODULE_UPDATED;
} else if (hcode == 204) {
rcode = RLM_MODULE_OK;
} else if ((hcode >= 200) && (hcode < 300)) {
- ret = rest_request_decode(inst, section, request, handle);
+ ret = rest_response_decode(inst, section, request, handle);
if (ret < 0) rcode = RLM_MODULE_FAIL;
else if (ret == 0) rcode = RLM_MODULE_OK;
else rcode = RLM_MODULE_UPDATED;
return rcode;
}
+static int parse_sub_section(CONF_SECTION *parent, rlm_rest_section_t *config, rlm_components_t comp)
+{
+ CONF_SECTION *cs;
+
+ char const *name = section_type_value[comp].section;
+
+ cs = cf_section_sub_find(parent, name);
+ if (!cs) {
+ /* TODO: Should really setup section with default values */
+ return 0;
+ }
+
+ if (cf_section_parse(cs, config, section_config) < 0) {
+ return -1;
+ }
+
+ /*
+ * Add section name (Maybe add to headers later?).
+ */
+ config->name = name;
+
+ /*
+ * Sanity check
+ */
+ if ((config->username && !config->password) || (!config->username && config->password)) {
+ cf_log_err_cs(cs, "'username' and 'password' must both be set or both be absent");
+
+ return -1;
+ }
+
+ /*
+ * Convert HTTP method auth and body type strings into their integer equivalents.
+ */
+ config->auth = fr_str2int(http_auth_table, config->auth_str, HTTP_AUTH_UNKNOWN);
+ if (config->auth == HTTP_AUTH_UNKNOWN) {
+ cf_log_err_cs(cs, "Unknown HTTP auth type '%s'", config->auth_str);
+ return -1;
+ } else if ((config->auth != HTTP_AUTH_NONE) && !http_curl_auth[config->auth]) {
+ cf_log_err_cs(cs, "Unsupported HTTP auth type \"%s\", check libcurl version, OpenSSL build "
+ "configuration, then recompile this module", config->auth_str);
+
+ return -1;
+ }
+
+ config->method = fr_str2int(http_method_table, config->method_str, HTTP_METHOD_CUSTOM);
+ config->body = fr_str2int(http_body_type_table, config->body_str, HTTP_BODY_UNKNOWN);
+
+ if (config->body == HTTP_BODY_UNKNOWN) {
+ cf_log_err_cs(cs, "Unknown HTTP body type '%s'", config->body_str);
+ return -1;
+ }
+
+ if (http_body_type_supported[config->body] == HTTP_BODY_UNSUPPORTED) {
+ cf_log_err_cs(cs, "Unsupported HTTP body type \"%s\", please submit patches", config->body_str);
+ return -1;
+ }
+
+ return 1;
+}
+
+/*
+ * Do any per-module initialization that is separate to each
+ * configured instance of the module. e.g. set up connections
+ * to external databases, read configuration files, set up
+ * dictionary entries, etc.
+ *
+ * If configuration information is given in the config section
+ * that must be referenced in later calls, store a handle to it
+ * in *instance otherwise put a null pointer there.
+ */
+static int mod_instantiate(CONF_SECTION *conf, void *instance)
+{
+ rlm_rest_t *inst = instance;
+ char const *xlat_name;
+
+ xlat_name = cf_section_name2(conf);
+ if (!xlat_name) {
+ xlat_name = cf_section_name1(conf);
+ }
+
+ inst->xlat_name = xlat_name;
+
+ /*
+ * Parse sub-section configs.
+ */
+ if (
+ (parse_sub_section(conf, &inst->authorize, RLM_COMPONENT_AUTZ) < 0) ||
+ (parse_sub_section(conf, &inst->authenticate, RLM_COMPONENT_AUTH) < 0) ||
+ (parse_sub_section(conf, &inst->accounting, RLM_COMPONENT_ACCT) < 0) ||
+ (parse_sub_section(conf, &inst->checksimul, RLM_COMPONENT_SESS) < 0) ||
+ (parse_sub_section(conf, &inst->postauth, RLM_COMPONENT_POST_AUTH) < 0))
+ {
+ return -1;
+ }
+
+ /*
+ * Initialise REST libraries.
+ */
+ if (rest_init(inst) < 0) {
+ return -1;
+ }
+
+ inst->conn_pool = fr_connection_pool_init(conf, inst, mod_conn_create, mod_conn_alive, mod_conn_delete, NULL);
+ if (!inst->conn_pool) {
+ return -1;
+ }
+
+ return 0;
+}
+
/*
* Only free memory we allocated. The strings allocated via
* cf_section_parse() do not need to be freed.