File Coverage

blib/lib/Jabber/Component/Proxy.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             # Jabber::Component::Proxy
2             # (c) DJ Adams 2001
3              
4             # $Id: Proxy.pm,v 1.3 2002/04/01 18:02:32 dj Exp $
5              
6             =head1 NAME
7              
8             Jabber::Component::Proxy - A simple proxy for Jabber Components
9              
10             =head1 SYNOPSIS
11              
12             use Jabber::Component::Proxy
13              
14             # Create proxy
15             my $proxy = new Jabber::Component::Proxy(
16             server => 'localhost:6789',
17             identauth => 'conference.qmacro.dyndns.org',
18             realcomp => 'conference.internal',
19             rulefile => './access.xml',
20             );
21              
22             $proxy->start;
23              
24             =head1 DESCRIPTION
25              
26             Jabber::Component::Proxy is a simple proxy mechanism that you can
27             use to control access to your Jabber services. If you attach a
28             component to your Jabber server, and give that component a 'real'
29             resolvable FQDN, people on other Jabber servers can access that
30             component.
31              
32             This might be what you want. But what if you want to allow access
33             to some people but not others? How can you control access on a
34             domain name basis, for example? Currently component access is
35             all or nothing:
36              
37             - give a component a resolvable name and the world can use it
38             - give a component an internal name and no one but those connected
39             the same Jabber server as the component can use it
40              
41             (This is assuming of course you're running an s2s service).
42              
43             You can effect a sort of access control, for non-local users, using
44             this module.
45              
46             =head1 VERSION
47              
48             0.01 (early)
49              
50             =head1 AUTHOR
51              
52             DJ Adams
53              
54             =head1 SEE ALSO
55              
56             Jabber::Connection
57              
58             =cut
59              
60             package Jabber::Component::Proxy;
61              
62 1     1   617 use vars qw($VERSION);
  1         1  
  1         56  
63             $VERSION = '0.02';
64              
65 1     1   2162 use Jabber::Connection;
  0            
  0            
66             use Jabber::NodeFactory;
67             use XML::XPath;
68             use XML::XPath::XMLParser;
69              
70             use warnings;
71             use strict;
72              
73              
74             sub new {
75              
76             my $class = shift; my %args = @_;
77             my $self = {};
78              
79             # My (the client's) host/port and Identity
80             $self->{server} = $args{server};
81             $self->{realcomp} = $args{realcomp};
82             $self->{rulefile} = $args{rulefile};
83             ($self->{id}, $self->{pass}) = split(':', $args{identauth});
84              
85             die "Bad rulefile $args{rulefile}"
86             unless -f $args{rulefile} and -r $args{rulefile};
87              
88             # Connect to Jabber
89             $self->{connection} = new Jabber::Connection(
90             server => $self->{server},
91             localname => $self->{id},
92             ns => 'jabber:component:accept',
93             # log => 1,
94             # debug => 1,
95             );
96              
97             $self->{connection}->connect
98             or die "oops: ".$self->{connection}->lastError;
99             _debug("Connected");
100              
101             $self->{connection}->auth($self->{pass});
102             _debug("Authenticated");
103              
104             # Node factory
105             $self->{nf} = new Jabber::NodeFactory;
106              
107             # Set up handlers
108             $self->{connection}->register_handler('iq', sub { $self->_proxy(@_) } );
109             $self->{connection}->register_handler('message', sub { $self->_proxy(@_) } );
110             $self->{connection}->register_handler('presence', sub { $self->_proxy(@_) } );
111              
112             # Set up HUP handler
113             $SIG{HUP} = sub { $self->_readrules };
114              
115             # Set up end handler
116             $SIG{KILL} = $SIG{TERM} = $SIG{INT} = sub { $self->_cleanup };
117              
118             _debug("Handlers set up");
119              
120             bless $self, $class;
121              
122             # Read in rules
123             $self->_readrules;
124              
125             return $self;
126            
127             }
128              
129              
130             # Start the proxy
131             sub start {
132              
133             my $self = shift;
134              
135             _debug("Starting proxy");
136              
137             # Go!
138             $self->{connection}->start;
139              
140             }
141              
142              
143             sub _debug {
144              
145             print STDERR "DEBUG: @_\n";
146              
147             }
148              
149              
150             sub _proxy {
151              
152             my $self = shift;
153             my $node = shift;
154              
155             my $from = _breakJID($node->attr('from'));
156             my $to = _breakJID($node->attr('to'));
157              
158             # Going back OUT
159             if ($from->{host} eq $self->{realcomp}) {
160             $node->attr('from', _makeJID($from->{user}, $self->{id}, $from->{resource}));
161             $node->attr('to',pack("H*",$to->{user}));
162             }
163              
164             # Coming IN
165             else {
166              
167             # Only proceed if allowed
168             my $userhost = _makeJID($from->{user}, $from->{host}, undef);
169             if ($self->_access($userhost)) {
170             $node->attr('to', _makeJID($to->{user}, $self->{realcomp}, $to->{resource}));
171             $node->attr('from', _makeJID(unpack("H*",$node->attr('from')), $self->{id}, $from->{resource}));
172             }
173              
174             # Deny if not allowed
175             else {
176             _debug("denying $userhost");
177             $node->attr('type', 'error');
178             my $error = $node->insertTag('error');
179             $error->attr('code', 403);
180             $error->data('Forbidden');
181             $node = _toFrom($node);
182             }
183              
184             }
185              
186             $self->{connection}->send($node);
187              
188              
189             }
190              
191              
192             sub _access {
193              
194             my $self = shift;
195             my $from = shift;
196              
197             my $allowed = 0;
198              
199             if (exists $self->{rules}->{allow}) {
200             foreach my $rule (@{$self->{rules}->{allow}}) {
201             $allowed = 1, last if $from =~ /$rule$/;
202             }
203             }
204             else { $allowed = 1 }
205              
206             if ($allowed and exists $self->{rules}->{deny}) {
207             foreach my $rule (@{$self->{rules}->{deny}}) {
208             $allowed = 0, last if $from =~ /$rule$/;
209             }
210             }
211              
212             return $allowed;
213              
214             }
215              
216              
217             sub _toFrom {
218             my $node = shift;
219             my $to = $node->attr('to');
220             $node->attr('to', $node->attr('from'));
221             $node->attr('from', $to);
222             return $node;
223             }
224              
225              
226             sub _breakJID {
227              
228             my $jid = shift;
229             my ($u, $h, $r) = $jid =~ m/^([^@]+(?=\@))?\@?([\w+\.\-]+)\/?([\w \-]+)?$/;
230             return {
231             user => $u,
232             host => $h,
233             resource => $r,
234             };
235              
236             }
237              
238              
239             sub _makeJID {
240              
241             my ($u, $h, $r) = @_;
242             my $jid;
243             $jid = $u.'@' if defined($u);
244             $jid.=$h;
245             $jid.= '/'.$r if defined($r);
246             return $jid;
247              
248             }
249              
250              
251             sub _readrules {
252              
253             my $self = shift;
254              
255             _debug("Reading access rules");
256             delete $self->{rules};
257             my $xp = XML::XPath->new(filename => $self->{rulefile});
258             foreach (qw/allow deny/) {
259             foreach my $node ($xp->find("/access/$_/address")->get_nodelist) {
260             _debug("Adding $_ rule for ".$node->string_value);
261             push(@{$self->{rules}->{$_}}, $node->string_value);
262             }
263             }
264              
265             }
266              
267              
268             sub _cleanup {
269              
270             my $self = shift;
271              
272             _debug("Shutting down");
273             $self->{connection}->disconnect;
274              
275             exit;
276              
277             }
278              
279              
280             1;
281