$Authen::SASL::Perl::VERSION
=
'2.1800'
;
my
%secflags
= (
noplaintext
=> 1,
noanonymous
=> 1,
nodictionary
=> 1,
);
my
%have
;
sub
server_new {
my
(
$pkg
,
$parent
,
$service
,
$host
,
$options
) =
@_
;
my
$self
= {
callback
=> { %{
$parent
->callback} },
service
=>
$service
||
''
,
host
=>
$host
||
''
,
debug
=>
$parent
->{debug} || 0,
need_step
=> 1,
};
my
$mechanism
=
$parent
->mechanism
or croak
"No server mechanism specified"
;
$mechanism
=~ s/^\s*\b(.*)\b\s*$/$1/g;
$mechanism
=~ s/-/_/g;
$mechanism
=
uc
$mechanism
;
my
$mpkg
= __PACKAGE__ .
"::$mechanism"
;
eval
"require $mpkg;"
or croak
"Cannot use $mpkg for "
.
$parent
->mechanism;
my
$server
=
$mpkg
->_init(
$self
);
$server
->_init_server(
$options
);
return
$server
;
}
sub
client_new {
my
(
$pkg
,
$parent
,
$service
,
$host
,
$secflags
) =
@_
;
my
@sec
=
grep
{
$secflags
{
$_
} }
split
/\W+/,
lc
(
$secflags
||
''
);
my
$self
= {
callback
=> { %{
$parent
->callback} },
service
=>
$service
||
''
,
host
=>
$host
||
''
,
debug
=>
$parent
->{debug} || 0,
need_step
=> 1,
};
my
@mpkg
=
sort
{
$b
->_order <=>
$a
->_order
}
grep
{
my
$have
=
$have
{
$_
} ||= (
eval
"require $_;"
and
$_
->can(
'_secflags'
)) ? 1 : -1;
$have
> 0 and
$_
->_secflags(
@sec
) ==
@sec
and
$_
->_acceptable( %{
$parent
->callback} )
}
map
{
(
my
$mpkg
= __PACKAGE__ .
"::$_"
) =~ s/-/_/g;
$mpkg
;
}
split
/[^-\w]+/,
$parent
->mechanism
or croak
"No SASL mechanism found: "
,
$parent
->mechanism,
"\n"
;
$mpkg
[0]->_init(
$self
);
}
sub
_init_server {}
sub
_acceptable { 1 }
sub
_order { 0 }
sub
code {
defined
(
shift
->{error}) || 0 }
sub
error {
shift
->{error} }
sub
service {
shift
->{service} }
sub
host {
shift
->{host} }
sub
need_step {
my
$self
=
shift
;
return
0
if
$self
->{error};
return
$self
->{need_step};
}
sub
set_success {
my
$self
=
shift
;
$self
->{need_step} = 0;
}
sub
is_success {
my
$self
=
shift
;
return
!
$self
->code && !
$self
->need_step;
}
sub
set_error {
my
$self
=
shift
;
$self
->{error} =
shift
;
return
;
}
sub
property {
my
$self
=
shift
;
my
$prop
=
$self
->{property} ||= {};
return
$prop
->{
$_
[0] }
if
@_
== 1;
my
%new
=
@_
;
@{
$prop
}{
keys
%new
} =
values
%new
;
1;
}
sub
callback {
my
$self
=
shift
;
return
$self
->{callback}{
$_
[0]}
if
@_
== 1;
my
%new
=
@_
;
@{
$self
->{callback}}{
keys
%new
} =
values
%new
;
$self
->{callback};
}
sub
mechanism {
undef
}
sub
client_step {
undef
}
sub
client_start {
undef
}
sub
server_step {
undef
}
sub
server_start {
undef
}
sub
_init {
my
(
$pkg
,
$href
) =
@_
;
bless
$href
,
$pkg
;
}
sub
_call {
my
(
$self
,
$name
) =
splice
(
@_
,0,2);
my
$cb
=
$self
->{callback}{
$name
};
return
undef
unless
defined
$cb
;
my
$value
;
if
(
ref
(
$cb
) eq
'ARRAY'
) {
my
@args
=
@$cb
;
$cb
=
shift
@args
;
$value
=
$cb
->(
$self
,
@args
);
}
elsif
(
ref
(
$cb
) eq
'CODE'
) {
$value
=
$cb
->(
$self
,
@_
);
}
else
{
$value
=
$cb
;
}
$self
->{answer}{
$name
} =
$value
unless
$name
eq
'pass'
;
return
$value
;
}
sub
answer {
my
(
$self
,
$name
) =
@_
;
$self
->{answer}{
$name
};
}
sub
_secflags { 0 }
sub
securesocket {
my
$self
=
shift
;
return
$_
[0]
unless
(
defined
(
$self
->property(
'ssf'
)) &&
$self
->property(
'ssf'
) > 0);
local
*GLOB
;
my
$glob
= \
do
{
local
*GLOB
; };
tie
(
*$glob
,
'Authen::SASL::Perl::Layer'
,
$_
[0],
$self
);
$glob
;
}
{
package
Authen::SASL::Perl::Layer;
our
@ISA
=
qw(Tie::Handle)
;
sub
TIEHANDLE {
my
(
$class
,
$fh
,
$conn
) =
@_
;
my
$self
;
warn
__PACKAGE__ .
': non-blocking handle may not work'
if
(
$fh
->can(
'blocking'
) and not
$fh
->blocking());
$self
->{fh} =
$fh
;
$self
->{conn} =
$conn
;
$self
->{readbuflen} = 0;
$self
->{sndbufsz} =
$conn
->property(
'maxout'
);
$self
->{rcvbufsz} =
$conn
->property(
'maxbuf'
);
return
bless
(
$self
,
$class
);
}
sub
CLOSE {
my
(
$self
) =
@_
;
close
(
$self
->{fh});
delete
$self
->{fh};
}
sub
DESTROY {
my
(
$self
) =
@_
;
delete
$self
->{fh};
undef
$self
;
}
sub
FETCH {
my
(
$self
) =
@_
;
return
$self
->{fh};
}
sub
FILENO {
my
(
$self
) =
@_
;
return
fileno
(
$self
->{fh});
}
sub
READ {
my
(
$self
,
$buf
,
$len
,
$offset
) =
@_
;
my
$debug
=
$self
->{conn}->{debug};
$buf
= \
$_
[1];
my
$avail
=
$self
->{readbuflen};
print
STDERR
" [READ(len=$len,offset=$offset)] avail=$avail;\n"
if
(
$debug
& 4);
if
(
$avail
<= 0) {
$avail
=
$self
->_getbuf();
return
undef
unless
(
$avail
> 0);
}
if
(
$avail
>=
$len
) {
print
STDERR
" GOT ALL: avail=$avail; need=$len\n"
if
(
$debug
& 4);
substr
(
$$buf
,
$offset
,
$len
) =
substr
(
$self
->{readbuf}, 0,
$len
,
''
);
$self
->{readbuflen} -=
$len
;
return
(
$len
);
}
print
STDERR
" GOT PARTIAL: avail=$avail; need=$len\n"
if
(
$debug
& 4);
substr
(
$$buf
,
$offset
|| 0,
$avail
) =
$self
->{readbuf};
$self
->{readbuf} =
''
;
$self
->{readbuflen} = 0;
return
(
$avail
);
}
sub
_getbuf {
my
(
$self
) =
@_
;
my
$debug
=
$self
->{conn}->{debug};
my
$fh
=
$self
->{fh};
my
$buf
=
''
;
my
$n
= 0;
while
(
$n
< 4) {
my
$rv
=
sysread
(
$fh
,
$buf
, 4 -
$n
,
$n
);
print
STDERR
" [getbuf: sysread($fh,$buf,4-$n,$n)=$rv: $!\n"
if
(
$debug
& 4);
return
$rv
unless
$rv
> 0;
$n
+=
$rv
;
}
my
(
$bsz
) =
unpack
(
'N'
,
$buf
);
print
STDERR
" [getbuf: cipher buffer sz=$bsz]\n"
if
(
$debug
& 4);
return
undef
unless
(
$bsz
<=
$self
->{rcvbufsz});
$buf
=
''
;
$n
= 0;
while
(
$n
<
$bsz
) {
my
$rv
=
sysread
(
$fh
,
$buf
,
$bsz
-
$n
,
$n
);
print
STDERR
" [getbuf: got o=$n,n="
,
$bsz
-
$n
,
",rv=$rv,bl="
.
length
(
$buf
) .
"]\n"
if
(
$debug
& 4);
return
$rv
unless
$rv
> 0;
$n
+=
$rv
;
}
$self
->{readbuf} =
$self
->{conn}->decode(
$buf
,
$bsz
);
$n
=
length
(
$self
->{readbuf});
print
STDERR
" [getbuf: clear text buffer sz=$n]\n"
if
(
$debug
& 4);
$self
->{readbuflen} =
$n
;
}
sub
WRITE {
my
(
$self
,
$data
,
$len
,
$offset
) =
@_
;
my
$debug
=
$self
->{conn}->{debug};
my
$fh
=
$self
->{fh};
$len
=
length
(
$data
)
if
$len
>
length
(
$data
);
my
$bsz
=
$self
->{sndbufsz};
while
(
$len
> 0) {
print
STDERR
" [WRITE: chunk $bsz/$len]\n"
if
(
$debug
& 8);
my
$x
=
$self
->{conn}->encode(
substr
(
$data
,
$offset
|| 0,
$bsz
));
print
$fh
pack
(
'N'
,
length
(
$x
)),
$x
;
$len
-=
$bsz
;
$offset
+=
$bsz
;
}
return
$_
[2];
}
}
1;