ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-XMPP2/lib/Net/XMPP2/SimpleConnection.pm
Revision: 1.4
Committed: Sat Apr 21 13:51:39 2007 UTC (19 years, 6 months ago) by elmex
Branch: MAIN
Changes since 1.3: +3 -3 lines
Log Message:
implemented the (hopefully) last bits of important error reporting.
Net::XMPP was released at the beginngin of this month - damn, i was
too slow. But i've taken a look at their code. 17000 lines of code
vs. my ~4500 LoC.
Also their Java-API is really a mess...

File Contents

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