The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
rlm_smtp.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: aa285b25f4d650c2d7eb78029a8da1b2039cb2cb $
19 * @file rlm_smtp.c
20 * @brief smtp server authentication.
21 *
22 * @copyright 2020 The FreeRADIUS server project
23 * @copyright 2020 Network RADIUS SAS (legal@networkradius.com)
24 */
25RCSID("$Id: aa285b25f4d650c2d7eb78029a8da1b2039cb2cb $")
26
27#include <freeradius-devel/curl/base.h>
28#include <freeradius-devel/server/base.h>
29#include <freeradius-devel/server/cf_priv.h>
30#include <freeradius-devel/server/module_rlm.h>
31#include <freeradius-devel/server/tmpl_dcursor.h>
32#include <freeradius-devel/util/token.h>
33
34#include <freeradius-devel/unlang/xlat_func.h>
35
37
40 { .out = &dict_freeradius, .proto = "freeradius"},
42};
43
46
49 { .out = &attr_smtp_header, .name = "SMTP-Mail-Header", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
50 { .out = &attr_smtp_body, .name = "SMTP-Mail-Body", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
52};
53
54extern global_lib_autoinst_t const * const rlm_smtp_lib[];
59
60/** Call environment for sending emails.
61*/
62typedef struct {
63 fr_value_box_t username; //!< User to authenticate as when sending emails.
64 tmpl_t *username_tmpl; //!< The tmpl used to produce the above.
65 fr_value_box_t password; //!< Password for authenticated mails.
66 fr_value_box_list_t *sender_address; //!< The address(es) used to generate the From: header
67 fr_value_box_list_t *recipient_addrs; //!< The address(es) used as recipients. Overrides elements in to, cc and bcc
68 fr_value_box_list_t *to_addrs; //!< The address(es) used for the To: header.
69 fr_value_box_list_t *cc_addrs; //!< The address(es) used for the Cc: header.
70 fr_value_box_list_t *bcc_addrs; //!< The address(es) used for the Bcc: header.
71 fr_value_box_list_t *attachments; //!< List of files to attach.
72 fr_value_box_list_t headers; //!< Entries to add to email header.
74
75/** Call environment for SMTP authentication
76 */
77typedef struct {
78 fr_value_box_t username; //!< Value to use for user name
79 tmpl_t *username_tmpl; //!< tmpl expanded to populate username
80 fr_value_box_t password; //!< Value to use for password
81 tmpl_t *password_tmpl; //!< tmpl expanded to populate password
83
84/** Call environment for sending simple emails using an xlat.
85*/
86typedef struct {
87 fr_value_box_t username; //!< User to authenticate as when sending emails.
88 tmpl_t *username_tmpl; //!< The tmpl used to produce the above.
89 fr_value_box_t password; //!< Password for authenticated mails.
90 fr_value_box_list_t *sender_address; //!< The address(es) used to generate the From: header
91 fr_value_box_list_t headers; //!< Entries to add to email header.
93
94static int smtp_header_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
95 CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule);
96
109
110typedef struct {
111 char const *uri; //!< URI of smtp server
112 char const *template_dir; //!< The directory that contains all email attachments
113 char const *envelope_address; //!< The address used to send the message
114
115 fr_time_delta_t timeout; //!< Timeout for connection and server response
116 fr_curl_tls_t tls; //!< Used for handled all tls specific curl components
117
119
120 fr_curl_conn_config_t conn_config; //!< Reusable CURL handle config
121} rlm_smtp_t;
122
123/*
124 * A mapping of configuration file names to internal variables.
125 */
126static const conf_parser_t module_config[] = {
127 { FR_CONF_OFFSET("uri", rlm_smtp_t, uri) },
128 { FR_CONF_OFFSET("template_directory", rlm_smtp_t, template_dir) },
129 { FR_CONF_OFFSET("envelope_address", rlm_smtp_t, envelope_address) },
130 { FR_CONF_OFFSET("timeout", rlm_smtp_t, timeout) },
131 { FR_CONF_OFFSET("set_date", rlm_smtp_t, set_date), .dflt = "yes" },
132 { FR_CONF_OFFSET_SUBSECTION("tls", 0, rlm_smtp_t, tls, fr_curl_tls_config) },//!<loading the tls values
133 { FR_CONF_OFFSET_SUBSECTION("connection", 0, rlm_smtp_t, conn_config, fr_curl_conn_config) },
135};
136
137/*
138 * Two types of SMTP connections are used:
139 * - persistent - where the connection can be left established as the same
140 * authentication is used for all mails sent.
141 * - onetime - where the connection is torn down after each use, since
142 * different authentication is needed each time.
143 *
144 * Memory for the handles for each is stored in slabs.
145 */
146
149
150typedef struct {
151 fr_curl_handle_t *mhandle; //!< Thread specific multi handle. Serves as the dispatch and
152 ///< coralling structure for smtp requests
153 smtp_slab_list_t *slab_persist; //!< Slab list for persistent connections.
154 smtp_slab_list_t *slab_onetime; //!< Slab list for onetime use connections.
156
157/*
158 * Holds the context for parsing the email elements
159 */
160typedef struct {
166
167 struct curl_slist *recipients;
168 struct curl_slist *header;
169 struct curl_slist *body_header;
170
172 char time_str[60];
173 curl_mime *mime;
175
176/*
177 * Adds every element associated with a dict_attr to a curl_slist
178 */
179static int da_to_slist(fr_mail_ctx_t *uctx, struct curl_slist **out, const fr_dict_attr_t *dict_attr)
180{
181 request_t *request = ((fr_mail_ctx_t *)uctx)->request;
182 fr_pair_t *vp;
183 int elems_added = 0;
184
185 /* Iterate over the VP and add the string value to the curl_slist */
186 vp = fr_pair_dcursor_by_da_init(&uctx->cursor, &uctx->request->request_pairs, dict_attr);
187
188 while (vp) {
189 *out = curl_slist_append(*out, vp->vp_strvalue);
190 vp = fr_dcursor_next(&uctx->cursor);
191 elems_added++;
192 }
193
194 if (!elems_added) RDEBUG3("There were no %s elements found", dict_attr->name);
195
196 return elems_added;
197}
198
199/** Transform an array of value box lists to entries in a CURL slist
200 *
201 */
202static int value_box_list_to_slist(struct curl_slist **out, fr_value_box_list_t const *lists)
203{
204 fr_value_box_list_t const *list = lists;
205 fr_value_box_t *vb = NULL;
206 int count = 0;
207 size_t i, list_count = talloc_array_length(lists);
208
209 for (i = 0; i < list_count; i++) {
210 while ((vb = fr_value_box_list_next(list, vb))) {
211 *out = curl_slist_append(*out, vb->vb_strvalue);
212 count ++;
213 }
214 list++;
215 }
216
217 return count;
218}
219
220/** Converts an array of value box lists to a curl_slist with a prefix
221 *
222 * @param uctx Mail context.
223 * @param out CURL slist to write to.
224 * @param lists Array of value box lists to copy.
225 * @param prefix to prepend to the output slist
226 * @return
227 * - 1 on success
228 * - 0 if no slist entry was created
229 * - -1 on failure
230 */
231static int value_box_list_to_header(fr_mail_ctx_t *uctx, struct curl_slist **out, fr_value_box_list_t const *lists,
232 const char *prefix)
233{
234 request_t *request = uctx->request;
235 fr_sbuff_t sbuff;
236 fr_sbuff_uctx_talloc_t sbuff_ctx;
237 bool added = false;
238 fr_value_box_t *vb = NULL;
239 fr_value_box_list_t const *list = lists;
240 size_t i, list_count = talloc_array_length(lists);
241
242 fr_sbuff_init_talloc(uctx, &sbuff, &sbuff_ctx, 256, SIZE_MAX);
243 (void) fr_sbuff_in_strcpy(&sbuff, prefix);
244
245 for (i = 0; i < list_count; i++) {
246 while ((vb = fr_value_box_list_next(list, vb))) {
247 /* If there are already values added, add the comma separator */
248 if (added) {
249 if (unlikely(fr_sbuff_in_strcpy(&sbuff, ", ") < 0)) {
250 error:
251 talloc_free(fr_sbuff_buff(&sbuff));
252 return -1;
253 }
254 }
255
256 if ((vb->type != FR_TYPE_STRING) && (fr_value_box_cast_in_place(vb, vb, FR_TYPE_STRING, NULL) < 0)) {
257 RERROR("Failed casting %pV to string", vb);
258 goto error;
259 }
260
261 if (unlikely(fr_sbuff_in_strcpy(&sbuff, vb->vb_strvalue) < 0)) goto error;
262 added = true;
263 }
264 list++;
265 }
266
267 if (!added) {
268 RDEBUG2("No values to add to \"%s\" header", prefix);
269 talloc_free(fr_sbuff_buff(&sbuff));
270 return 0;
271 }
272
273 /* Add the generated buffer the the curl_slist */
274 *out = curl_slist_append(*out, fr_sbuff_buff(&sbuff));
275 talloc_free(fr_sbuff_buff(&sbuff));
276 return 1;
277}
278
279/*
280 * Takes a string value and adds it as a file path to upload as an attachment
281 */
282static int str_to_attachments(fr_mail_ctx_t *uctx, curl_mime *mime, fr_value_box_t *vb,
283 fr_sbuff_t *path_buffer, fr_sbuff_marker_t *m)
284{
285 request_t *request = uctx->request;
286 curl_mimepart *part;
287
288 /* Move to the end of the template directory filepath */
289 fr_sbuff_set(path_buffer, m);
290
291 /* Check to see if the file attachment is valid, skip it if not */
292 RDEBUG2("Trying to set attachment: %pV", vb);
293
294 if (vb->type != FR_TYPE_STRING) {
295 return -1;
296 }
297
298 if (rad_filename_box_make_safe(vb, NULL) < 0) {
299 RPEDEBUG2("Failed escaping path");
300 return -1;
301 }
302
303 if (fr_sbuff_in_bstrncpy(path_buffer, vb->vb_strvalue, vb->vb_length) < 0) {
304 RPEDEBUG2("Path is too long");
305 return -1;
306 }
307
308 /* Add the file attachment as a mime encoded part */
309 part = curl_mime_addpart(mime);
310 curl_mime_encoder(part, "base64");
311 if (curl_mime_filedata(part, path_buffer->buff) != CURLE_OK) {
312 REDEBUG2("Cannot add file attachment");
313 return -1;
314 }
315
316 return 0;
317}
318
319/** Generate the `From:` header
320 *
321 * Defaults to using `sender_address` values, falls back to `envelope_address`
322 */
323static int generate_from_header(fr_mail_ctx_t *uctx, struct curl_slist **out, rlm_smtp_t const *inst,
324 rlm_smtp_env_t const *call_env)
325{
326 fr_sbuff_t sbuff;
327 fr_sbuff_uctx_talloc_t sbuff_ctx;
328
329 if (call_env->sender_address) return value_box_list_to_header(uctx, &uctx->header, call_env->sender_address, "From: ");
330
331 /* Initialize the buffer for the recipients. Used for FROM */
332 fr_sbuff_init_talloc(uctx, &sbuff, &sbuff_ctx, 256, SIZE_MAX);
333
334 /* Add the preposition for the header element */
335 (void) fr_sbuff_in_strcpy(&sbuff, "From: ");
336
337 /* Copy the envelope address as the From: source */
338 if (unlikely(fr_sbuff_in_bstrncpy(&sbuff, inst->envelope_address,
339 strlen(inst->envelope_address)) < 0)) {
340 talloc_free(fr_sbuff_buff(&sbuff));
341 return -1;
342 }
343
344 *out = curl_slist_append(*out, sbuff.buff);
345
346 /* Free the buffer used to generate the FROM header */
347 talloc_free(fr_sbuff_buff(&sbuff));
348
349 return 0;
350}
351
352/*
353 * Generates a curl_slist of recipients
354 */
355static int recipients_source(fr_mail_ctx_t *uctx, rlm_smtp_env_t const *call_env)
356{
357 request_t *request = uctx->request;
358 int recipients_set = 0;
359
360 /*
361 * Try to load the recipients into the envelope recipients if they are set
362 */
363 if(call_env->recipient_addrs) {
364 recipients_set += value_box_list_to_slist(&uctx->recipients, call_env->recipient_addrs);
365 }
366
367 /*
368 * If any recipients were found, ignore to cc and bcc, return the amount added.
369 **/
370 if (recipients_set) {
371 RDEBUG2("Recipients were generated from \"SMTP-Recipients\" and/or recipients in the config");
372 return recipients_set;
373 }
374 RDEBUG2("No addresses were found in \"SMTP-Recipients\"");
375
376 /*
377 * Try to load the to: addresses into the envelope recipients if they are set
378 */
379 if (call_env->to_addrs) recipients_set += value_box_list_to_slist(&uctx->recipients, call_env->to_addrs);
380
381 /*
382 * Try to load the cc: addresses into the envelope recipients if they are set
383 */
384 if (call_env->cc_addrs) recipients_set += value_box_list_to_slist(&uctx->recipients, call_env->cc_addrs);
385
386 /*
387 * Try to load the bcc: addresses into the envelope recipients if they are set
388 */
389 if (call_env->bcc_addrs) recipients_set += value_box_list_to_slist(&uctx->recipients, call_env->bcc_addrs);
390
391 RDEBUG2("%d recipients set", recipients_set);
392 return recipients_set;
393}
394
395/*
396 * Generates a curl_slist of header elements header elements
397 */
398static int header_source(fr_mail_ctx_t *uctx, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
399{
400 fr_sbuff_t time_out;
401 request_t *request = uctx->request;
402 fr_value_box_t *vb = NULL;
403
404 /*
405 * Load in all of the header elements supplied in the config
406 */
407 if (fr_value_box_list_initialised(&call_env->headers)) {
408 while ((vb = fr_value_box_list_next(&call_env->headers, vb))) {
409 RDEBUG2("Adding header \"%pV\"", vb);
410 uctx->header = curl_slist_append(uctx->header, vb->vb_strvalue);
411 }
412 }
413
414 /* Add the From: line */
415 if (unlikely(generate_from_header(uctx, &uctx->header, inst, call_env) < 0)) {
416 RDEBUG2("From: header could not be added");
417 return -1;
418 }
419
420 /* Add the TO: line if there is one provided in the request by SMTP-TO */
421 value_box_list_to_header(uctx, &uctx->header, call_env->to_addrs, "To: ");
422
423 /* Add the CC: line if there is one provided in the request by SMTP-CC */
424 value_box_list_to_header(uctx, &uctx->header, call_env->cc_addrs, "Cc: ");
425
426 /* Add all the generic header elements in the request */
427 da_to_slist(uctx, &uctx->header, attr_smtp_header);
428
429 /* If no header elements could be found, there is an error */
430 if (!uctx->header) {
431 RDEBUG2("Header elements could not be added");
432 return -1;
433 }
434
435 /* Set the DATE: to the time that the request was received */
436 if (inst->set_date) {
437 time_out = FR_SBUFF_OUT(uctx->time_str, sizeof(uctx->time_str));
438 fr_time_strftime_local(&time_out, fr_time(), "DATE: %a, %d %b %Y %T %z, (%Z) \r\n");
439 uctx->header = curl_slist_append(uctx->header, uctx->time_str);
440 }
441
442 RDEBUG2("Finished generating the curl_slist for the header elements");
443 return 0;
444}
445
446/*
447 * Add the Body elements to the email
448 */
449static size_t body_source(char *ptr, size_t size, size_t nmemb, void *mail_ctx)
450{
451 fr_mail_ctx_t *uctx = mail_ctx;
453 request_t *request = uctx->request;
454 fr_pair_t *vp;
455
456 fr_dbuff_init(&out, (uint8_t *)ptr, (size * nmemb)); /* Wrap the output buffer so we can track our position easily */
457
459 if (!vp) {
460 RWARN("vp could not be found for the body element");
461 return 0;
462 }
463
464 /*
465 * Copy the vp into the email. If it cannot all be
466 * loaded, return the amount of memory that was loaded
467 * and get called again.
468 */
469 if (fr_dbuff_in_memcpy_partial(&out, &uctx->vp_in, SIZE_MAX) < fr_dbuff_remaining(&uctx->vp_in)) {
470 RDEBUG3("%zu bytes used (partial copy)", fr_dbuff_used(&out));
471 return fr_dbuff_used(&out);
472 }
473
474 /*
475 * Once this value pair is fully copied, prepare for the next element
476 */
478 if (vp) {
479 fr_dbuff_init(&uctx->vp_in, (uint8_t const *)vp->vp_strvalue, vp->vp_length);
480
481 }
482
483 RDEBUG3("%zu bytes used (full copy)", fr_dbuff_used(&out));
484 return fr_dbuff_used(&out);
485}
486
487/*
488 * Initialize all the body elements to be uploaded later
489 */
490static int body_init(fr_mail_ctx_t *uctx, curl_mime *mime)
491{
492 fr_pair_t *vp;
493 request_t *request = uctx->request;
494
495 curl_mimepart *part;
496 curl_mime *mime_body;
497
498 int body_elements = 0;
499
500 /* Initialize a second mime to apply special conditions to the body elements */
501 MEM(mime_body = curl_mime_init(uctx->randle->candle));
502
503 /* initialize the cursor used by the body_source function*/
504 vp = fr_pair_dcursor_by_da_init(&uctx->body_cursor, &uctx->request->request_pairs, attr_smtp_body);
505 fr_dbuff_init(&uctx->vp_in, (uint8_t const *)vp->vp_strvalue, vp->vp_length);
506
507 /* Add a mime part to mime_body for every body element */
508 while (vp) {
509 body_elements++;
510 MEM(part = curl_mime_addpart(mime_body));
511
512 curl_mime_encoder(part, "8bit");
513 curl_mime_data_cb(part, vp->vp_length, body_source, NULL, NULL, uctx);
514
516 }
517 RDEBUG2("initialized %d body element part(s)", body_elements);
518
519 /* Re-initialize the cursor for use when uploading the data to curl */
520 fr_pair_dcursor_by_da_init(&uctx->body_cursor, &uctx->request->request_pairs, attr_smtp_body);
521
522 /*
523 * Add body_mime as a subpart of the mime request with a local content-disposition
524 */
525 MEM(part = curl_mime_addpart(mime));
526 curl_mime_subparts(part, mime_body);
527 MEM(curl_mime_type(part, "multipart/mixed") == CURLE_OK);
528 uctx->body_header = curl_slist_append(NULL, "Content-Disposition: inline"); /* Initialize the body_header curl_slist */
529 curl_mime_headers(part, uctx->body_header, 1);
530
531 return body_elements;
532}
533
534/*
535 * Adds every SMTP_Attachments file to the email as a MIME part
536 */
537static int attachments_source(fr_mail_ctx_t *uctx, curl_mime *mime, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
538{
539 int attachments_set = 0;
540 fr_sbuff_uctx_talloc_t sbuff_ctx;
541 fr_sbuff_t path_buffer;
543 fr_value_box_t *vb = NULL;
544 fr_value_box_list_t const *list = call_env->attachments;
545 size_t i, list_count = talloc_array_length(call_env->attachments);
546
547
548 /* Make sure that a template directory is provided */
549 if (!inst->template_dir) return 0;
550
551 /* Initialize the buffer to write the file path */
552 fr_sbuff_init_talloc(uctx, &path_buffer, &sbuff_ctx, talloc_array_length(inst->template_dir) + 128, SIZE_MAX);
553
554 /* Write the initial path to the buffer */
555 fr_sbuff_in_bstrcpy_buffer(&path_buffer, inst->template_dir);
556
557 /* Make sure the template_directory path ends in a "/" */
558 if (inst->template_dir[talloc_array_length(inst->template_dir) - 2] != '/'){
559 (void) fr_sbuff_in_char(&path_buffer, '/');
560 }
561
562 /* Mark the buffer so we only re-write after the template_dir component */
563 fr_sbuff_marker(&m, &path_buffer);
564
565 /* Add the attachments to the email */
566 for (i = 0; i < list_count; i++) {
567 while ((vb = fr_value_box_list_next(list, vb))) {
568 if (str_to_attachments(uctx, mime, vb, &path_buffer, &m) < 0) return -1;
569
570 attachments_set++;
571 }
572 list++;
573 }
574
575 /* Check for any file attachments */
576 talloc_free(path_buffer.buff);
577 return attachments_set;
578}
579
580static void smtp_io_module_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
581{
582 fr_curl_io_request_t *randle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
583 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
584 CURLMcode ret;
585
586 RDEBUG2("Forcefully cancelling pending SMTP request");
587
588 ret = curl_multi_remove_handle(t->mhandle->mandle, randle->candle); /* Gracefully terminate the request */
589 if (ret != CURLM_OK) {
590 RERROR("Failed removing curl handle from multi-handle: %s (%i)", curl_multi_strerror(ret), ret);
591 /* Not much we can do */
592 }
593 t->mhandle->transfers--;
594 smtp_slab_release(randle);
595}
596
597/** Callback to process response of SMTP server
598 *
599 * It checks if the response was CURLE_OK
600 * If it was, it tries to extract the certificate attributes
601 * If the response was not OK, we REJECT the request
602 * When responding to requests initiated by mod_authenticate this is simply
603 * a check on the username and password.
604 * When responding to requests initiated by mod_mail this indicates
605 * the mail has been queued.
606 */
608{
610 fr_curl_io_request_t *randle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
611 fr_curl_tls_t const *tls = &inst->tls;
612 long curl_out;
613 long curl_out_valid;
614
615 curl_out_valid = curl_easy_getinfo(randle->candle, CURLINFO_SSL_VERIFYRESULT, &curl_out);
616
617 if (curl_out_valid == CURLE_OK){
618 RDEBUG2("server certificate %s verified", curl_out ? "was" : "not");
619 } else {
620 RDEBUG2("server certificate result not found");
621 }
622
623 if (randle->result != CURLE_OK) {
624 CURLcode result = randle->result;
625 smtp_slab_release(randle);
626
627 switch (result) {
628 case CURLE_PEER_FAILED_VERIFICATION:
629 case CURLE_LOGIN_DENIED:
631
632 default:
634 }
635 }
636
637 if (tls->extract_cert_attrs) fr_curl_response_certinfo(request, randle);
638 smtp_slab_release(randle);
639
641}
642
643/*
644 * Checks that there is a User-Name and User-Password field in the request
645 * As well as all of the required SMTP elements
646 * Sets the: username, password
647 * SMTP server URI
648 * timeout information
649 * TLS information
650 * Sender and recipient information
651 * Email header and body
652 * File attachments
653 *
654 * Then it queues the request and yields until a response is given
655 * When it responds, smtp_io_module_resume is called.
656 */
657static unlang_action_t CC_HINT(nonnull) mod_mail(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
658{
660 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
661 rlm_smtp_env_t *call_env = talloc_get_type_abort(mctx->env_data, rlm_smtp_env_t);
662 fr_curl_io_request_t *randle = NULL;
663 fr_mail_ctx_t *mail_ctx;
665 fr_pair_t const *smtp_body;
666
667 /* Elements provided by the request */
668 smtp_body = fr_pair_find_by_da(&request->request_pairs, NULL, attr_smtp_body);
669
670 /* Make sure all of the essential email components are present and possible*/
671 if (!smtp_body) {
672 RERROR("Attribute \"smtp-body\" is required for smtp");
674 }
675
676 if (!call_env->sender_address && !inst->envelope_address) {
677 RERROR("At least one of \"sender_address\" or \"envelope_address\" in the config, or \"SMTP-Sender-Address\" in the request is needed");
678 error:
679 if (randle) smtp_slab_release(randle);
680 RETURN_UNLANG_RCODE(rcode);
681 }
682
683 /*
684 * If the username is defined and is not static data
685 * a onetime connection is used, otherwise a persistent one
686 * can be used.
687 */
688 randle = (call_env->username_tmpl &&
689 !tmpl_is_data(call_env->username_tmpl)) ? smtp_slab_reserve(t->slab_onetime) :
690 smtp_slab_reserve(t->slab_persist);
691 if (!randle) {
692 RDEBUG2("A handle could not be allocated for the request");
694 }
695
696 /* Initialize the uctx to perform the email */
697 mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t);
698 *mail_ctx = (fr_mail_ctx_t) {
699 .request = request,
700 .randle = randle,
701 .mime = curl_mime_init(randle->candle),
702 .time = fr_time(), /* time the request was received. Used to set DATE: */
703 .recipients = NULL,
704 .header = NULL
705 };
706
707 FR_CURL_REQUEST_SET_OPTION(CURLOPT_UPLOAD, 1L);
708
709 /* Set the username and password if they have been provided */
710 if (call_env->username.vb_strvalue) {
711 FR_CURL_REQUEST_SET_OPTION(CURLOPT_USERNAME, call_env->username.vb_strvalue);
712
713 if (!call_env->password.vb_strvalue) goto skip_auth;
714
715 FR_CURL_REQUEST_SET_OPTION(CURLOPT_PASSWORD, call_env->password.vb_strvalue);
716 RDEBUG3("Username and password set");
717 }
718
719skip_auth:
720 /* Set the envelope mail from address */
721 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MAIL_FROM,
722 (inst->envelope_address ? inst->envelope_address :
723 fr_value_box_list_head(call_env->sender_address)->vb_strvalue));
724
725 /* Set the recipients */
726 if (recipients_source(mail_ctx, call_env) <= 0) {
727 REDEBUG("At least one recipient is required to send an email");
728 goto error;
729 }
730 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MAIL_RCPT, mail_ctx->recipients);
731
732 /* Set the header elements */
733 if (header_source(mail_ctx, inst, call_env) != 0) {
734 REDEBUG("The header slist could not be generated");
735 rcode = RLM_MODULE_FAIL;
736 goto error;
737 }
738
739 /*
740 * CURLOPT_HTTPHEADER is the option that they use for the header in the curl example
741 *
742 * https://curl.haxx.se/libcurl/c/smtp-mime.html
743 */
744 FR_CURL_REQUEST_SET_OPTION(CURLOPT_HTTPHEADER, mail_ctx->header);
745
746 /* Initialize the body elements to be uploaded */
747 if (body_init(mail_ctx, mail_ctx->mime) == 0) {
748 REDEBUG("The body could not be generated");
749 rcode = RLM_MODULE_FAIL;
750 goto error;
751 }
752
753 /* Initialize the attachments if there are any*/
754 if (attachments_source(mail_ctx, mail_ctx->mime, inst, call_env) < 0) {
755 rcode = RLM_MODULE_FAIL;
756 goto error;
757 }
758
759 /* Add the mime encoded elements to the curl request */
760 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MIMEPOST, mail_ctx->mime);
761
762 if (fr_curl_io_request_enqueue(t->mhandle, request, randle)) {
763 rcode = RLM_MODULE_FAIL;
764 goto error;
765 }
766
768}
769
770/*
771 * Sets the: username, password
772 * SMTP server URI
773 * timeout information
774 * and TLS information
775 *
776 * Then it queues the request and yields until a response is given
777 * When it responds, smtp_io_module_resume is called.
778 */
779static unlang_action_t CC_HINT(nonnull(1,2)) mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
780{
781 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
782 rlm_smtp_auth_env_t *env_data = talloc_get_type_abort(mctx->env_data, rlm_smtp_auth_env_t);
783 fr_curl_io_request_t *randle;
784
785 if (!env_data->username_tmpl) {
786 RDEBUG("No 'username' was set for authentication - failing the request");
788 }
789
790 if (!env_data->password_tmpl) {
791 RDEBUG("No 'username' was set for authentication - failing the request");
793 }
794
795 if (env_data->username.type != FR_TYPE_STRING || (env_data->username.vb_length == 0)) {
796 RWARN("\"%s\" is required for authentication", env_data->username_tmpl->name);
798 }
799
800 if (env_data->password.type != FR_TYPE_STRING || (env_data->password.vb_length == 0)) {
801 RWARN("\"%s\" is required for authentication", env_data->password_tmpl->name);
803 }
804
805 randle = smtp_slab_reserve(t->slab_onetime);
806 if (!randle) {
808 }
809
810 FR_CURL_REQUEST_SET_OPTION(CURLOPT_USERNAME, env_data->username.vb_strvalue);
811 FR_CURL_REQUEST_SET_OPTION(CURLOPT_PASSWORD, env_data->password.vb_strvalue);
812
813 if (fr_curl_io_request_enqueue(t->mhandle, request, randle) < 0) {
814 error:
815 smtp_slab_release(randle);
817 }
818
820}
821
823 { .required = true, .concat = true, .type = FR_TYPE_STRING }, /* To: */
824 { .required = true, .concat = true, .type = FR_TYPE_STRING }, /* Subject: */
825 { .concat = true, .type = FR_TYPE_STRING }, /* Body */
827};
828
830 xlat_ctx_t const *xctx,
831 request_t *request, UNUSED fr_value_box_list_t *in)
832{
833 fr_curl_io_request_t *randle = talloc_get_type_abort(xctx->rctx, fr_curl_io_request_t);
834 fr_value_box_t *vb;
835
836 if (randle->result != CURLE_OK) {
837 RPERROR("Sending mail failed: %s %i", curl_easy_strerror(randle->result), randle->result);
838 smtp_slab_release(randle);
839 return XLAT_ACTION_FAIL;
840 }
841
842 smtp_slab_release(randle);
843
844 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
845 vb->vb_bool = true;
847
848 return XLAT_ACTION_DONE;
849}
850
851static void smtp_xlat_signal(xlat_ctx_t const *xctx, request_t *request, fr_signal_t action)
852{
853 fr_curl_io_request_t *randle = talloc_get_type_abort(xctx->rctx, fr_curl_io_request_t);
854 rlm_smtp_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, rlm_smtp_thread_t);
855
856 smtp_io_module_signal(MODULE_CTX(xctx->mctx->mi, t, NULL, randle), request, action);
857}
858
859/** Xlat to send simple emails
860 *
861 * Example:
862 @verbatim
863 %smtp.send('bob@example.com', 'Something happened', 'More details about what happened')
864 @endverbatim
865 *
866 * @ingroup xlat_functions
867 */
869 xlat_ctx_t const *xctx, request_t *request,
870 fr_value_box_list_t *in)
871{
873 rlm_smtp_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, rlm_smtp_thread_t);
874 fr_value_box_t *to, *subject, *body;
875 rlm_smtp_xlat_env_t *call_env = talloc_get_type_abort(xctx->env_data, rlm_smtp_xlat_env_t);
876 fr_curl_io_request_t *randle = NULL;
877 fr_mail_ctx_t *mail_ctx;
878 fr_value_box_t *vb = NULL;
879 char const *sender;
880 char *header_string;
881 curl_mimepart *part;
882
883 XLAT_ARGS(in, &to, &subject, &body);
884
885 randle = (call_env->username_tmpl &&
886 !tmpl_is_data(call_env->username_tmpl)) ? smtp_slab_reserve(t->slab_onetime) :
887 smtp_slab_reserve(t->slab_persist);
888 if (!randle) {
889 RERROR("A handle could not be allocated for the request");
890 return XLAT_ACTION_FAIL;
891 }
892
893 mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t);
894 *mail_ctx = (fr_mail_ctx_t) {
895 .request = request,
896 .randle = randle,
897 .mime = curl_mime_init(randle->candle),
898 .time = fr_time(),
899 };
900
901 FR_CURL_REQUEST_SET_OPTION(CURLOPT_UPLOAD, 1L);
902
903 /* Set the username and password if they have been provided */
904 if (call_env->username.vb_strvalue) {
905 FR_CURL_REQUEST_SET_OPTION(CURLOPT_USERNAME, call_env->username.vb_strvalue);
906
907 if (!call_env->password.vb_strvalue) goto skip_auth;
908
909 FR_CURL_REQUEST_SET_OPTION(CURLOPT_PASSWORD, call_env->password.vb_strvalue);
910 RDEBUG2("Username and password set");
911 }
912
913skip_auth:
914 sender = inst->envelope_address ? inst->envelope_address : fr_value_box_list_head(call_env->sender_address)->vb_strvalue;
915 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MAIL_FROM, sender);
916 header_string = talloc_asprintf(call_env, "From: %s", sender);
917 mail_ctx->header = curl_slist_append(mail_ctx->header, header_string);
918
919 mail_ctx->recipients = curl_slist_append(mail_ctx->recipients, to->vb_strvalue);
920 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MAIL_RCPT, mail_ctx->recipients);
921 header_string = talloc_asprintf(call_env, "To: %s", to->vb_strvalue);
922 mail_ctx->header = curl_slist_append(mail_ctx->header, header_string);
923
924 header_string = talloc_asprintf(call_env, "Subject: %s", subject->vb_strvalue);
925 mail_ctx->header = curl_slist_append(mail_ctx->header, header_string);
926
927 if (fr_value_box_list_initialised(&call_env->headers)) {
928 while ((vb = fr_value_box_list_next(&call_env->headers, vb))) {
929 RDEBUG2("Adding header \"%pV\"", vb);
930 mail_ctx->header = curl_slist_append(mail_ctx->header, vb->vb_strvalue);
931 }
932 }
933 FR_CURL_REQUEST_SET_OPTION(CURLOPT_HTTPHEADER, mail_ctx->header);
934
935 MEM(part = curl_mime_addpart(mail_ctx->mime));
936 if (curl_mime_encoder(part, "8bit") != CURLE_OK) {
937 RERROR("Failed setting mime encoder");
938 error:
939 if (randle) smtp_slab_release(randle);
940 return XLAT_ACTION_FAIL;
941 }
942
943 if (body && (body->type == FR_TYPE_STRING)) {
944 if (curl_mime_data(part, body->vb_strvalue, body->vb_length) != CURLE_OK) {
945 RERROR("Failed adding email body");
946 goto error;
947 }
948 } else {
949 curl_mime_data(part, "", 0);
950 }
951 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MIMEPOST, mail_ctx->mime);
952
953 if (fr_curl_io_request_enqueue(t->mhandle, request, randle)) goto error;
954
956}
957
958static int mod_bootstrap(module_inst_ctx_t const *mctx)
959{
960 xlat_t *xlat;
961
962 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "send", smtp_send_xlat, FR_TYPE_BOOL);
965
966 return 0;
967}
968
969static int mod_instantiate(module_inst_ctx_t const *mctx)
970{
971 rlm_smtp_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_smtp_t);
972
973 inst->conn_config.reuse.num_children = 1;
974 inst->conn_config.reuse.child_pool_size = sizeof(fr_mail_ctx_t);
975
976 return 0;
977}
978
979#define SMTP_COMMON_CLEANUP \
980 fr_mail_ctx_t *mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t); \
981 if (mail_ctx->mime) curl_mime_free(mail_ctx->mime); \
982 if (mail_ctx->header) curl_slist_free_all(mail_ctx->header); \
983 if (mail_ctx->recipients) curl_slist_free_all(mail_ctx->recipients)
984
986{
988
989 if (randle->candle) curl_easy_cleanup(randle->candle);
990
991 return 0;
992}
993
995{
997
998 if (randle->candle) curl_easy_reset(randle->candle);
999
1000 return 0;
1001}
1002
1004{
1005 fr_mail_ctx_t *mail_ctx = NULL;
1006
1007 MEM(mail_ctx = talloc_zero(randle, fr_mail_ctx_t));
1008 randle->uctx = mail_ctx;
1009
1010 smtp_slab_element_set_destructor(randle, smtp_onetime_request_cleanup, NULL);
1011
1012 return 0;
1013}
1014
1016{
1017 if (mail_ctx->randle && mail_ctx->randle->candle) curl_easy_cleanup(mail_ctx->randle->candle);
1018
1019 return 0;
1020}
1021
1023{
1024 fr_mail_ctx_t *mail_ctx = NULL;
1025
1026 MEM(mail_ctx = talloc_zero(randle, fr_mail_ctx_t));
1027 mail_ctx->randle = randle;
1028 randle->uctx = mail_ctx;
1029 randle->candle = curl_easy_init();
1030 if (unlikely(!randle->candle)) {
1031 fr_strerror_printf("Unable to initialise CURL handle");
1032 return -1;
1033 }
1034 talloc_set_destructor(mail_ctx, smtp_mail_ctx_free);
1035
1036 smtp_slab_element_set_destructor(randle, smtp_persist_request_cleanup, NULL);
1037
1038 return 0;
1039}
1040
1042{
1043#if CURL_AT_LEAST_VERSION(7,45,0)
1044 FR_CURL_SET_OPTION(CURLOPT_DEFAULT_PROTOCOL, "smtp");
1045#endif
1046 FR_CURL_SET_OPTION(CURLOPT_URL, inst->uri);
1047#if CURL_AT_LEAST_VERSION(7,85,0)
1048 FR_CURL_SET_OPTION(CURLOPT_PROTOCOLS_STR, "smtp,smtps");
1049#else
1050 FR_CURL_SET_OPTION(CURLOPT_PROTOCOLS, CURLPROTO_SMTP | CURLPROTO_SMTPS);
1051#endif
1052 FR_CURL_SET_OPTION(CURLOPT_CONNECTTIMEOUT_MS, fr_time_delta_to_msec(inst->timeout));
1053 FR_CURL_SET_OPTION(CURLOPT_TIMEOUT_MS, fr_time_delta_to_msec(inst->timeout));
1054
1055 if (DEBUG_ENABLED3) FR_CURL_SET_OPTION(CURLOPT_VERBOSE, 1L);
1056
1057 if (fr_curl_easy_tls_init(randle, &inst->tls) != 0) goto error;
1058
1059 return 0;
1060error:
1061 return -1;
1062}
1063
1064static int smtp_onetime_conn_init(fr_curl_io_request_t *randle, void *uctx)
1065{
1066 rlm_smtp_t const *inst = talloc_get_type_abort(uctx, rlm_smtp_t);
1067 fr_mail_ctx_t *mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t);
1068
1069 randle->candle = curl_easy_init();
1070 if (unlikely(!randle->candle)) {
1071 fr_strerror_printf("Unable to initialise CURL handle");
1072 return -1;
1073 }
1074
1075 memset(mail_ctx, 0, sizeof(fr_mail_ctx_t));
1076
1077 return smtp_conn_common_init(randle, inst);
1078}
1079
1080
1081static int smtp_persist_conn_init(fr_curl_io_request_t *randle, void *uctx)
1082{
1083 rlm_smtp_t const *inst = talloc_get_type_abort(uctx, rlm_smtp_t);
1084 fr_mail_ctx_t *mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t);
1085
1086 memset(mail_ctx, 0, sizeof(fr_mail_ctx_t));
1087
1088 return smtp_conn_common_init(randle, inst);
1089}
1090
1091/*
1092 * Initialize a new thread with a curl instance
1093 */
1095{
1096 rlm_smtp_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_smtp_t);
1097 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
1098 fr_curl_handle_t *mhandle;
1099
1100 if (!(t->slab_onetime = smtp_slab_list_alloc(t, mctx->el, &inst->conn_config.reuse,
1102 inst, false, false))) {
1103 ERROR("Connection handle pool instantiation failed");
1104 return -1;
1105 }
1106 if (!(t->slab_persist = smtp_slab_list_alloc(t, mctx->el, &inst->conn_config.reuse,
1108 inst, false, true))) {
1109 ERROR("Connection handle pool instantiation failed");
1110 return -1;
1111 }
1112
1113 mhandle = fr_curl_io_init(t, mctx->el, false);
1114 if (!mhandle) return -1;
1115
1116 t->mhandle = mhandle;
1117 return 0;
1118}
1119
1120/*
1121 * Close the thread and free the memory
1122 */
1124{
1125 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
1126
1127 talloc_free(t->mhandle);
1130 return 0;
1131}
1132
1133/** Parse the header section into tmpls for producing email headers
1134 *
1135 */
1136static int smtp_header_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
1137 CONF_ITEM *ci,
1138 UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
1139{
1140 CONF_SECTION const *cs = cf_item_to_section(ci);
1141 CONF_ITEM const *item = NULL;
1142 CONF_PAIR const *cp;
1143 call_env_parsed_t *parsed_env;
1144 tmpl_t *parsed_tmpl;
1145 ssize_t slen;
1146
1147 while ((item = cf_item_next(cs, item))) {
1148 char *to_parse;
1149
1150 if (!cf_item_is_pair(item)) {
1151 cf_log_err(item, "Entry is not in \"header = value\" format");
1152 return -1;
1153 }
1154 cp = cf_item_to_pair(item);
1155
1156 MEM(parsed_env = call_env_parsed_add(ctx, out,
1158
1159 /*
1160 * Turn the conf pair `attr = value` to email header format
1161 */
1162 to_parse = talloc_asprintf(NULL, "%s: %s", cf_pair_attr(cp), cf_pair_value(cp));
1163
1164 slen = tmpl_afrom_substr(parsed_env, &parsed_tmpl,
1165 &FR_SBUFF_IN(to_parse, talloc_strlen(to_parse)),
1167 t_rules);
1168 talloc_free(to_parse);
1169
1170 if (slen <= 0) {
1171 cf_canonicalize_error(cp, slen, "Failed parsing header", cf_pair_value(cp));
1172 error:
1173 call_env_parsed_free(out, parsed_env);
1174 return -1;
1175 }
1176
1177 if (tmpl_needs_resolving(parsed_tmpl) &&
1178 (tmpl_resolve(parsed_tmpl, &(tmpl_res_rules_t){ .dict_def = t_rules->attr.dict_def }) < 0)) {
1179 cf_log_perr(cp, "Failed resolving header");
1180 goto error;
1181 }
1182
1183 call_env_parsed_set_tmpl(parsed_env, parsed_tmpl);
1184 }
1185
1186 return 0;
1187}
1188
1191 .env = (call_env_parser_t[]) {
1193 .pair.dflt_quote = T_DOUBLE_QUOTED_STRING },
1197 .pair.dflt = "SMTP-Recipients[*]", .pair.dflt_quote = T_BARE_WORD },
1199 .pair.dflt = "SMTP-TO[*]", .pair.dflt_quote = T_BARE_WORD },
1201 .pair.dflt = "SMTP-CC[*]", .pair.dflt_quote = T_BARE_WORD },
1203 .pair.dflt = "SMTP-BCC[*]", .pair.dflt_quote = T_BARE_WORD },
1205 .pair.dflt = "SMTP-Attachments[*]", .pair.dflt_quote = T_BARE_WORD },
1207
1209 }
1210};
1211
1214 .env = (call_env_parser_t[]) {
1216 ((call_env_parser_t[]) {
1219 rlm_smtp_auth_env_t, username, username_tmpl), .pair.dflt = "User-Name", .pair.dflt_quote = T_BARE_WORD },
1222 rlm_smtp_auth_env_t, password, password_tmpl), .pair.dflt = "User-Password", .pair.dflt_quote = T_BARE_WORD },
1224 }))},
1226 }
1227};
1228
1229/*
1230 * The module name should be the only globally exported symbol.
1231 * That is, everything else should be 'static'.
1232 *
1233 * If the module needs to temporarily modify it's instantiation
1234 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
1235 * The server will then take care of ensuring that the module
1236 * is single-threaded.
1237 */
1238extern module_rlm_t rlm_smtp;
1240 .common = {
1241 .magic = MODULE_MAGIC_INIT,
1242 .name = "smtp",
1243 .inst_size = sizeof(rlm_smtp_t),
1244 .thread_inst_size = sizeof(rlm_smtp_thread_t),
1245 .config = module_config,
1246 .bootstrap = mod_bootstrap,
1247 .instantiate = mod_instantiate,
1248 .thread_instantiate = mod_thread_instantiate,
1249 .thread_detach = mod_thread_detach,
1250 },
1251 .method_group = {
1252 .bindings = (module_method_binding_t[]){
1253 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate, .method_env = &auth_env },
1254 { .section = SECTION_NAME("mail", CF_IDENT_ANY), .method = mod_mail, .method_env = &method_env },
1255
1257 }
1258 }
1259};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
#define RCSID(id)
Definition build.h:512
#define unlikely(_x)
Definition build.h:407
#define UNUSED
Definition build.h:336
void call_env_parsed_free(call_env_parsed_head_t *parsed, call_env_parsed_t *ptr)
Remove a call_env_parsed_t from the list of parsed call envs.
Definition call_env.c:776
call_env_parsed_t * call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule)
Allocate a new call_env_parsed_t structure and add it to the list of parsed call envs.
Definition call_env.c:689
void call_env_parsed_set_tmpl(call_env_parsed_t *parsed, tmpl_t const *tmpl)
Assign a tmpl to a call_env_parsed_t.
Definition call_env.c:718
#define CALL_ENV_TERMINATOR
Definition call_env.h:236
#define FR_CALL_ENV_PARSE_OFFSET(_name, _cast_type, _flags, _struct, _field, _parse_field)
Specify a call_env_parser_t which writes out runtime results and the result of the parsing phase to t...
Definition call_env.h:365
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
Definition call_env.h:240
call_env_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:247
#define FR_CALL_ENV_SUBSECTION(_name, _name2, _flags, _subcs)
Specify a call_env_parser_t which defines a nested subsection.
Definition call_env.h:402
@ CALL_ENV_FLAG_CONCAT
If the tmpl produced multiple boxes they should be concatenated.
Definition call_env.h:76
@ CALL_ENV_FLAG_SUBSECTION
This is a subsection.
Definition call_env.h:87
@ CALL_ENV_FLAG_SECRET
The value is a secret, and should not be logged.
Definition call_env.h:91
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition call_env.h:75
@ CALL_ENV_FLAG_PARSE_MISSING
If this subsection is missing, still parse it.
Definition call_env.h:88
@ CALL_ENV_FLAG_BARE_WORD_ATTRIBUTE
bare words are treated as an attribute, but strings may be xlats.
Definition call_env.h:92
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition call_env.h:80
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _name2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
Definition call_env.h:412
#define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field)
Specify a call_env_parser_t which writes out runtime results to the specified field.
Definition call_env.h:340
Per method call config.
Definition call_env.h:180
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:669
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition cf_parse.h:280
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
Definition cf_parse.h:309
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:606
Common header for all CONF_* types.
Definition cf_priv.h:54
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:77
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:106
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
Definition cf_util.c:640
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:692
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition cf_util.c:1790
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:672
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1746
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1730
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:345
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition cf_util.h:423
#define cf_item_next(_parent, _curr)
Definition cf_util.h:94
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:352
#define CF_IDENT_ANY
Definition cf_util.h:80
fr_curl_handle_t * fr_curl_io_init(TALLOC_CTX *ctx, fr_event_list_t *el, bool multiplex)
#define FR_CURL_REQUEST_SET_OPTION(_x, _y)
Definition base.h:67
CURLcode result
Result of executing the request.
Definition base.h:103
#define FR_CURL_SET_OPTION(_x, _y)
Definition base.h:45
uint64_t transfers
How many transfers are current in progress.
Definition base.h:94
bool extract_cert_attrs
Definition base.h:119
CURLM * mandle
The multi handle.
Definition base.h:95
void * uctx
Private data for the module using the API.
Definition base.h:105
int fr_curl_io_request_enqueue(fr_curl_handle_t *mhandle, request_t *request, fr_curl_io_request_t *creq)
Sends a request using libcurl.
Definition io.c:478
CURL * candle
Request specific handle.
Definition base.h:102
Uctx data for timer and I/O functions.
Definition base.h:91
Structure representing an individual request being passed to curl for processing.
Definition base.h:101
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
Definition dbuff.h:775
#define fr_dbuff_init(_out, _start, _len_or_end)
Initialise an dbuff for encoding or decoding.
Definition dbuff.h:362
#define fr_dbuff_remaining(_dbuff_or_marker)
Return the number of bytes remaining between the dbuff or marker and the end of the buffer.
Definition dbuff.h:751
#define fr_dbuff_in_memcpy_partial(_out, _in, _inlen)
Copy at most _inlen bytes into the dbuff.
Definition dbuff.h:1447
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:406
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:337
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:40
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
static fr_slen_t in
Definition dict.h:882
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define GLOBAL_LIB_TERMINATOR
Definition global_lib.h:51
Structure to define how to initialise libraries with global configuration.
Definition global_lib.h:38
static xlat_action_t smtp_send_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Xlat to send simple emails.
Definition rlm_smtp.c:868
talloc_free(hp)
int fr_curl_response_certinfo(request_t *request, fr_curl_io_request_t *randle)
Definition base.c:170
int fr_curl_easy_tls_init(fr_curl_io_request_t *randle, fr_curl_tls_t const *conf)
Definition base.c:139
global_lib_autoinst_t fr_curl_autoinst
Definition base.c:396
conf_parser_t fr_curl_conn_config[]
Definition base.c:97
conf_parser_t fr_curl_tls_config[]
Definition base.c:68
#define RDEBUG3(fmt,...)
Definition log.h:355
#define RWARN(fmt,...)
Definition log.h:309
#define RERROR(fmt,...)
Definition log.h:310
#define RPERROR(fmt,...)
Definition log.h:314
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition log.h:259
#define RPEDEBUG2(fmt,...)
Definition log.h:389
#define REDEBUG2(fmt,...)
Definition log.h:384
int rad_filename_box_make_safe(fr_value_box_t *vb, UNUSED void *uxtc)
Definition util.c:131
#define fr_time()
Definition event.c:60
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:121
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_BOOL
A truth value.
long int ssize_t
unsigned char uint8_t
void * env_data
Per call environment data.
Definition module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * thread
Thread specific instance data.
Definition module_ctx.h:43
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
fr_event_list_t * el
Event list to register any IO handlers and timers against.
Definition module_ctx.h:68
void * thread
Thread instance data.
Definition module_ctx.h:67
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:64
#define MODULE_CTX(_mi, _thread, _env_data, _rctx)
Wrapper to create a module_ctx_t as a compound literal.
Definition module_ctx.h:128
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
Temporary structure to hold arguments for thread_instantiation calls.
Definition module_ctx.h:63
xlat_t * module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Definition module_rlm.c:232
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:707
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define RDEBUG(fmt,...)
#define RETURN_UNLANG_INVALID
Definition rcode.h:66
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:61
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
#define RETURN_UNLANG_REJECT
Definition rcode.h:62
#define RETURN_UNLANG_OK
Definition rcode.h:64
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:51
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
static const call_env_method_t auth_env
Definition rlm_smtp.c:1212
struct curl_slist * recipients
Definition rlm_smtp.c:167
static int header_source(fr_mail_ctx_t *uctx, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
Definition rlm_smtp.c:398
fr_value_box_list_t headers
Entries to add to email header.
Definition rlm_smtp.c:91
fr_time_t time
Definition rlm_smtp.c:171
fr_value_box_list_t * bcc_addrs
The address(es) used for the Bcc: header.
Definition rlm_smtp.c:70
global_lib_autoinst_t const *const rlm_smtp_lib[]
Definition rlm_smtp.c:55
static unlang_action_t smtp_io_module_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Callback to process response of SMTP server.
Definition rlm_smtp.c:607
static int generate_from_header(fr_mail_ctx_t *uctx, struct curl_slist **out, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
Generate the From: header.
Definition rlm_smtp.c:323
static int value_box_list_to_slist(struct curl_slist **out, fr_value_box_list_t const *lists)
Transform an array of value box lists to entries in a CURL slist.
Definition rlm_smtp.c:202
static int smtp_persist_conn_alloc(fr_curl_io_request_t *randle, UNUSED void *uctx)
Definition rlm_smtp.c:1022
static const call_env_method_t smtp_call_env_xlat
Definition rlm_smtp.c:97
static int value_box_list_to_header(fr_mail_ctx_t *uctx, struct curl_slist **out, fr_value_box_list_t const *lists, const char *prefix)
Converts an array of value box lists to a curl_slist with a prefix.
Definition rlm_smtp.c:231
fr_value_box_t username
Value to use for user name.
Definition rlm_smtp.c:78
static int attachments_source(fr_mail_ctx_t *uctx, curl_mime *mime, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
Definition rlm_smtp.c:537
static void smtp_xlat_signal(xlat_ctx_t const *xctx, request_t *request, fr_signal_t action)
Definition rlm_smtp.c:851
static int smtp_persist_conn_init(fr_curl_io_request_t *randle, void *uctx)
Definition rlm_smtp.c:1081
fr_dcursor_t cursor
Definition rlm_smtp.c:163
smtp_slab_list_t * slab_persist
Slab list for persistent connections.
Definition rlm_smtp.c:153
static int smtp_conn_common_init(fr_curl_io_request_t *randle, rlm_smtp_t const *inst)
Definition rlm_smtp.c:1041
fr_curl_io_request_t * randle
Definition rlm_smtp.c:162
static fr_dict_attr_t const * attr_smtp_body
Definition rlm_smtp.c:45
static const call_env_method_t method_env
Definition rlm_smtp.c:1189
static int smtp_onetime_request_cleanup(fr_curl_io_request_t *randle, UNUSED void *uctx)
Definition rlm_smtp.c:985
static fr_dict_t const * dict_freeradius
Definition rlm_smtp.c:36
fr_value_box_t password
Password for authenticated mails.
Definition rlm_smtp.c:89
fr_dict_attr_autoload_t rlm_smtp_dict_attr[]
Definition rlm_smtp.c:48
fr_time_delta_t timeout
Timeout for connection and server response.
Definition rlm_smtp.c:115
static int recipients_source(fr_mail_ctx_t *uctx, rlm_smtp_env_t const *call_env)
Definition rlm_smtp.c:355
fr_dict_autoload_t rlm_smtp_dict[]
Definition rlm_smtp.c:39
fr_curl_conn_config_t conn_config
Reusable CURL handle config.
Definition rlm_smtp.c:120
tmpl_t * username_tmpl
tmpl expanded to populate username
Definition rlm_smtp.c:79
static void smtp_io_module_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Definition rlm_smtp.c:580
char const * uri
URI of smtp server.
Definition rlm_smtp.c:111
fr_value_box_t username
User to authenticate as when sending emails.
Definition rlm_smtp.c:63
fr_dbuff_t vp_in
Definition rlm_smtp.c:165
fr_value_box_t password
Value to use for password.
Definition rlm_smtp.c:80
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_smtp.c:958
struct curl_slist * header
Definition rlm_smtp.c:168
tmpl_t * password_tmpl
tmpl expanded to populate password
Definition rlm_smtp.c:81
static int smtp_header_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule)
static fr_dict_attr_t const * attr_smtp_header
Definition rlm_smtp.c:44
fr_dcursor_t body_cursor
Definition rlm_smtp.c:164
static int smtp_persist_request_cleanup(fr_curl_io_request_t *randle, UNUSED void *uctx)
Definition rlm_smtp.c:994
curl_mime * mime
Definition rlm_smtp.c:173
fr_value_box_list_t * cc_addrs
The address(es) used for the Cc: header.
Definition rlm_smtp.c:69
fr_value_box_list_t * to_addrs
The address(es) used for the To: header.
Definition rlm_smtp.c:68
#define SMTP_COMMON_CLEANUP
Definition rlm_smtp.c:979
request_t * request
Definition rlm_smtp.c:161
static int body_init(fr_mail_ctx_t *uctx, curl_mime *mime)
Definition rlm_smtp.c:490
fr_value_box_list_t headers
Entries to add to email header.
Definition rlm_smtp.c:72
module_rlm_t rlm_smtp
Definition rlm_smtp.c:1239
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Definition rlm_smtp.c:1094
struct curl_slist * body_header
Definition rlm_smtp.c:169
static unlang_action_t mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_smtp.c:779
bool set_date
Definition rlm_smtp.c:118
static int str_to_attachments(fr_mail_ctx_t *uctx, curl_mime *mime, fr_value_box_t *vb, fr_sbuff_t *path_buffer, fr_sbuff_marker_t *m)
Definition rlm_smtp.c:282
static unlang_action_t mod_mail(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_smtp.c:657
static int smtp_onetime_conn_alloc(fr_curl_io_request_t *randle, UNUSED void *uctx)
Definition rlm_smtp.c:1003
fr_value_box_list_t * sender_address
The address(es) used to generate the From: header.
Definition rlm_smtp.c:90
fr_value_box_t password
Password for authenticated mails.
Definition rlm_smtp.c:65
char const * envelope_address
The address used to send the message.
Definition rlm_smtp.c:113
fr_value_box_list_t * recipient_addrs
The address(es) used as recipients. Overrides elements in to, cc and bcc.
Definition rlm_smtp.c:67
tmpl_t * username_tmpl
The tmpl used to produce the above.
Definition rlm_smtp.c:88
static int smtp_onetime_conn_init(fr_curl_io_request_t *randle, void *uctx)
Definition rlm_smtp.c:1064
char time_str[60]
Definition rlm_smtp.c:172
tmpl_t * username_tmpl
The tmpl used to produce the above.
Definition rlm_smtp.c:64
static size_t body_source(char *ptr, size_t size, size_t nmemb, void *mail_ctx)
Definition rlm_smtp.c:449
static xlat_arg_parser_t const smtp_xlat_args[]
Definition rlm_smtp.c:822
char const * template_dir
The directory that contains all email attachments.
Definition rlm_smtp.c:112
fr_value_box_t username
User to authenticate as when sending emails.
Definition rlm_smtp.c:87
fr_curl_tls_t tls
Used for handled all tls specific curl components.
Definition rlm_smtp.c:116
static int da_to_slist(fr_mail_ctx_t *uctx, struct curl_slist **out, const fr_dict_attr_t *dict_attr)
Definition rlm_smtp.c:179
static const conf_parser_t module_config[]
Definition rlm_smtp.c:126
static int smtp_mail_ctx_free(fr_mail_ctx_t *mail_ctx)
Definition rlm_smtp.c:1015
fr_value_box_list_t * attachments
List of files to attach.
Definition rlm_smtp.c:71
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Definition rlm_smtp.c:1123
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_smtp.c:969
fr_curl_handle_t * mhandle
Thread specific multi handle.
Definition rlm_smtp.c:151
smtp_slab_list_t * slab_onetime
Slab list for onetime use connections.
Definition rlm_smtp.c:154
static xlat_action_t smtp_send_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Definition rlm_smtp.c:829
fr_value_box_list_t * sender_address
The address(es) used to generate the From: header.
Definition rlm_smtp.c:66
Call environment for SMTP authentication.
Definition rlm_smtp.c:77
Call environment for sending emails.
Definition rlm_smtp.c:62
Call environment for sending simple emails using an xlat.
Definition rlm_smtp.c:86
ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1469
ssize_t fr_sbuff_in_bstrncpy(fr_sbuff_t *sbuff, char const *str, size_t len)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1493
ssize_t fr_sbuff_in_bstrcpy_buffer(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1513
#define fr_sbuff_set(_dst, _src)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_buff(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define fr_sbuff_in_char(_sbuff,...)
Talloc sbuff extension structure.
Definition sbuff.h:137
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:293
void * boot
Data allocated during the boostrap phase.
Definition module.h:296
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Named methods exported by a module.
Definition module.h:174
int tmpl_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules))
Attempt to resolve functions and attributes in xlats and attribute references.
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
#define tmpl_is_data(vpt)
Definition tmpl.h:206
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:339
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:223
Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution ...
Definition tmpl.h:368
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
#define FR_SLAB_FUNCS(_name, _type)
Define type specific wrapper functions for slabs and slab elements.
Definition slab.h:124
#define FR_SLAB_TYPES(_name, _type)
Define type specific wrapper structs for slabs and slab elements.
Definition slab.h:75
return count
Definition module.c:155
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition module.c:431
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
#define talloc_get_type_abort_const
Definition talloc.h:117
#define talloc_asprintf
Definition talloc.h:151
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:143
size_t fr_time_strftime_local(fr_sbuff_t *out, fr_time_t time, char const *fmt)
Copy a time string (local timezone) to an sbuff.
Definition time.c:519
static int64_t fr_time_delta_to_msec(fr_time_delta_t delta)
Definition time.h:637
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
@ T_BARE_WORD
Definition token.h:118
@ T_DOUBLE_QUOTED_STRING
Definition token.h:119
xlat_action_t unlang_xlat_yield(request_t *request, xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition xlat.c:543
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:383
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:146
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:170
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
Definition for a single argument consumed by an xlat function.
Definition xlat.h:145
#define fr_pair_dcursor_by_da_init(_cursor, _list, _da)
Initialise a cursor that will return only attributes matching the specified fr_dict_attr_t.
Definition pair.h:639
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
fr_sbuff_parse_rules_t const * value_parse_rules_quoted[T_TOKEN_LAST]
Parse rules for quoted strings.
Definition value.c:611
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
Definition value.c:4196
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:644
int nonnull(2, 5))
static size_t char ** out
Definition value.h:1030
void * rctx
Resume context.
Definition xlat_ctx.h:54
void * env_data
Expanded call env data.
Definition xlat_ctx.h:53
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition xlat_ctx.h:52
An xlat calling ctx.
Definition xlat_ctx.h:49
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:365
void xlat_func_call_env_set(xlat_t *x, call_env_method_t const *env_method)
Register call environment of an xlat.
Definition xlat_func.c:382