# outgoing connection to another server for the sole purpose of verifying a dialback result. package DJabberd::Connection::ServerOut; use strict; use base 'DJabberd::Connection'; use fields ( 'state', 'queue', # our DJabberd::Queue::ServerOut ); use IO::Handle; use Socket qw(PF_INET IPPROTO_TCP SOCK_STREAM); use Carp qw(croak); sub new { my ($class, %opts) = @_; my $ip = delete $opts{ip}; my $endpt = delete $opts{endpoint}; my $queue = delete $opts{queue} or croak "no queue"; die "unknown options" if %opts; croak "No 'ip' or 'endpoint'\n" unless $ip || $endpt; $endpt ||= DJabberd::IPEndpoint->new($ip, 5269); my $sock; socket $sock, PF_INET, SOCK_STREAM, IPPROTO_TCP; unless ($sock && defined fileno($sock)) { $queue->on_connection_failed("Cannot alloc socket"); return; } IO::Handle::blocking($sock, 0); $ip = $endpt->addr; connect $sock, Socket::sockaddr_in($endpt->port, Socket::inet_aton($ip)); $DJabberd::Stats::counter{connect}++; my $self = $class->SUPER::new($sock, $queue->vhost->server); $self->log->debug("Connecting to '$ip' for '$queue->{domain}'"); $self->{state} = "connecting"; $self->{queue} = $queue; $self->{vhost} = $queue->vhost; Scalar::Util::weaken($self->{queue}); return $self; } sub namespace { return "jabber:server"; } sub start_connecting { my $self = shift; $self->watch_write(1); } sub on_connected { my $self = shift; $self->start_init_stream(extra_attr => "xmlns:db='jabber:server:dialback'", to => $self->{queue}->{domain}); $self->watch_read(1); } sub event_write { my $self = shift; if ($self->{state} eq "connecting") { $self->{state} = "connected"; $self->on_connected; } else { return $self->SUPER::event_write; } } sub on_stream_start { my ($self, $ss) = @_; $self->{in_stream} = 1; $self->log->debug("We got a stream back from connection $self->{id}!\n"); unless ($ss->announced_dialback) { $self->log->warn("Connection $self->{id} doesn't support dialbacl, failing"); $self->{queue}->on_connection_failed($self, "no dialback"); return; } $self->log->debug("Connection $self->{id} supports dialback"); if ($ss->version->supports_features) { # they can eat a dick for all we care. they get no features. # what is this weird XMPP 1.0 + old-school Dialback world anyway? # maybe we're still confused. FIXME: care. my $features = ""; $self->write($features); $self->log->debug("$self->{id} sending '$features'"); } my $vhost = $self->{queue}->vhost; my $orig_server = $vhost->name; my $recv_server = $self->{queue}->domain; my $db_params = DJabberd::DialbackParams->new( id => $ss->id, recv => $recv_server, orig => $orig_server, vhost => $vhost, ); $db_params->generate_dialback_result(sub { my $res = shift; $self->log->debug("$self->{id} sending res '$res'"); $self->write(qq{$res}); }); } sub on_stanza_received { my ($self, $node) = @_; if ($self->xmllog->is_info) { $self->log_incoming_data($node); } # we only deal with dialback verifies here. kinda ghetto # don't make a Stanza::DialbackVerify, maybe we should. unless ($node->element eq "{jabber:server:dialback}result") { return $self->SUPER::process_incoming_stanza_from_s2s_out($node); } unless ($node->attr("{}type") eq "valid") { # FIXME: also verify other attributes warn "Not valid?\n"; return; } $self->log->debug("Connection $self->{id} established"); $self->{queue}->on_connection_connected($self); } sub event_err { my $self = shift; $self->{queue}->on_connection_error($self); return $self->SUPER::event_err; } sub event_hup { my $self = shift; return $self->event_err; } sub close { my $self = shift; return if $self->{closed}; $self->{queue}->on_connection_error($self); return $self->SUPER::close; } 1;