1 /*
2 * Copyright (c) 2000 Charles Ying. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the same terms as sendmail itself.
6 *
7 */
8
9 #include "EXTERN.h"
10 #include "perl.h"
11 #include "XSUB.h"
12 #include <pthread.h>
13
14 #include "intpools.h"
15
16 #include "libmilter/mfapi.h"
17
18 /* Keys for each callback for the register callback hash */
19
20 #define KEY_CONNECT newSVpv("connect", 0)
21 #define KEY_HELO newSVpv("helo", 0)
22 #define KEY_ENVFROM newSVpv("envfrom", 0)
23 #define KEY_ENVRCPT newSVpv("envrcpt", 0)
24 #define KEY_HEADER newSVpv("header", 0)
25 #define KEY_EOH newSVpv("eoh", 0)
26 #define KEY_BODY newSVpv("body", 0)
27 #define KEY_EOM newSVpv("eom", 0)
28 #define KEY_ABORT newSVpv("abort", 0)
29 #define KEY_CLOSE newSVpv("close", 0)
30
31 /* Macro for pushing the SMFICTX * argument */
32
33 #define XPUSHs_Sendmail_Milter_Context \
34 (XPUSHs(sv_2mortal(sv_setref_iv(NEWSV(25, 0), \
35 "Sendmail::Milter::Context", (IV) ctx))))
36
37 /* Global callback variable names */
38
39 #define GLOBAL_CONNECT "Sendmail::Milter::Callbacks::_xxfi_connect"
40 #define GLOBAL_HELO "Sendmail::Milter::Callbacks::_xxfi_helo"
41 #define GLOBAL_ENVFROM "Sendmail::Milter::Callbacks::_xxfi_envfrom"
42 #define GLOBAL_ENVRCPT "Sendmail::Milter::Callbacks::_xxfi_envrcpt"
43 #define GLOBAL_HEADER "Sendmail::Milter::Callbacks::_xxfi_header"
44 #define GLOBAL_EOH "Sendmail::Milter::Callbacks::_xxfi_eoh"
45 #define GLOBAL_BODY "Sendmail::Milter::Callbacks::_xxfi_body"
46 #define GLOBAL_EOM "Sendmail::Milter::Callbacks::_xxfi_eom"
47 #define GLOBAL_ABORT "Sendmail::Milter::Callbacks::_xxfi_abort"
48 #define GLOBAL_CLOSE "Sendmail::Milter::Callbacks::_xxfi_close"
49
50
51 /* Callback prototypes for first-level callback wrappers. */
52
53 sfsistat hook_connect(SMFICTX *, char *, _SOCK_ADDR *);
54 sfsistat hook_helo(SMFICTX *, char *);
55 sfsistat hook_envfrom(SMFICTX *, char **);
56 sfsistat hook_envrcpt(SMFICTX *, char **);
57 sfsistat hook_header(SMFICTX *, char *, char *);
58 sfsistat hook_eoh(SMFICTX *);
59 sfsistat hook_body(SMFICTX *, u_char *, size_t);
60 sfsistat hook_eom(SMFICTX *);
61 sfsistat hook_abort(SMFICTX *);
62 sfsistat hook_close(SMFICTX *);
63
64
65 /* A structure for housing callbacks and their mutexes. */
66
67 struct callback_cache_t
68 {
69 SV *xxfi_connect;
70 SV *xxfi_helo;
71 SV *xxfi_envfrom;
72 SV *xxfi_envrcpt;
73 SV *xxfi_header;
74 SV *xxfi_eoh;
75 SV *xxfi_body;
76 SV *xxfi_eom;
77 SV *xxfi_abort;
78 SV *xxfi_close;
79 };
80
81 typedef struct callback_cache_t callback_cache_t;
82
83
84 /* The Milter perl interpreter pool */
85
86 static intpool_t I_pool;
87
88
89 /* Routines for managing callback caches */
90
91 void
init_callback_cache(pTHX_ interp_t * interp)92 init_callback_cache(pTHX_ interp_t *interp)
93 {
94 callback_cache_t *cache_ptr;
95
96 if (interp->cache != NULL)
97 return;
98
99 alloc_interpreter_cache(interp, sizeof(callback_cache_t));
100
101 cache_ptr = (callback_cache_t *)interp->cache;
102
103 cache_ptr->xxfi_connect = get_sv(GLOBAL_CONNECT, FALSE);
104 cache_ptr->xxfi_helo = get_sv(GLOBAL_HELO, FALSE);
105 cache_ptr->xxfi_envfrom = get_sv(GLOBAL_ENVFROM, FALSE);
106 cache_ptr->xxfi_envrcpt = get_sv(GLOBAL_ENVRCPT, FALSE);
107 cache_ptr->xxfi_header = get_sv(GLOBAL_HEADER, FALSE);
108 cache_ptr->xxfi_eoh = get_sv(GLOBAL_EOH, FALSE);
109 cache_ptr->xxfi_body = get_sv(GLOBAL_BODY, FALSE);
110 cache_ptr->xxfi_eom = get_sv(GLOBAL_EOM, FALSE);
111 cache_ptr->xxfi_abort = get_sv(GLOBAL_ABORT, FALSE);
112 cache_ptr->xxfi_close = get_sv(GLOBAL_CLOSE, FALSE);
113 }
114
115
116 /* Set global variables in the parent interpreter. */
117
118 void
init_callback(char * var_name,SV * parent_callback)119 init_callback(char *var_name, SV *parent_callback)
120 {
121 SV *new_sv;
122
123 new_sv = get_sv(var_name, TRUE);
124 sv_setsv(new_sv, parent_callback);
125 }
126
127
128 /* Main interfaces. */
129
130 void
init_callbacks(max_interpreters,max_requests)131 init_callbacks(max_interpreters, max_requests)
132 int max_interpreters;
133 int max_requests;
134 {
135 init_interpreters(&I_pool, max_interpreters, max_requests);
136 }
137
138
139 SV *
get_callback(perl_desc,key)140 get_callback(perl_desc, key)
141 HV *perl_desc;
142 SV *key;
143 {
144 HE *entry;
145
146 entry = hv_fetch_ent(perl_desc, key, 0, 0);
147
148 if (entry == NULL)
149 croak("couldn't fetch callback symbol from descriptor.");
150
151 return newSVsv(HeVAL(entry));
152 }
153
154
155 void
register_callbacks(desc,name,my_callback_table,flags)156 register_callbacks(desc, name, my_callback_table, flags)
157 struct smfiDesc *desc;
158 char *name;
159 HV *my_callback_table;
160 int flags;
161 {
162 memset(desc, '\0', sizeof(struct smfiDesc));
163
164 desc->xxfi_name = strdup(name);
165 desc->xxfi_version = SMFI_VERSION;
166 desc->xxfi_flags = flags;
167
168 if (hv_exists_ent(my_callback_table, KEY_CONNECT, 0))
169 {
170 init_callback(GLOBAL_CONNECT,
171 get_callback(my_callback_table, KEY_CONNECT));
172
173 desc->xxfi_connect = hook_connect;
174 }
175
176 if (hv_exists_ent(my_callback_table, KEY_HELO, 0))
177 {
178 init_callback(GLOBAL_HELO,
179 get_callback(my_callback_table, KEY_HELO));
180
181 desc->xxfi_helo = hook_helo;
182 }
183
184 if (hv_exists_ent(my_callback_table, KEY_ENVFROM, 0))
185 {
186 init_callback(GLOBAL_ENVFROM,
187 get_callback(my_callback_table, KEY_ENVFROM));
188
189 desc->xxfi_envfrom = hook_envfrom;
190 }
191
192 if (hv_exists_ent(my_callback_table, KEY_ENVRCPT, 0))
193 {
194 init_callback(GLOBAL_ENVRCPT,
195 get_callback(my_callback_table, KEY_ENVRCPT));
196
197 desc->xxfi_envrcpt = hook_envrcpt;
198 }
199
200 if (hv_exists_ent(my_callback_table, KEY_HEADER, 0))
201 {
202 init_callback(GLOBAL_HEADER,
203 get_callback(my_callback_table, KEY_HEADER));
204
205 desc->xxfi_header = hook_header;
206 }
207
208 if (hv_exists_ent(my_callback_table, KEY_EOH, 0))
209 {
210 init_callback(GLOBAL_EOH,
211 get_callback(my_callback_table, KEY_EOH));
212
213 desc->xxfi_eoh = hook_eoh;
214 }
215
216 if (hv_exists_ent(my_callback_table, KEY_BODY, 0))
217 {
218 init_callback(GLOBAL_BODY,
219 get_callback(my_callback_table, KEY_BODY));
220
221 desc->xxfi_body = hook_body;
222 }
223
224 if (hv_exists_ent(my_callback_table, KEY_EOM, 0))
225 {
226 init_callback(GLOBAL_EOM,
227 get_callback(my_callback_table, KEY_EOM));
228
229 desc->xxfi_eom = hook_eom;
230 }
231
232 if (hv_exists_ent(my_callback_table, KEY_ABORT, 0))
233 {
234 init_callback(GLOBAL_ABORT,
235 get_callback(my_callback_table, KEY_ABORT));
236
237 desc->xxfi_abort = hook_abort;
238 }
239
240 if (hv_exists_ent(my_callback_table, KEY_CLOSE, 0))
241 {
242 init_callback(GLOBAL_CLOSE,
243 get_callback(my_callback_table, KEY_CLOSE));
244
245 desc->xxfi_close = hook_close;
246 }
247 }
248
249
250 /* Second-layer callbacks. These do the actual work. */
251
252 sfsistat
callback_noargs(pTHX_ SV * callback,SMFICTX * ctx)253 callback_noargs(pTHX_ SV *callback, SMFICTX *ctx)
254 {
255 int n;
256 sfsistat retval;
257 dSP;
258 ENTER;
259 SAVETMPS;
260 PUSHMARK(SP);
261
262 XPUSHs_Sendmail_Milter_Context;
263
264 PUTBACK;
265
266 n = call_sv(callback, G_EVAL | G_SCALAR);
267
268 SPAGAIN;
269
270 /* Check the eval first. */
271 if (SvTRUE(ERRSV))
272 {
273 POPs;
274 retval = SMFIS_TEMPFAIL;
275 }
276 else if (n == 1)
277 {
278 retval = (sfsistat) POPi;
279 }
280 else
281 {
282 retval = SMFIS_CONTINUE;
283 }
284
285 PUTBACK;
286 FREETMPS;
287 LEAVE;
288
289 return retval;
290 }
291
292 sfsistat
callback_s(pTHX_ SV * callback,SMFICTX * ctx,char * arg1)293 callback_s(pTHX_ SV *callback, SMFICTX *ctx, char *arg1)
294 {
295 int n;
296 sfsistat retval;
297 dSP;
298 ENTER;
299 SAVETMPS;
300 PUSHMARK(SP);
301
302 XPUSHs_Sendmail_Milter_Context;
303 XPUSHs(sv_2mortal(newSVpv(arg1, 0)));
304
305 PUTBACK;
306
307 n = call_sv(callback, G_EVAL | G_SCALAR);
308
309 SPAGAIN;
310
311 /* Check the eval first. */
312 if (SvTRUE(ERRSV))
313 {
314 POPs;
315 retval = SMFIS_TEMPFAIL;
316 }
317 else if (n == 1)
318 {
319 retval = (sfsistat) POPi;
320 }
321 else
322 {
323 retval = SMFIS_CONTINUE;
324 }
325
326 PUTBACK;
327 FREETMPS;
328 LEAVE;
329
330 return retval;
331 }
332
333 sfsistat
callback_body(pTHX_ SV * callback,SMFICTX * ctx,u_char * arg1,size_t arg2)334 callback_body(pTHX_ SV *callback, SMFICTX *ctx,
335 u_char *arg1, size_t arg2)
336 {
337 int n;
338 sfsistat retval;
339 dSP;
340 ENTER;
341 SAVETMPS;
342 PUSHMARK(SP);
343
344 XPUSHs_Sendmail_Milter_Context;
345 XPUSHs(sv_2mortal(newSVpvn(arg1, arg2)));
346 XPUSHs(sv_2mortal(newSViv((IV) arg2)));
347
348 PUTBACK;
349
350 n = call_sv(callback, G_EVAL | G_SCALAR);
351
352 SPAGAIN;
353
354 /* Check the eval first. */
355 if (SvTRUE(ERRSV))
356 {
357 POPs;
358 retval = SMFIS_TEMPFAIL;
359 }
360 else if (n == 1)
361 {
362 retval = (sfsistat) POPi;
363 }
364 else
365 {
366 retval = SMFIS_CONTINUE;
367 }
368
369 PUTBACK;
370 FREETMPS;
371 LEAVE;
372
373 return retval;
374 }
375
376 sfsistat
callback_argv(pTHX_ SV * callback,SMFICTX * ctx,char ** arg1)377 callback_argv(pTHX_ SV *callback, SMFICTX *ctx, char **arg1)
378 {
379 int n;
380 sfsistat retval;
381 char **iter = arg1;
382 dSP;
383 ENTER;
384 SAVETMPS;
385 PUSHMARK(SP);
386
387 XPUSHs_Sendmail_Milter_Context;
388
389 while(iter != NULL)
390 {
391 if (*iter == NULL)
392 break;
393
394 XPUSHs(sv_2mortal(newSVpv(*iter, 0)));
395 iter++;
396 }
397
398 PUTBACK;
399
400 n = call_sv(callback, G_EVAL | G_SCALAR);
401
402 SPAGAIN;
403
404 /* Check the eval first. */
405 if (SvTRUE(ERRSV))
406 {
407 POPs;
408 retval = SMFIS_TEMPFAIL;
409 }
410 else if (n == 1)
411 {
412 retval = (sfsistat) POPi;
413 }
414 else
415 {
416 retval = SMFIS_CONTINUE;
417 }
418
419 PUTBACK;
420 FREETMPS;
421 LEAVE;
422
423 return retval;
424 }
425
426 sfsistat
callback_ss(pTHX_ SV * callback,SMFICTX * ctx,char * arg1,char * arg2)427 callback_ss(pTHX_ SV *callback, SMFICTX *ctx, char *arg1, char *arg2)
428 {
429 int n;
430 sfsistat retval;
431 dSP;
432 ENTER;
433 SAVETMPS;
434 PUSHMARK(SP);
435
436 XPUSHs_Sendmail_Milter_Context;
437 XPUSHs(sv_2mortal(newSVpv(arg1, 0)));
438 XPUSHs(sv_2mortal(newSVpv(arg2, 0)));
439
440 PUTBACK;
441
442 n = call_sv(callback, G_EVAL | G_SCALAR);
443
444 SPAGAIN;
445
446 /* Check the eval first. */
447 if (SvTRUE(ERRSV))
448 {
449 POPs;
450 retval = SMFIS_TEMPFAIL;
451 }
452 else if (n == 1)
453 {
454 retval = (sfsistat) POPi;
455 }
456 else
457 {
458 retval = SMFIS_CONTINUE;
459 }
460
461 PUTBACK;
462 FREETMPS;
463 LEAVE;
464
465 return retval;
466 }
467
468 sfsistat
callback_ssockaddr(pTHX_ SV * callback,SMFICTX * ctx,char * arg1,_SOCK_ADDR * arg_sa)469 callback_ssockaddr(pTHX_ SV *callback, SMFICTX *ctx, char *arg1,
470 _SOCK_ADDR *arg_sa)
471 {
472 int n;
473 sfsistat retval;
474 dSP;
475 ENTER;
476 SAVETMPS;
477 PUSHMARK(SP);
478
479 XPUSHs_Sendmail_Milter_Context;
480
481 XPUSHs(sv_2mortal(newSVpv(arg1, 0)));
482
483 /* A Perl sockaddr_in is all we handle right now. */
484 if (arg_sa == NULL)
485 {
486 XPUSHs(sv_2mortal(newSVsv(&PL_sv_undef)));
487 }
488 else if (arg_sa->sa_family == AF_INET)
489 {
490 XPUSHs(sv_2mortal(newSVpvn((char *)arg_sa,
491 sizeof(_SOCK_ADDR))));
492 }
493 else if (arg_sa->sa_family == AF_INET6)
494 {
495 XPUSHs(sv_2mortal(newSVpvn((char *)arg_sa,
496 sizeof(struct sockaddr_in6))));
497 }
498 else
499 {
500 XPUSHs(sv_2mortal(newSVsv(&PL_sv_undef)));
501 }
502
503 PUTBACK;
504
505 n = call_sv(callback, G_EVAL | G_SCALAR);
506
507 SPAGAIN;
508
509 /* Check the eval first. */
510 if (SvTRUE(ERRSV))
511 {
512 POPs;
513 retval = SMFIS_TEMPFAIL;
514 }
515 else if (n == 1)
516 {
517 retval = (sfsistat) POPi;
518 }
519 else
520 {
521 retval = SMFIS_CONTINUE;
522 }
523
524 PUTBACK;
525 FREETMPS;
526 LEAVE;
527
528 return retval;
529 }
530
531
532 /* First-layer callbacks */
533
534 sfsistat
hook_connect(ctx,hostname,hostaddr)535 hook_connect(ctx, hostname, hostaddr)
536 SMFICTX *ctx;
537 char *hostname;
538 _SOCK_ADDR *hostaddr;
539 {
540 interp_t *interp;
541 sfsistat retval;
542 SV *callback;
543
544 if ((interp = lock_interpreter(&I_pool)) == NULL)
545 croak("could not lock a new perl interpreter.");
546
547 PERL_SET_CONTEXT(interp->perl);
548
549 init_callback_cache(aTHX_ interp);
550 callback = ((callback_cache_t *)(interp->cache))->xxfi_connect;
551
552 retval = callback_ssockaddr(aTHX_ callback, ctx,
553 hostname, hostaddr);
554
555 unlock_interpreter(&I_pool, interp);
556
557 return retval;
558 }
559
560 sfsistat
hook_helo(ctx,helohost)561 hook_helo(ctx, helohost)
562 SMFICTX *ctx;
563 char *helohost;
564 {
565 interp_t *interp;
566 sfsistat retval;
567 SV *callback;
568
569 if ((interp = lock_interpreter(&I_pool)) == NULL)
570 croak("could not lock a new perl interpreter.");
571
572 PERL_SET_CONTEXT(interp->perl);
573
574 init_callback_cache(aTHX_ interp);
575 callback = ((callback_cache_t *)(interp->cache))->xxfi_helo;
576
577 retval = callback_s(aTHX_ callback, ctx, helohost);
578
579 unlock_interpreter(&I_pool, interp);
580
581 return retval;
582 }
583
584 sfsistat
hook_envfrom(ctx,argv)585 hook_envfrom(ctx, argv)
586 SMFICTX *ctx;
587 char **argv;
588 {
589 interp_t *interp;
590 sfsistat retval;
591 SV *callback;
592
593 if ((interp = lock_interpreter(&I_pool)) == NULL)
594 croak("could not lock a new perl interpreter.");
595
596 PERL_SET_CONTEXT(interp->perl);
597
598 init_callback_cache(aTHX_ interp);
599 callback = ((callback_cache_t *)(interp->cache))->xxfi_envfrom;
600
601 retval = callback_argv(aTHX_ callback, ctx, argv);
602
603 unlock_interpreter(&I_pool, interp);
604
605 return retval;
606 }
607
608 sfsistat
hook_envrcpt(ctx,argv)609 hook_envrcpt(ctx, argv)
610 SMFICTX *ctx;
611 char **argv;
612 {
613 interp_t *interp;
614 sfsistat retval;
615 SV *callback;
616
617 if ((interp = lock_interpreter(&I_pool)) == NULL)
618 croak("could not lock a new perl interpreter.");
619
620 PERL_SET_CONTEXT(interp->perl);
621
622 init_callback_cache(aTHX_ interp);
623 callback = ((callback_cache_t *)(interp->cache))->xxfi_envrcpt;
624
625 retval = callback_argv(aTHX_ callback, ctx, argv);
626
627 unlock_interpreter(&I_pool, interp);
628
629 return retval;
630 }
631
632 sfsistat
hook_header(ctx,headerf,headerv)633 hook_header(ctx, headerf, headerv)
634 SMFICTX *ctx;
635 char *headerf;
636 char *headerv;
637 {
638 interp_t *interp;
639 sfsistat retval;
640 SV *callback;
641
642 if ((interp = lock_interpreter(&I_pool)) == NULL)
643 croak("could not lock a new perl interpreter.");
644
645 PERL_SET_CONTEXT(interp->perl);
646
647 init_callback_cache(aTHX_ interp);
648 callback = ((callback_cache_t *)(interp->cache))->xxfi_header;
649
650 retval = callback_ss(aTHX_ callback, ctx, headerf, headerv);
651
652 unlock_interpreter(&I_pool, interp);
653
654 return retval;
655 }
656
657 sfsistat
hook_eoh(ctx)658 hook_eoh(ctx)
659 SMFICTX *ctx;
660 {
661 interp_t *interp;
662 sfsistat retval;
663 SV *callback;
664
665 if ((interp = lock_interpreter(&I_pool)) == NULL)
666 croak("could not lock a new perl interpreter.");
667
668 PERL_SET_CONTEXT(interp->perl);
669
670 init_callback_cache(aTHX_ interp);
671 callback = ((callback_cache_t *)(interp->cache))->xxfi_eoh;
672
673 retval = callback_noargs(aTHX_ callback, ctx);
674
675 unlock_interpreter(&I_pool, interp);
676
677 return retval;
678 }
679
680 sfsistat
hook_body(ctx,bodyp,bodylen)681 hook_body(ctx, bodyp, bodylen)
682 SMFICTX *ctx;
683 u_char *bodyp;
684 size_t bodylen;
685 {
686 interp_t *interp;
687 sfsistat retval;
688 SV *callback;
689
690 if ((interp = lock_interpreter(&I_pool)) == NULL)
691 croak("could not lock a new perl interpreter.");
692
693 PERL_SET_CONTEXT(interp->perl);
694
695 init_callback_cache(aTHX_ interp);
696 callback = ((callback_cache_t *)(interp->cache))->xxfi_body;
697
698 retval = callback_body(aTHX_ callback, ctx, bodyp, bodylen);
699
700 unlock_interpreter(&I_pool, interp);
701
702 return retval;
703 }
704
705 sfsistat
hook_eom(ctx)706 hook_eom(ctx)
707 SMFICTX *ctx;
708 {
709 interp_t *interp;
710 sfsistat retval;
711 SV *callback;
712
713 if ((interp = lock_interpreter(&I_pool)) == NULL)
714 croak("could not lock a new perl interpreter.");
715
716 PERL_SET_CONTEXT(interp->perl);
717
718 init_callback_cache(aTHX_ interp);
719 callback = ((callback_cache_t *)(interp->cache))->xxfi_eom;
720
721 retval = callback_noargs(aTHX_ callback, ctx);
722
723 unlock_interpreter(&I_pool, interp);
724
725 return retval;
726 }
727
728 sfsistat
hook_abort(ctx)729 hook_abort(ctx)
730 SMFICTX *ctx;
731 {
732 interp_t *interp;
733 sfsistat retval;
734 SV *callback;
735
736 if ((interp = lock_interpreter(&I_pool)) == NULL)
737 croak("could not lock a new perl interpreter.");
738
739 PERL_SET_CONTEXT(interp->perl);
740
741 init_callback_cache(aTHX_ interp);
742 callback = ((callback_cache_t *)(interp->cache))->xxfi_abort;
743
744 retval = callback_noargs(aTHX_ callback, ctx);
745
746 unlock_interpreter(&I_pool, interp);
747
748 return retval;
749 }
750
751 sfsistat
hook_close(ctx)752 hook_close(ctx)
753 SMFICTX *ctx;
754 {
755 interp_t *interp;
756 sfsistat retval;
757 SV *callback;
758
759 if ((interp = lock_interpreter(&I_pool)) == NULL)
760 croak("could not lock a new perl interpreter.");
761
762 PERL_SET_CONTEXT(interp->perl);
763
764 init_callback_cache(aTHX_ interp);
765 callback = ((callback_cache_t *)(interp->cache))->xxfi_close;
766
767 retval = callback_noargs(aTHX_ callback, ctx);
768
769 unlock_interpreter(&I_pool, interp);
770
771 return retval;
772 }
773
774