ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-XMPP2/lib/Net/XMPP2/SimpleConnection.pm
Revision: 1.9
Committed: Thu Jul 5 19:27:35 2007 UTC (19 years, 2 months ago) by elmex
Branch: MAIN
Changes since 1.8: +1 -1 lines
Log Message:
fixed some typos and such

File Contents

# User Rev Content
1 elmex 1.1 package Net::XMPP2::SimpleConnection;
2 elmex 1.7 use strict;
3    
4 elmex 1.1 use IO::Socket::INET;
5     use Errno;
6     use Fcntl;
7     use Encode;
8     use Net::SSLeay;
9 elmex 1.5 use IO::Handle;
10 elmex 1.1
11     BEGIN {
12     Net::SSLeay::load_error_strings ();
13     Net::SSLeay::SSLeay_add_ssl_algorithms ();
14     Net::SSLeay::randomize ();
15     }
16    
17     =head1 NAME
18    
19 elmex 1.9 Net::XMPP2::SimpleConnection - Low level TCP/TLS connection
20 elmex 1.1
21     =head1 SYNOPSIS
22    
23     package foo;
24     use Net::XMPP2::SimpleConnection;
25    
26     our @ISA = qw/Net::XMPP2::SimpleConnection/;
27    
28     =head1 DESCRIPTION
29    
30     This module only implements the basic low level socket and SSL handling stuff.
31 elmex 1.8 It is used by L<Net::XMPP2::Connection> and you shouldn't mess with it :-)
32    
33     (NOTE: This is the part of Net::XMPP2 which I feel least confident about :-)
34 elmex 1.1
35     =cut
36    
37     sub new {
38     my $this = shift;
39     my $class = ref($this) || $this;
40     my $self = { disconnect_cb => sub {}, @_ };
41     bless $self, $class;
42     return $self;
43     }
44    
45     sub set_block {
46     my ($self) = @_;
47     my $flags = 0;
48     fcntl($self->{socket}, F_GETFL, $flags)
49     or die "Couldn't get flags for HANDLE : $!\n";
50     $flags &= ~O_NONBLOCK;
51     fcntl($self->{socket}, F_SETFL, $flags)
52     or die "Couldn't set flags for HANDLE: $!\n";
53     }
54    
55     sub set_noblock {
56     my ($self) = @_;
57     my $flags = 0;
58     fcntl($self->{socket}, F_GETFL, $flags)
59     or die "Couldn't get flags for HANDLE : $!\n";
60     $flags |= O_NONBLOCK;
61     fcntl($self->{socket}, F_SETFL, $flags)
62     or die "Couldn't set flags for HANDLE: $!\n";
63     }
64    
65     sub connect {
66     my ($self, $host, $port) = @_;
67    
68     $self->{socket}
69     and return 1;
70    
71     my $sock = IO::Socket::INET->new (
72     PeerAddr => $host,
73     PeerPort => $port,
74     Proto => 'tcp',
75     Blocking => 1
76     );
77     return undef unless $sock;;
78    
79     $self->{socket} = $sock;
80     $self->{host} = $host;
81     $self->{port} = $port;
82    
83     $self->set_noblock;
84    
85 elmex 1.4 binmode $sock, ":raw";
86 elmex 1.1
87     $self->{r} =
88     AnyEvent->io (poll => 'r', fh => $sock, cb => sub {
89     my $l = sysread $sock, my $data, 1024;
90    
91     if ($l) {
92 elmex 1.4 $self->{read_buffer} .= decode_utf8 $data;
93 elmex 1.1 $self->handle_data (\$self->{read_buffer});
94    
95     } else {
96     return if $! == Errno::EAGAIN;
97     if (defined $l) {
98 elmex 1.5 $self->disconnect ("EOF from server '$self->{host}:$self->{port}'");
99 elmex 1.1 return;
100    
101     } else {
102 elmex 1.5 $self->disconnect ("Error while reading from server '$self->{host}:$port': $!");
103 elmex 1.1 return;
104     }
105     }
106     });
107     return 1;
108     }
109    
110     sub end_sockets {
111     my ($self) = @_;
112     delete $self->{r};
113     delete $self->{w};
114     if (delete $self->{ssl_enabled}) {
115     Net::SSLeay::free ($self->{ssl});
116     delete $self->{ssl};
117     Net::SSLeay::CTX_free ($self->{ctx});
118     delete $self->{ctx};
119     }
120 elmex 1.5 close ($self->{socket});
121     delete $self->{socket};
122 elmex 1.1 }
123    
124     sub try_ssl_write {
125     my ($self) = @_;
126    
127 elmex 1.2 my $l = Net::SSLeay::write ($self->{ssl}, $self->{write_buffer});
128 elmex 1.1
129     if ($l <= 0) {
130     if ($l == 0) {
131 elmex 1.5 $self->disconnect ("unexpected EOF from server (ssl) '$self->{host}:$self->{port}'");
132 elmex 1.1 return;
133    
134     } else {
135     my $err2 = Net::SSLeay::get_error $self->{ssl}, $l;
136     if ($err2 == 2 || $err2 == 3) {
137     delete $self->{w};
138     $self->make_ssl_write_watcher ($err2 == 2 ? 'r' : 'w');
139     return;
140     }
141    
142     if ($! != Errno::EAGAIN
143     or my $err = Net::SSLeay::ERR_get_error) {
144    
145 elmex 1.5 $self->disconnect (
146 elmex 1.1 sprintf (
147     "Error while writing from server '$self->{host}:$self->{port}': (%d|%s|%s)",
148     $err2, (Net::SSLeay::ERR_error_string $err), "$!")
149     );
150     return;
151     }
152     }
153     } else {
154 elmex 1.2 $self->debug_wrote_data (substr $self->{write_buffer}, 0, $l);
155     $self->{write_buffer} = substr $self->{write_buffer}, $l;
156     if (length ($self->{write_buffer}) <= 0) {
157     delete $self->{w};
158     }
159 elmex 1.1 }
160     }
161    
162     sub try_ssl_read {
163     my ($self) = @_;
164 elmex 1.2 my $r = Net::SSLeay::read ($self->{ssl});
165    
166     if (defined $r) {
167 elmex 1.7 if ($r eq '') {
168     if (my $err = Net::SSLeay::ERR_get_error) {
169     $self->disconnect (
170     sprintf (
171     "Error while reading from server '$self->{host}:$self->{port}':"
172     ."(%s|%s)",
173     (Net::SSLeay::ERR_error_string $err), "$!")
174     );
175     return;
176     }
177     # is this right? $r = '' => EOF? sucky Net::SSLeay... arg...
178     $self->disconnect ("EOF from server '$self->{host}:$self->{port}'.");
179     return;
180     }
181    
182 elmex 1.2 $self->{read_buffer} .= decode_utf8 ($r);
183     $self->handle_data (\$self->{read_buffer});
184     } else {
185 elmex 1.7 my $err2 = Net::SSLeay::get_error $self->{ssl}, $r;
186 elmex 1.2 if ($err2 == 2 || $err2 == 3) {
187 elmex 1.6 #d# warn "READ RETRY $err2\n";
188 elmex 1.2 delete $self->{r};
189     $self->make_ssl_read_watcher ($err2 == 2 ? 'r' : 'w');
190     return;
191     }
192    
193     if ($! != Errno::EAGAIN
194     or my $err = Net::SSLeay::ERR_get_error) {
195 elmex 1.1
196 elmex 1.5 $self->disconnect (
197 elmex 1.2 sprintf (
198     "Error while reading from server '$self->{host}:$self->{port}':"
199     ."(%d|%s|%s)",
200     $err2, (Net::SSLeay::ERR_error_string $err), "$!")
201     );
202 elmex 1.1 return;
203     }
204     }
205     }
206    
207     sub write_data {
208     my ($self, $data) = @_;
209     #return unless $self->{r};
210    
211     my $cl = $self->{socket};
212 elmex 1.4 $self->{write_buffer} .= encode_utf8 ($data);
213 elmex 1.1
214     unless ($self->{w}) {
215 elmex 1.2 $self->{w} =
216     AnyEvent->io (poll => 'w', fh => $cl, cb => sub {
217     if (not $self->{ssl_enabled}) {
218 elmex 1.1 if (my $data = $self->{write_buffer}) {
219     my $len = syswrite $cl, $data;
220     unless ($len) {
221     return if $! == Errno::EAGAIN;
222     if (not defined $len) {
223     warn "error when writing data on $self->{host}:$self->{port}: $!";
224     return;
225     } else {
226     delete $self->{w};
227     }
228     }
229    
230     if ($len == length $self->{write_buffer}) {
231     delete $self->{w};
232     }
233    
234     $self->debug_wrote_data (substr $self->{write_buffer}, 0, $len);
235     $self->{write_buffer} = substr $self->{write_buffer}, $len;
236     }
237 elmex 1.2 } else {
238     $self->try_ssl_write;
239     }
240     });
241     }
242     }
243    
244     sub make_ssl_read_watcher {
245     my ($self, $poll) = @_;
246     return if $self->{r};
247 elmex 1.1
248 elmex 1.2 $poll ||= 'r';
249     $self->{r} =
250     AnyEvent->io (poll => $poll, fh => $self->{socket}, cb => sub {
251     $self->try_ssl_read;
252     });
253     }
254    
255     sub make_ssl_write_watcher {
256     my ($self, $poll) = @_;
257     return if $self->{w};
258    
259     $poll ||= 'w';
260     $self->{w} =
261     AnyEvent->io (poll => $poll, fh => $self->{socket}, cb => sub {
262     $self->try_ssl_write;
263     });
264 elmex 1.1 }
265    
266     sub enable_ssl {
267     my ($self) = @_;
268    
269     $Net::SSLeay::ssl_version = 10; # Insist on TLSv1
270    
271     $self->{ssl_enabled} = 1;
272    
273 elmex 1.3 #d# warn "START TLS!\n";
274 elmex 1.1
275     $self->{r} = undef;
276     $self->{w} = undef;
277    
278     $self->{ctx} = Net::SSLeay::CTX_new ();
279 elmex 1.3
280 elmex 1.2 # enable SSL_MODE_ENABLE_PARTIAL_WRITE and SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
281     Net::SSLeay::CTX_set_mode($self->{ctx}, 1 | 2);
282 elmex 1.3
283 elmex 1.1 $self->{ssl} = Net::SSLeay::new ($self->{ctx});
284    
285     Net::SSLeay::set_fd ($self->{ssl}, fileno $self->{socket});
286     #d# warn "CONNECT\n";
287     Net::SSLeay::connect $self->{ssl};
288     #d# warn "CONNECT END\n";
289     binmode $self->{socket}, ":bytes";
290    
291     $self->{ssl_read_data} = "";
292     $self->make_ssl_read_watcher;
293     }
294    
295 elmex 1.5 sub disconnect {
296     my ($self, $msg) = @_;
297     $self->end_sockets;
298     $self->{disconnect_cb}->($self->{host}, $self->{port}, $msg);
299     }
300    
301 elmex 1.1 1;