ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-XMPP2/lib/Net/XMPP2/SimpleConnection.pm
Revision: 1.11
Committed: Fri Jul 6 15:19:52 2007 UTC (19 years, 2 months ago) by elmex
Branch: MAIN
CVS Tags: HEAD
Changes since 1.10: +0 -2 lines
Log Message:
fixed some bugs in max write length and limit searcher

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