ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-XMPP2/lib/Net/XMPP2/SimpleConnection.pm
Revision: 1.6
Committed: Mon Jun 25 07:56:52 2007 UTC (19 years, 3 months ago) by elmex
Branch: MAIN
Changes since 1.5: +1 -1 lines
Log Message:
sime fixes and changes

File Contents

# User Rev Content
1 elmex 1.1 package Net::XMPP2::SimpleConnection;
2     use IO::Socket::INET;
3     use Errno;
4     use Fcntl;
5     use Encode;
6     use Net::SSLeay;
7 elmex 1.5 use IO::Handle;
8 elmex 1.1
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 elmex 1.4 binmode $sock, ":raw";
82 elmex 1.1
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 elmex 1.4 $self->{read_buffer} .= decode_utf8 $data;
89 elmex 1.1 $self->handle_data (\$self->{read_buffer});
90    
91     } else {
92     return if $! == Errno::EAGAIN;
93     if (defined $l) {
94 elmex 1.5 $self->disconnect ("EOF from server '$self->{host}:$self->{port}'");
95 elmex 1.1 return;
96    
97     } else {
98 elmex 1.5 $self->disconnect ("Error while reading from server '$self->{host}:$port': $!");
99 elmex 1.1 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 elmex 1.5 close ($self->{socket});
117     delete $self->{socket};
118 elmex 1.1 }
119    
120     sub try_ssl_write {
121     my ($self) = @_;
122    
123 elmex 1.2 my $l = Net::SSLeay::write ($self->{ssl}, $self->{write_buffer});
124 elmex 1.1
125     if ($l <= 0) {
126     if ($l == 0) {
127 elmex 1.5 $self->disconnect ("unexpected EOF from server (ssl) '$self->{host}:$self->{port}'");
128 elmex 1.1 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 elmex 1.5 $self->disconnect (
142 elmex 1.1 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 elmex 1.2 $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 elmex 1.1 }
156     }
157    
158     sub try_ssl_read {
159     my ($self) = @_;
160 elmex 1.2 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 elmex 1.6 #d# warn "READ RETRY $err2\n";
169 elmex 1.2 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 elmex 1.1
177 elmex 1.5 $self->disconnect (
178 elmex 1.2 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 elmex 1.1 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 elmex 1.4 $self->{write_buffer} .= encode_utf8 ($data);
194 elmex 1.1
195     unless ($self->{w}) {
196 elmex 1.2 $self->{w} =
197     AnyEvent->io (poll => 'w', fh => $cl, cb => sub {
198     if (not $self->{ssl_enabled}) {
199 elmex 1.1 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 elmex 1.2 } 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 elmex 1.1
229 elmex 1.2 $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 elmex 1.1 }
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 elmex 1.3 #d# warn "START TLS!\n";
255 elmex 1.1
256     $self->{r} = undef;
257     $self->{w} = undef;
258    
259     $self->{ctx} = Net::SSLeay::CTX_new ();
260 elmex 1.3
261 elmex 1.2 # enable SSL_MODE_ENABLE_PARTIAL_WRITE and SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
262     Net::SSLeay::CTX_set_mode($self->{ctx}, 1 | 2);
263 elmex 1.3
264 elmex 1.1 $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 elmex 1.5 sub disconnect {
277     my ($self, $msg) = @_;
278     $self->end_sockets;
279     $self->{disconnect_cb}->($self->{host}, $self->{port}, $msg);
280     }
281    
282 elmex 1.1 1;