ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-XMPP2/lib/Net/XMPP2/SimpleConnection.pm
Revision: 1.5
Committed: Sun Jun 24 17:17:43 2007 UTC (19 years, 3 months ago) by elmex
Branch: MAIN
Changes since 1.4: +14 -12 lines
Log Message:
fixed some issues and rounded up the client for usage in Net::Bummskraut

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