ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-XMPP2/lib/Net/XMPP2/SimpleConnection.pm
Revision: 1.7
Committed: Mon Jul 2 13:15:00 2007 UTC (19 years, 4 months ago) by elmex
Branch: MAIN
Changes since 1.6: +18 -1 lines
Log Message:
added some error handling for EOF ... err... disconnects from SSL

File Contents

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