| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # Collection of sockets associated with a Leg: | 
| 2 |  |  |  |  |  |  | # This gets attached to an IO-Loop so that a common callback will be called with | 
| 3 |  |  |  |  |  |  | # (packet,from) which then can be processed by the Leg and Dispatcher. | 
| 4 |  |  |  |  |  |  | # Sending through the SocketPool is done by automatically selecting or creating | 
| 5 |  |  |  |  |  |  | # the appropriate socket based on target and/or packet->tid. | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 43 |  |  | 43 |  | 273 | use strict; | 
|  | 43 |  |  |  |  | 83 |  | 
|  | 43 |  |  |  |  | 1248 |  | 
| 8 | 43 |  |  | 43 |  | 209 | use warnings; | 
|  | 43 |  |  |  |  | 86 |  | 
|  | 43 |  |  |  |  | 1709 |  | 
| 9 |  |  |  |  |  |  | package Net::SIP::SocketPool; | 
| 10 | 43 |  |  | 43 |  | 205 | use fields qw(loop ipproto tls dst fds tids cb timeout_timer); | 
|  | 43 |  |  |  |  | 77 |  | 
|  | 43 |  |  |  |  | 275 |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 | 43 |  |  | 43 |  | 4197 | use Net::SIP::Util ':all'; | 
|  | 43 |  |  |  |  | 72 |  | 
|  | 43 |  |  |  |  | 6741 |  | 
| 13 | 43 |  |  | 43 |  | 18847 | use Net::SIP::Packet; | 
|  | 43 |  |  |  |  | 128 |  | 
|  | 43 |  |  |  |  | 1264 |  | 
| 14 | 43 |  |  | 43 |  | 248 | use Net::SIP::Debug; | 
|  | 43 |  |  |  |  | 78 |  | 
|  | 43 |  |  |  |  | 204 |  | 
| 15 | 43 |  |  | 43 |  | 18277 | use Net::SIP::Dispatcher::Eventloop; | 
|  | 43 |  |  |  |  | 88 |  | 
|  | 43 |  |  |  |  | 2102 |  | 
| 16 | 43 |  |  | 43 |  | 255 | use Socket qw(SOL_SOCKET SO_ERROR); | 
|  | 43 |  |  |  |  | 73 |  | 
|  | 43 |  |  |  |  | 189993 |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | # RFC does not specify some fixed limit for the SIP header and body so we have | 
| 19 |  |  |  |  |  |  | # to make up some limits we think are useful. | 
| 20 |  |  |  |  |  |  | my $MAX_SIP_HEADER = 2**14;   # 16k header | 
| 21 |  |  |  |  |  |  | my $MAX_SIP_BODY   = 2**16;   # 64k body | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | # how many requests we can associate with a socket at the same time | 
| 24 |  |  |  |  |  |  | my $MAX_TIDLIST = 30; | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | my $MIN_EXPIRE = 15;      # wait at least this time before closing on inactivity | 
| 27 |  |  |  |  |  |  | my $MAX_EXPIRE = 120;     # wait at most this time | 
| 28 |  |  |  |  |  |  | my $CONNECT_TIMEOUT = 10; # max time for TCP connect | 
| 29 |  |  |  |  |  |  | my $TCP_READSIZE = 2**16; # size of TCP read | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | sub import { | 
| 32 | 43 |  |  | 43 |  | 252 | my %m = ( | 
| 33 |  |  |  |  |  |  | MAX_SIP_HEADER  => \$MAX_SIP_HEADER, | 
| 34 |  |  |  |  |  |  | MAX_SIP_BODY    => \$MAX_SIP_BODY, | 
| 35 |  |  |  |  |  |  | MAX_TIDLIST     => \$MAX_TIDLIST, | 
| 36 |  |  |  |  |  |  | MIN_EXPIRE      => \$MIN_EXPIRE, | 
| 37 |  |  |  |  |  |  | MAX_EXPIRE      => \$MAX_EXPIRE, | 
| 38 |  |  |  |  |  |  | CONNECT_TIMEOUT => \$CONNECT_TIMEOUT, | 
| 39 |  |  |  |  |  |  | TCP_READSIZE    => \$TCP_READSIZE, | 
| 40 |  |  |  |  |  |  | ); | 
| 41 | 43 |  |  |  |  | 1433 | for(my $i=1;$i<@_;$i+=2) { | 
| 42 | 0 | 0 |  |  |  | 0 | my $ref = $m{$_[$i]} or die "no such config key '$_[$i]'"; | 
| 43 | 0 |  |  |  |  | 0 | $$ref = $_[$i+1]; | 
| 44 |  |  |  |  |  |  | } | 
| 45 |  |  |  |  |  |  | } | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | my %TLSClientDefault = (SSL_verifycn_scheme => 'sip'); | 
| 48 |  |  |  |  |  |  | my %TLSServerDefault = (); | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | # will be defined on first use of SSL depending if IO::Socket::SSL is available | 
| 51 |  |  |  |  |  |  | my $CAN_TLS; | 
| 52 |  |  |  |  |  |  | my $SSL_REUSE_CTX; | 
| 53 |  |  |  |  |  |  | my ($SSL_WANT_READ, $SSL_WANT_WRITE, $SSL_VERIFY_PEER, | 
| 54 |  |  |  |  |  |  | $SSL_VERIFY_FAIL_IF_NO_PEER_CERT); | 
| 55 |  |  |  |  |  |  | our $SSL_ERROR; | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | ########################################################################### | 
| 59 |  |  |  |  |  |  | # create a new SocketPool | 
| 60 |  |  |  |  |  |  | # Args: ($class,$proto,$fd,$peer,$connected,$tls) | 
| 61 |  |  |  |  |  |  | #  $proto: udp|tcp|tls | 
| 62 |  |  |  |  |  |  | #  $fd: the file descriptor for the master socket (i.e. listener) | 
| 63 |  |  |  |  |  |  | #  $peer: optional hash with addr,port,family of destination if restricted | 
| 64 |  |  |  |  |  |  | #  $connected: true if $fd is connected to $peer (useful with UDP only) | 
| 65 |  |  |  |  |  |  | #  $tls: \%options for IO::Socket::SSL when proto is tls | 
| 66 |  |  |  |  |  |  | # Returns: $self | 
| 67 |  |  |  |  |  |  | ########################################################################### | 
| 68 |  |  |  |  |  |  | sub new { | 
| 69 | 143 |  |  | 143 | 1 | 605 | my ($class,$proto,$fd,$peer,$connected,$tls) = @_; | 
| 70 | 143 |  |  |  |  | 490 | my $self = fields::new($class); | 
| 71 | 143 | 100 |  |  |  | 26521 | if ($proto eq 'tls') { | 
| 72 |  |  |  |  |  |  | # the underlying proto is still TCP and we remember to use TLS by | 
| 73 |  |  |  |  |  |  | # having a true self.tls | 
| 74 | 38 |  |  |  |  | 355 | $self->{ipproto} = 'tcp'; | 
| 75 | 38 |  | 50 | 13 |  | 5091 | $CAN_TLS //= eval "use IO::Socket::SSL;1" && eval { | 
|  | 13 |  | 66 |  |  | 437 |  | 
|  | 13 |  |  |  |  | 344 |  | 
|  | 13 |  |  |  |  | 538 |  | 
| 76 |  |  |  |  |  |  | # 1.956 defines the 'sip' scheme for hostname validation | 
| 77 |  |  |  |  |  |  | IO::Socket::SSL->VERSION >= 1.956 | 
| 78 |  |  |  |  |  |  | or die "need at least version 1.956"; | 
| 79 |  |  |  |  |  |  | $SSL_WANT_READ  = IO::Socket::SSL::SSL_WANT_READ(); | 
| 80 |  |  |  |  |  |  | $SSL_WANT_WRITE = IO::Socket::SSL::SSL_WANT_WRITE(); | 
| 81 |  |  |  |  |  |  | $SSL_VERIFY_PEER = IO::Socket::SSL::SSL_VERIFY_PEER(); | 
| 82 |  |  |  |  |  |  | $SSL_VERIFY_FAIL_IF_NO_PEER_CERT = | 
| 83 |  |  |  |  |  |  | IO::Socket::SSL::SSL_VERIFY_FAIL_IF_NO_PEER_CERT(); | 
| 84 |  |  |  |  |  |  | *SSL_ERROR = \$IO::Socket::SSL::SSL_ERROR; | 
| 85 |  |  |  |  |  |  | # 1.969 fixed name validation when reusing context | 
| 86 |  |  |  |  |  |  | $SSL_REUSE_CTX = IO::Socket::SSL->VERSION >= 1.969; | 
| 87 |  |  |  |  |  |  | 1; | 
| 88 |  |  |  |  |  |  | } || die "no SSL support using IO::Socket::SSL: $@"; | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | # create different contexts for [m]aster and [c]lient | 
| 91 | 38 |  | 50 |  |  | 271 | $tls ||= {}; | 
| 92 | 38 |  |  |  |  | 470 | my $verify_client = delete $tls->{verify_client}; | 
| 93 | 38 |  |  |  |  | 595 | $self->{tls}{c} = { %TLSClientDefault, %$tls }; | 
| 94 |  |  |  |  |  |  | $self->{tls}{m} = { | 
| 95 | 38 | 0 |  |  |  | 801 | %TLSServerDefault, | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | %$tls, | 
| 97 |  |  |  |  |  |  | SSL_server => 1, | 
| 98 |  |  |  |  |  |  | # request client certificate? | 
| 99 |  |  |  |  |  |  | ! $verify_client ? (): | 
| 100 |  |  |  |  |  |  | $verify_client == -1 ? (SSL_verify_mode => $SSL_VERIFY_PEER) : | 
| 101 |  |  |  |  |  |  | $verify_client ==  1 ? (SSL_verify_mode => | 
| 102 |  |  |  |  |  |  | $SSL_VERIFY_PEER|$SSL_VERIFY_FAIL_IF_NO_PEER_CERT) : | 
| 103 |  |  |  |  |  |  | die "invalid setting for SSL_verify_client: $verify_client" | 
| 104 |  |  |  |  |  |  | }; | 
| 105 | 38 | 50 |  |  |  | 225 | if ($SSL_REUSE_CTX) { | 
| 106 | 38 |  |  |  |  | 135 | for(qw(m c)) { | 
| 107 | 76 | 50 |  |  |  | 294 | $self->{tls}{$_}{SSL_reuse_ctx} and next; | 
| 108 | 76 |  | 50 |  |  | 662 | my $ctx = IO::Socket::SSL::SSL_Context->new($self->{tls}{$_}) | 
| 109 |  |  |  |  |  |  | || die "failed to create SSL context: $SSL_ERROR"; | 
| 110 | 76 |  |  |  |  | 97039 | $self->{tls}{$_}{SSL_reuse_ctx} = $ctx; | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  | } else { | 
| 114 | 105 |  | 50 |  |  | 531 | $self->{ipproto} = $proto || die "no protocol given"; | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 143 |  |  |  |  | 383 | $self->{fds}   = {}; | 
| 118 | 143 |  |  |  |  | 326 | $self->{tids}  = {}; | 
| 119 | 143 | 50 |  |  |  | 662 | if (!$connected) { | 
| 120 | 143 |  |  |  |  | 276 | $self->{dst} = $peer; | 
| 121 | 143 |  |  |  |  | 358 | $peer = undef; | 
| 122 |  |  |  |  |  |  | } | 
| 123 | 143 | 50 |  |  |  | 2289 | _add_socket($self,{ | 
| 124 |  |  |  |  |  |  | fd => $fd, | 
| 125 |  |  |  |  |  |  | $peer ? (peer => $peer) : (), | 
| 126 |  |  |  |  |  |  | master => 1, | 
| 127 |  |  |  |  |  |  | }); | 
| 128 | 143 |  |  |  |  | 1168 | return $self; | 
| 129 |  |  |  |  |  |  | } | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | sub DESTROY { | 
| 132 | 134 |  |  | 134 |  | 29912124 | my Net::SIP::SocketPool $self = shift; | 
| 133 |  |  |  |  |  |  | # detach from current loop | 
| 134 | 134 | 50 |  |  |  | 11373 | if ($self->{loop}) { | 
| 135 | 0 |  |  |  |  | 0 | for(values %{$self->{fds}}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 136 | 0 |  | 0 |  |  | 0 | $self->{loop}->delFD($_->{fd} || next); | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  | } | 
| 139 |  |  |  |  |  |  | } | 
| 140 |  |  |  |  |  |  |  | 
| 141 |  |  |  |  |  |  | ########################################################################### | 
| 142 |  |  |  |  |  |  | # attaches SocketPool to EventLoop | 
| 143 |  |  |  |  |  |  | # Args: ($self,$loop,$callback) | 
| 144 |  |  |  |  |  |  | #  $loop: Net::SIP::Dispatcher::Eventloop or API compatible | 
| 145 |  |  |  |  |  |  | #  $callback: should be called for each new SIP packet received | 
| 146 |  |  |  |  |  |  | # Comment: | 
| 147 |  |  |  |  |  |  | #  If $loop is empty it just detaches from the current loop | 
| 148 |  |  |  |  |  |  | ########################################################################### | 
| 149 |  |  |  |  |  |  | sub attach_eventloop { | 
| 150 | 112 |  |  | 112 | 1 | 282 | my Net::SIP::SocketPool $self = shift; | 
| 151 | 112 |  |  |  |  | 268 | my ($loop,$cb) = @_; | 
| 152 | 112 | 100 |  |  |  | 378 | if ($self->{loop}) { | 
| 153 | 53 |  |  |  |  | 104 | for(values %{$self->{fds}}) { | 
|  | 53 |  |  |  |  | 192 |  | 
| 154 | 76 |  |  |  |  | 332 | $self->{loop}->delFD($_->{fd}); | 
| 155 |  |  |  |  |  |  | } | 
| 156 | 53 | 100 |  |  |  | 200 | if ($self->{timeout_timer}) { | 
| 157 | 25 |  |  |  |  | 103 | $self->{timeout_timer}->cancel; | 
| 158 | 25 |  |  |  |  | 56 | undef $self->{timeout_timer}; | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  | } | 
| 161 | 112 | 100 |  |  |  | 1221 | if ($self->{loop} = $loop) { | 
| 162 | 59 |  |  |  |  | 138 | $self->{cb} = $cb; | 
| 163 | 59 |  |  |  |  | 106 | _addreader2loop($self,$_) for values %{$self->{fds}}; | 
|  | 59 |  |  |  |  | 1116 |  | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  | } | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | ########################################################################### | 
| 168 |  |  |  |  |  |  | # returns master socket | 
| 169 |  |  |  |  |  |  | # Args: $self | 
| 170 |  |  |  |  |  |  | # Returns: $fd | 
| 171 |  |  |  |  |  |  | #  $fd: master socket | 
| 172 |  |  |  |  |  |  | ########################################################################### | 
| 173 |  |  |  |  |  |  | sub master { | 
| 174 | 0 |  |  | 0 | 1 | 0 | my Net::SIP::SocketPool $self = shift; | 
| 175 | 0 |  |  |  |  | 0 | my @fo = grep { $_->{master} } values %{$self->{fds}}; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 176 | 0 | 0 |  |  |  | 0 | die "no master" if ! @fo; | 
| 177 | 0 | 0 |  |  |  | 0 | die "multiple master" if @fo>1; | 
| 178 | 0 |  |  |  |  | 0 | return $fo[0]{fd}; | 
| 179 |  |  |  |  |  |  | } | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | ########################################################################### | 
| 182 |  |  |  |  |  |  | # send packet via SocketPool | 
| 183 |  |  |  |  |  |  | # Args: ($self,$packet,$dst,$callback) | 
| 184 |  |  |  |  |  |  | #  $packet: Net::SIP::Packet | 
| 185 |  |  |  |  |  |  | #  $dst: where to send as hash with addr,port,family | 
| 186 |  |  |  |  |  |  | #  $callback: callback to call on definite successful delivery (TCP/TLS only) | 
| 187 |  |  |  |  |  |  | #    or on error | 
| 188 |  |  |  |  |  |  | ########################################################################### | 
| 189 |  |  |  |  |  |  | sub sendto { | 
| 190 | 194 |  |  | 194 | 1 | 384 | my Net::SIP::SocketPool $self = shift; | 
| 191 | 194 |  |  |  |  | 423 | my ($packet,$dst,$callback) = @_; | 
| 192 | 194 | 50 |  |  |  | 1025 | if ($self->{dst}) { | 
|  |  | 50 |  |  |  |  |  | 
| 193 | 0 |  |  |  |  | 0 | $dst = $self->{dst}; # override destination | 
| 194 |  |  |  |  |  |  | } elsif (!ref($dst)) { | 
| 195 | 0 |  |  |  |  | 0 | $dst = ip_string2parts($dst); | 
| 196 |  |  |  |  |  |  | } | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | # select all sockets which are connected to the target | 
| 199 |  |  |  |  |  |  | # if we have multiple connected reduce further by packets tid | 
| 200 |  |  |  |  |  |  | # take one socket | 
| 201 |  |  |  |  |  |  |  | 
| 202 | 194 |  |  |  |  | 314 | my $fos = [ values %{$self->{fds}} ]; | 
|  | 194 |  |  |  |  | 881 |  | 
| 203 | 194 | 100 |  |  |  | 1552 | if (@$fos>1) { | 
| 204 | 67 |  |  |  |  | 172 | my $match = 0; | 
| 205 |  |  |  |  |  |  | # any socket associated with tid? | 
| 206 | 67 | 100 | 66 |  |  | 241 | if ($packet->is_response and my $fo = $self->{tids}{$packet->tid}) { | 
| 207 | 37 | 50 |  |  |  | 101 | if (my @s = grep { $_ == $fo } @$fos) { | 
|  | 74 |  |  |  |  | 278 |  | 
| 208 | 37 |  |  |  |  | 74 | $match |= 1; | 
| 209 | 37 |  |  |  |  | 120 | $fos = \@s | 
| 210 |  |  |  |  |  |  | } | 
| 211 |  |  |  |  |  |  | } | 
| 212 | 67 | 100 |  |  |  | 345 | if (@$fos>1) { | 
| 213 |  |  |  |  |  |  | # any socket connected to dst? | 
| 214 | 30 | 50 |  |  |  | 70 | if ( my @s = grep { | 
| 215 |  |  |  |  |  |  | $_->{peer} && | 
| 216 |  |  |  |  |  |  | $_->{peer}{addr} eq $dst->{addr} && | 
| 217 |  |  |  |  |  |  | $_->{peer}{port} == $dst->{port} | 
| 218 | 60 | 100 | 66 |  |  | 649 | } @$fos) { | 
| 219 | 30 |  |  |  |  | 74 | $match |= 2; | 
| 220 | 30 |  |  |  |  | 88 | $fos = \@s; | 
| 221 |  |  |  |  |  |  | } | 
| 222 |  |  |  |  |  |  | } | 
| 223 | 67 | 50 |  |  |  | 209 | if (!$match) { | 
| 224 |  |  |  |  |  |  | # use master | 
| 225 | 0 |  |  |  |  | 0 | $fos = [ grep { $_->{master} } @$fos ]; | 
|  | 0 |  |  |  |  | 0 |  | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  |  | 
| 229 | 194 |  |  |  |  | 449 | my $fo = $fos->[0]; | 
| 230 | 194 |  |  |  |  | 801 | my $data = $packet->as_string; | 
| 231 | 194 | 100 |  |  |  | 1879 | if ($self->{ipproto} eq 'udp') { | 
| 232 | 112 | 50 |  |  |  | 332 | if ($fo->{peer}) { | 
| 233 |  |  |  |  |  |  | # send over connected UDP socket | 
| 234 | 0 |  |  |  |  | 0 | my $rv = send($fo->{fd},$data,0); | 
| 235 | 0 | 0 |  |  |  | 0 | invoke_callback($callback, $!) if ! defined($rv); | 
| 236 | 0 |  |  |  |  | 0 | return; | 
| 237 |  |  |  |  |  |  | } else { | 
| 238 |  |  |  |  |  |  | # sendto over unconnected UDP socket | 
| 239 | 112 |  |  |  |  | 625 | my $rv = send($fo->{fd},$data,0, ip_parts2sockaddr($dst)); | 
| 240 | 112 | 50 |  |  |  | 617 | invoke_callback($callback, $!) if ! defined($rv); | 
| 241 | 112 |  |  |  |  | 602 | return; | 
| 242 |  |  |  |  |  |  | } | 
| 243 |  |  |  |  |  |  | } | 
| 244 |  |  |  |  |  |  |  | 
| 245 | 82 | 50 |  |  |  | 464 | if ($self->{ipproto} eq 'tcp') { | 
| 246 | 82 | 100 |  |  |  | 352 | if ($fo->{peer}) { | 
| 247 |  |  |  |  |  |  | $DEBUG && DEBUG(40,"send tcp data to %s via %s", | 
| 248 |  |  |  |  |  |  | ip_parts2string($dst), | 
| 249 | 67 | 50 |  |  |  | 171 | ip_parts2string($fo->{peer})); | 
| 250 |  |  |  |  |  |  | # send over this connected socket | 
| 251 | 67 |  |  |  |  | 305 | $fo->{wbuf} .= $data; | 
| 252 | 67 | 50 |  |  |  | 490 | _tcp_send($self,$fo,$callback) if ! $fo->{inside_connect}; | 
| 253 | 67 |  |  |  |  | 295 | return; | 
| 254 |  |  |  |  |  |  | } | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | # TCP listener: we need to create a new connected socket first | 
| 257 | 15 | 50 |  |  |  | 131 | $DEBUG && DEBUG(40,"need new tcp socket to %s", | 
| 258 |  |  |  |  |  |  | ip_parts2string($dst)); | 
| 259 |  |  |  |  |  |  | my $clfd = INETSOCK( | 
| 260 |  |  |  |  |  |  | Proto => 'tcp', | 
| 261 |  |  |  |  |  |  | Reuse => 1, ReuseAddr => 1, | 
| 262 | 15 |  |  |  |  | 335 | LocalAddr => (ip_sockaddr2parts(getsockname($fo->{fd})))[0], | 
| 263 |  |  |  |  |  |  | Blocking => 0, | 
| 264 |  |  |  |  |  |  | ); | 
| 265 |  |  |  |  |  |  | $fo = $self->_add_socket({ | 
| 266 |  |  |  |  |  |  | fd => $clfd, | 
| 267 |  |  |  |  |  |  | peer => $dst, | 
| 268 |  |  |  |  |  |  | rbuf => '', | 
| 269 |  |  |  |  |  |  | wbuf => $data, | 
| 270 |  |  |  |  |  |  | didit => $self->{loop}->looptime, | 
| 271 | 15 |  |  |  |  | 8810 | inside_connect => 1, | 
| 272 |  |  |  |  |  |  | }); | 
| 273 | 15 |  |  |  |  | 70 | _tcp_connect($self,$fo,ip_parts2sockaddr($dst),$callback); | 
| 274 | 15 |  |  |  |  | 76 | return; | 
| 275 |  |  |  |  |  |  | } | 
| 276 |  |  |  |  |  |  |  | 
| 277 | 0 |  |  |  |  | 0 | die "unknown type $self->{ipproto}"; | 
| 278 |  |  |  |  |  |  | } | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | sub _add_socket { | 
| 282 | 168 |  |  | 168 |  | 406 | my Net::SIP::SocketPool $self = shift; | 
| 283 | 168 |  |  |  |  | 275 | my $fo = shift; | 
| 284 | 168 |  |  |  |  | 2199 | $fo->{fd}->blocking(0); | 
| 285 | 168 |  |  |  |  | 4695 | $self->{fds}{ fileno($fo->{fd}) } = $fo; | 
| 286 | 168 | 100 | 100 |  |  | 1152 | _addreader2loop($self,$fo) if $self->{loop} && ! $fo->{inside_connect}; | 
| 287 | 168 | 100 | 66 |  |  | 1460 | $self->_timeout_sockets if ! $self->{timeout_timer} && $fo->{didit}; | 
| 288 | 168 |  |  |  |  | 831 | return $fo; | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | sub _del_socket { | 
| 292 | 2 |  |  | 2 |  | 6 | my Net::SIP::SocketPool $self = shift; | 
| 293 | 2 |  |  |  |  | 4 | my $fo = shift; | 
| 294 | 2 | 50 |  |  |  | 8 | $self->_error(@_) if @_; | 
| 295 | 2 | 50 |  |  |  | 17 | $self->{loop}->delFD($fo->{fd}) if $self->{loop}; | 
| 296 | 2 |  |  |  |  | 8 | delete $self->{fds}{ fileno($fo->{fd}) }; | 
| 297 | 2 | 50 |  |  |  | 16 | if ($fo->{tids}) { | 
| 298 | 2 |  |  |  |  | 5 | delete $self->{tids}{$_} for @{$fo->{tids}}; | 
|  | 2 |  |  |  |  | 13 |  | 
| 299 |  |  |  |  |  |  | } | 
| 300 | 2 |  |  |  |  | 93 | return; | 
| 301 |  |  |  |  |  |  | } | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | sub _timeout_sockets { | 
| 304 | 30 |  |  | 30 |  | 144 | my Net::SIP::SocketPool $self = shift; | 
| 305 | 30 |  |  |  |  | 127 | my $fds = $self->{fds}; | 
| 306 | 30 | 50 |  |  |  | 198 | goto disable_timer if keys(%$fds) <= 1; | 
| 307 | 30 | 50 |  |  |  | 216 | return if ! $self->{loop}; | 
| 308 |  |  |  |  |  |  |  | 
| 309 | 30 |  |  |  |  | 249 | DEBUG(99,"timeout sockets"); | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | # the more sockets we have open the faster expire | 
| 312 | 30 |  |  |  |  | 228 | my $expire = $MIN_EXPIRE + ($MAX_EXPIRE - $MIN_EXPIRE)/(keys(%$fds)-1); | 
| 313 | 30 |  |  |  |  | 113 | my ($time,$need_timer); | 
| 314 | 30 |  |  |  |  | 213 | for (values %$fds) { | 
| 315 | 60 |  | 100 |  |  | 753 | my $tdiff = -($_->{didit} || next) + ($time||= $self->{loop}->looptime); | 
|  |  |  | 33 |  |  |  |  | 
| 316 | 30 | 50 | 66 |  |  | 768 | if ($tdiff>$expire) { | 
|  |  | 50 |  |  |  |  |  | 
| 317 | 0 |  |  |  |  | 0 | $self->_del_socket($_); | 
| 318 |  |  |  |  |  |  | } elsif ($_->{inside_connect} && $tdiff > $CONNECT_TIMEOUT) { | 
| 319 | 0 |  |  |  |  | 0 | $self->_del_socket($_,"connect timed out"); | 
| 320 |  |  |  |  |  |  | } else { | 
| 321 | 30 |  |  |  |  | 121 | $need_timer = 1; | 
| 322 |  |  |  |  |  |  | } | 
| 323 |  |  |  |  |  |  | } | 
| 324 | 30 | 50 |  |  |  | 141 | if ($need_timer) { | 
| 325 | 30 | 100 |  |  |  | 488 | return if $self->{timeout_timer}; | 
| 326 | 25 |  |  |  |  | 133 | DEBUG(99,"timeout sockets - need timer"); | 
| 327 |  |  |  |  |  |  | $self->{timeout_timer} = $self->{loop}->add_timer( | 
| 328 | 25 |  |  |  |  | 255 | int($MIN_EXPIRE/2)+1, | 
| 329 |  |  |  |  |  |  | [ \&_timeout_sockets, $self ], | 
| 330 |  |  |  |  |  |  | int($MIN_EXPIRE/2)+1, | 
| 331 |  |  |  |  |  |  | 'socketpool-timeout' | 
| 332 |  |  |  |  |  |  | ); | 
| 333 | 25 |  |  |  |  | 88 | return; | 
| 334 |  |  |  |  |  |  | } | 
| 335 |  |  |  |  |  |  | disable_timer: | 
| 336 | 0 |  |  |  |  | 0 | DEBUG(99,"timer cancel"); | 
| 337 | 0 |  | 0 |  |  | 0 | ($self->{timeout_timer} || return)->cancel; | 
| 338 | 0 |  |  |  |  | 0 | undef $self->{timeout_timer}; | 
| 339 |  |  |  |  |  |  | } | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  | sub _error { | 
| 342 | 0 |  |  | 0 |  | 0 | my Net::SIP::SocketPool $self = shift; | 
| 343 | 0 |  |  |  |  | 0 | my $msg = shift; | 
| 344 | 0 | 0 |  |  |  | 0 | $msg = sprintf($msg,@_) if @_; | 
| 345 | 0 |  |  |  |  | 0 | DEBUG(1,$msg); | 
| 346 | 0 |  |  |  |  | 0 | return; | 
| 347 |  |  |  |  |  |  | } | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | { | 
| 350 |  |  |  |  |  |  | my %type2cb = ( | 
| 351 |  |  |  |  |  |  | # unconnected UDP socket: receive and send | 
| 352 |  |  |  |  |  |  | udp_m  => sub { | 
| 353 |  |  |  |  |  |  | my Net::SIP::SocketPool $self = shift; | 
| 354 |  |  |  |  |  |  | return $self->{dst} | 
| 355 |  |  |  |  |  |  | ? sub { _handle_read_udp(@_,1) } | 
| 356 |  |  |  |  |  |  | : sub { _handle_read_udp(@_) } | 
| 357 |  |  |  |  |  |  | }, | 
| 358 |  |  |  |  |  |  | # connected UDP socket: receive and send with fixed peer | 
| 359 |  |  |  |  |  |  | udp_co => sub { | 
| 360 |  |  |  |  |  |  | return \&_handle_read_udp | 
| 361 |  |  |  |  |  |  | }, | 
| 362 |  |  |  |  |  |  | # unconnected TCP socket: listen, accept and create tcp_co | 
| 363 |  |  |  |  |  |  | tcp_m  => sub { | 
| 364 |  |  |  |  |  |  | return \&_handle_read_tcp_m | 
| 365 |  |  |  |  |  |  | }, | 
| 366 |  |  |  |  |  |  | # connected TCP socket: receive and send with fixed peer | 
| 367 |  |  |  |  |  |  | tcp_co => sub { | 
| 368 |  |  |  |  |  |  | my (undef,$fd) = @_; | 
| 369 |  |  |  |  |  |  | my $from = getpeername($fd); | 
| 370 |  |  |  |  |  |  | return sub { _handle_read_tcp_co(@_,$from) } | 
| 371 |  |  |  |  |  |  | } | 
| 372 |  |  |  |  |  |  | ); | 
| 373 |  |  |  |  |  |  | sub _addreader2loop { | 
| 374 | 84 |  |  | 84 |  | 206 | my Net::SIP::SocketPool $self = shift; | 
| 375 | 84 |  |  |  |  | 147 | my $fo = shift; | 
| 376 |  |  |  |  |  |  | # proto_co: connected socket, proto_m: (unconnected) master socket | 
| 377 | 84 | 100 |  |  |  | 452 | my $type = $self->{ipproto} . ($fo->{peer} ? '_co':'_m'); | 
| 378 |  |  |  |  |  |  | $self->{loop}->addFD($fo->{fd}, EV_READ, [ | 
| 379 | 84 |  |  |  |  | 967 | $type2cb{$type}($self,$fo->{fd}), | 
| 380 |  |  |  |  |  |  | $self | 
| 381 |  |  |  |  |  |  | ]); | 
| 382 |  |  |  |  |  |  | } | 
| 383 |  |  |  |  |  |  | } | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | sub _check_from { | 
| 386 | 0 |  |  | 0 |  | 0 | my Net::SIP::SocketPool $self = shift; | 
| 387 | 0 | 0 |  |  |  | 0 | my $dst = $self->{dst} or return; | 
| 388 | 0 |  |  |  |  | 0 | my ($ip,$port) = ip_sockaddr2parts(shift()); | 
| 389 | 0 | 0 | 0 |  |  | 0 | if ($ip ne $dst->{addr} or $port ne $dst->{port}) { | 
| 390 | 0 | 0 |  |  |  | 0 | $DEBUG && DEBUG(1, | 
| 391 |  |  |  |  |  |  | "drop packet received from %s since expecting only from %s", | 
| 392 |  |  |  |  |  |  | ip_parts2string($ip,$port), | 
| 393 |  |  |  |  |  |  | ip_parts2string($dst) | 
| 394 |  |  |  |  |  |  | ); | 
| 395 | 0 |  |  |  |  | 0 | return 0; | 
| 396 |  |  |  |  |  |  | } | 
| 397 | 0 |  |  |  |  | 0 | return 1; | 
| 398 |  |  |  |  |  |  | } | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | sub _handle_read_udp { | 
| 401 | 123 |  |  | 123 |  | 254 | my Net::SIP::SocketPool $self = shift; | 
| 402 | 123 |  |  |  |  | 226 | my $fd = shift; | 
| 403 | 123 | 50 |  |  |  | 888 | my $fo = $self->{fds}{ fileno($fd) } or die; | 
| 404 | 123 | 50 |  |  |  | 3408 | my $from = recv($fd, my $buf, 2**16, 0) or return; | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | # packet must be at least 13 bytes big (first line incl version | 
| 407 |  |  |  |  |  |  | # + final crlf crlf). Ignore anything smaller, probably keep-alives | 
| 408 | 123 | 50 |  |  |  | 632 | if ( length($buf)<13 ) { | 
| 409 | 0 |  |  |  |  | 0 | DEBUG(11,"ignored packet with len ".length($buf)." because to small (keep-alive?)"); | 
| 410 | 0 |  |  |  |  | 0 | return; | 
| 411 |  |  |  |  |  |  | } | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | # check dst on unconnected UDP sockets | 
| 414 | 123 | 50 | 33 |  |  | 428 | shift() && ! _check_from($self,$from) && return; | 
| 415 |  |  |  |  |  |  |  | 
| 416 | 123 | 50 |  |  |  | 913 | my $pkt = eval { Net::SIP::Packet->new_from_string($buf) } or | 
|  | 123 |  |  |  |  | 2264 |  | 
| 417 |  |  |  |  |  |  | return $self->_error( | 
| 418 |  |  |  |  |  |  | "drop invalid packet received from %s: %s", | 
| 419 |  |  |  |  |  |  | ip_sockaddr2string($from), $@ | 
| 420 |  |  |  |  |  |  | ); | 
| 421 |  |  |  |  |  |  |  | 
| 422 |  |  |  |  |  |  | invoke_callback($self->{cb},$pkt, { | 
| 423 | 123 |  |  |  |  | 368 | %{ ip_sockaddr2parts($from) }, | 
|  | 123 |  |  |  |  | 614 |  | 
| 424 |  |  |  |  |  |  | proto => 'udp', | 
| 425 |  |  |  |  |  |  | socket => $fd, | 
| 426 |  |  |  |  |  |  | }); | 
| 427 |  |  |  |  |  |  | } | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | # read from unconnected TCP socket: | 
| 430 |  |  |  |  |  |  | # - accept new connection | 
| 431 |  |  |  |  |  |  | # - check against dst | 
| 432 |  |  |  |  |  |  | # - setup new connection to receive data | 
| 433 |  |  |  |  |  |  | sub _handle_read_tcp_m { | 
| 434 | 10 |  |  | 10 |  | 68 | my Net::SIP::SocketPool $self = shift; | 
| 435 | 10 |  |  |  |  | 61 | my $srvfd = shift; | 
| 436 | 10 | 50 |  |  |  | 431 | my $srvfo = $self->{fds}{ fileno($srvfd) } or die; | 
| 437 | 10 | 50 |  |  |  | 1025 | my $from = accept(my $clfd, $srvfd) or return; | 
| 438 | 10 | 50 | 33 |  |  | 325 | $self->{dst} && ! _check_from($self,$from) && return; | 
| 439 |  |  |  |  |  |  | my $clfo = $self->_add_socket({ | 
| 440 |  |  |  |  |  |  | fd => $clfd, | 
| 441 |  |  |  |  |  |  | peer => scalar(ip_sockaddr2parts($from)), | 
| 442 |  |  |  |  |  |  | rbuf => '', | 
| 443 |  |  |  |  |  |  | wbuf => '', | 
| 444 |  |  |  |  |  |  | didit => $self->{loop}->looptime, | 
| 445 | 10 |  | 100 |  |  | 301 | inside_connect => $self->{tls} && 1, | 
| 446 |  |  |  |  |  |  | }); | 
| 447 | 10 | 100 |  |  |  | 155 | _tls_accept($self,$clfo) if $self->{tls}; | 
| 448 |  |  |  |  |  |  | } | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | # read from connected TCP socket: | 
| 452 |  |  |  |  |  |  | # Since TCP is a stream SIP messages might be split over multiple reads or | 
| 453 |  |  |  |  |  |  | # a single read might contain more than one message. | 
| 454 |  |  |  |  |  |  | sub _handle_read_tcp_co { | 
| 455 | 88 |  |  | 88 |  | 215 | my Net::SIP::SocketPool $self = shift; | 
| 456 | 88 |  |  |  |  | 263 | my ($fd,$from) = @_; | 
| 457 | 88 | 50 |  |  |  | 724 | my $fo = $self->{fds}{ fileno($fd) } or die "no fd for read"; | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | $DEBUG && $fo->{rbuf} ne '' && DEBUG(20, | 
| 460 | 88 | 50 | 33 |  |  | 1131 | "continue reading SIP packet, offset=%d",length($fo->{rbuf})); | 
| 461 |  |  |  |  |  |  |  | 
| 462 |  |  |  |  |  |  | retry: | 
| 463 |  |  |  |  |  |  | my $n = sysread($fd, $fo->{rbuf}, | 
| 464 |  |  |  |  |  |  | # read max size of TLS frame when tls so that we don't get any awkward | 
| 465 |  |  |  |  |  |  | # effects with user space buffering in TLS stack and select(2) | 
| 466 |  |  |  |  |  |  | $self->{tls} ? 2**14 : $TCP_READSIZE, | 
| 467 | 88 | 100 |  |  |  | 1764 | length($fo->{rbuf})); | 
| 468 | 88 | 50 |  |  |  | 4092 | if (!defined $n) { | 
| 469 | 0 | 0 |  |  |  | 0 | goto retry if $!{EINTR}; | 
| 470 | 0 | 0 | 0 |  |  | 0 | return if $!{EAGAIN} || $!{EWOULDBLOCK}; | 
| 471 | 0 |  |  |  |  | 0 | return $self->_del_socket($fo, | 
| 472 |  |  |  |  |  |  | "error while reading from %s: %s", | 
| 473 |  |  |  |  |  |  | ip_sockaddr2string($from), $!); | 
| 474 |  |  |  |  |  |  | } | 
| 475 | 88 | 100 |  |  |  | 616 | if (!$n) { | 
| 476 |  |  |  |  |  |  | # peer closed | 
| 477 | 2 |  |  |  |  | 16 | return $self->_del_socket($fo); | 
| 478 |  |  |  |  |  |  | } | 
| 479 |  |  |  |  |  |  |  | 
| 480 |  |  |  |  |  |  | process_packet: | 
| 481 |  |  |  |  |  |  | # ignore any leading \r\n according to RFC 3261 7.5 | 
| 482 | 86 | 50 |  |  |  | 604 | if ($fo->{rbuf} =~s{\A((?:\r\n)+)}{}) { | 
| 483 | 0 | 0 |  |  |  | 0 | $DEBUG && DEBUG(20,"skipped over newlines preceding packet, size=%d", | 
| 484 |  |  |  |  |  |  | length($1)); | 
| 485 |  |  |  |  |  |  | } | 
| 486 |  |  |  |  |  |  |  | 
| 487 | 86 |  |  |  |  | 787 | my $hdrpos = index($fo->{rbuf},"\r\n\r\n"); | 
| 488 | 86 | 50 | 33 |  |  | 1262 | if ($hdrpos<0 && length($fo->{rbuf}) > $MAX_SIP_HEADER | 
|  |  |  | 33 |  |  |  |  | 
| 489 |  |  |  |  |  |  | or $hdrpos > $MAX_SIP_HEADER) { | 
| 490 | 0 |  |  |  |  | 0 | return $self->_del_socket($fo, | 
| 491 |  |  |  |  |  |  | "drop packet from %s since SIP header is too big", | 
| 492 |  |  |  |  |  |  | ip_sockaddr2string($from)); | 
| 493 |  |  |  |  |  |  | } | 
| 494 | 86 | 50 |  |  |  | 307 | if ($hdrpos<0) { | 
| 495 | 0 | 0 |  |  |  | 0 | $DEBUG && DEBUG(20,"need more data for SIP header"); | 
| 496 | 0 |  |  |  |  | 0 | return; | 
| 497 |  |  |  |  |  |  | } | 
| 498 | 86 |  |  |  |  | 164 | $hdrpos += 4; # after header | 
| 499 | 86 |  |  |  |  | 553 | my %clen = map { $_ => 1 } | 
| 500 | 86 |  |  |  |  | 1505 | substr($fo->{rbuf},0,$hdrpos) =~m{\nContent-length:\s*(\d+)\s*\n}ig; | 
| 501 | 86 | 50 |  |  |  | 455 | if (!%clen) { | 
| 502 | 0 |  |  |  |  | 0 | return $self->_del_socket($fo, | 
| 503 |  |  |  |  |  |  | "drop invalid SIP packet from %s: missing content-length", | 
| 504 |  |  |  |  |  |  | ip_sockaddr2string($from)); | 
| 505 |  |  |  |  |  |  | } | 
| 506 | 86 | 50 |  |  |  | 422 | if (keys(%clen)>1) { | 
| 507 | 0 |  |  |  |  | 0 | return $self->_del_socket($fo, | 
| 508 |  |  |  |  |  |  | "drop invalid SIP packet from %s: conflicting content-length", | 
| 509 |  |  |  |  |  |  | ip_sockaddr2string($from)); | 
| 510 |  |  |  |  |  |  | } | 
| 511 | 86 |  |  |  |  | 270 | my $clen = (keys %clen)[0]; | 
| 512 | 86 | 50 |  |  |  | 336 | if ($clen > $MAX_SIP_BODY) { | 
| 513 | 0 |  |  |  |  | 0 | return $self->_del_socket($fo, | 
| 514 |  |  |  |  |  |  | "drop packet from %s since SIP body is too big: %d>%d", | 
| 515 |  |  |  |  |  |  | ip_sockaddr2string($from), $clen, $MAX_SIP_BODY); | 
| 516 |  |  |  |  |  |  | } | 
| 517 | 86 | 50 |  |  |  | 317 | if ($hdrpos + $clen > length($fo->{rbuf})) { | 
| 518 |  |  |  |  |  |  | $DEBUG && DEBUG(20,"need %d more bytes for SIP body", | 
| 519 | 0 | 0 |  |  |  | 0 | $hdrpos + $clen - length($fo->{rbuf})); | 
| 520 | 0 |  |  |  |  | 0 | return; | 
| 521 |  |  |  |  |  |  | } | 
| 522 |  |  |  |  |  |  |  | 
| 523 | 86 | 50 |  |  |  | 209 | my $pkt = eval { | 
| 524 | 86 |  |  |  |  | 1406 | Net::SIP::Packet->new_from_string(substr($fo->{rbuf},0,$hdrpos+$clen,'')) | 
| 525 |  |  |  |  |  |  | } or return $self->_del_socket($fo, | 
| 526 |  |  |  |  |  |  | "drop invalid packet received from %s: %s", | 
| 527 |  |  |  |  |  |  | ip_sockaddr2string($from), $@); | 
| 528 |  |  |  |  |  |  |  | 
| 529 | 86 | 100 |  |  |  | 1022 | if ($pkt->is_request) { | 
| 530 |  |  |  |  |  |  | # associate $pkt->tid with this socket | 
| 531 | 30 |  | 100 |  |  | 230 | my $tidlist = $fo->{tids} ||= []; | 
| 532 | 30 |  |  |  |  | 235 | push @$tidlist, $pkt->tid; | 
| 533 | 30 |  |  |  |  | 313 | while (@$tidlist > $MAX_TIDLIST) { | 
| 534 | 0 |  |  |  |  | 0 | my $tid = shift(@$tidlist); | 
| 535 | 0 |  |  |  |  | 0 | delete $self->{tids}{$tid}; | 
| 536 |  |  |  |  |  |  | } | 
| 537 | 30 |  |  |  |  | 158 | $self->{tids}{ $tidlist->[-1] } = $fo; | 
| 538 |  |  |  |  |  |  | } | 
| 539 |  |  |  |  |  |  |  | 
| 540 | 86 | 50 |  |  |  | 727 | $fo->{didit} = $self->{loop}->looptime if $self->{loop}; | 
| 541 |  |  |  |  |  |  | invoke_callback($self->{cb},$pkt, { | 
| 542 | 86 |  |  |  |  | 434 | %{ ip_sockaddr2parts($from) }, | 
| 543 | 86 | 100 |  |  |  | 209 | proto => $self->{tls} ? 'tls' : 'tcp', | 
| 544 |  |  |  |  |  |  | socket => $fd, | 
| 545 |  |  |  |  |  |  | }); | 
| 546 |  |  |  |  |  |  |  | 
| 547 |  |  |  |  |  |  | # continue with processing any remaining data in the buffer | 
| 548 | 86 | 50 |  |  |  | 9725 | goto process_packet if $fo->{rbuf} ne ''; | 
| 549 |  |  |  |  |  |  | } | 
| 550 |  |  |  |  |  |  |  | 
| 551 |  |  |  |  |  |  | sub _tcp_connect { | 
| 552 | 30 |  |  | 30 |  | 85 | my Net::SIP::SocketPool $self = shift; | 
| 553 | 30 |  |  |  |  | 72 | my ($fo,$peer,$callback,$xxfd) = @_; | 
| 554 |  |  |  |  |  |  |  | 
| 555 | 30 |  |  |  |  | 117 | while (!$xxfd) { | 
| 556 |  |  |  |  |  |  | # direct call, no connect done yet | 
| 557 | 15 |  |  |  |  | 79 | $fo->{didit} = $self->{loop}->looptime; | 
| 558 | 15 |  |  |  |  | 8756 | my $rv = connect($fo->{fd},$peer); | 
| 559 | 15 | 50 | 0 |  |  | 91 | $DEBUG && DEBUG(100,"tcp connect: ".($rv || $!)); | 
| 560 | 15 | 50 |  |  |  | 73 | if ($rv) { | 
| 561 |  |  |  |  |  |  | # successful connect | 
| 562 | 0 | 0 |  |  |  | 0 | return _tls_connect($self,$fo,$callback) if $self->{tls}; | 
| 563 | 0 |  |  |  |  | 0 | delete $fo->{inside_connect}; | 
| 564 | 0 |  |  |  |  | 0 | last; | 
| 565 |  |  |  |  |  |  | } | 
| 566 | 15 | 50 |  |  |  | 2517 | next if $!{EINTR}; | 
| 567 | 15 | 50 | 33 |  |  | 511 | if ($!{EALREADY} || $!{EINPROGRESS}) { | 
| 568 |  |  |  |  |  |  | # insert write handler | 
| 569 | 15 | 50 |  |  |  | 598 | $DEBUG && DEBUG(100,"tcp connect: add write handler for async connect"); | 
| 570 | 15 |  |  |  |  | 128 | $self->{loop}->addFD($fo->{fd}, EV_WRITE, | 
| 571 |  |  |  |  |  |  | [ \&_tcp_connect, $self,$fo,$peer,$callback ]); | 
| 572 | 15 |  |  |  |  | 50 | return; | 
| 573 |  |  |  |  |  |  | } | 
| 574 |  |  |  |  |  |  | # connect permanently failed | 
| 575 | 0 |  |  |  |  | 0 | my $err = $!; | 
| 576 | 0 |  |  |  |  | 0 | $self->_del_socket($fo, | 
| 577 |  |  |  |  |  |  | "connect to ".ip_sockaddr2string($peer)." failed: $!"); | 
| 578 | 0 |  |  |  |  | 0 | invoke_callback($callback,$err); | 
| 579 | 0 |  |  |  |  | 0 | return; | 
| 580 |  |  |  |  |  |  | } | 
| 581 |  |  |  |  |  |  |  | 
| 582 | 15 | 50 |  |  |  | 88 | if ($xxfd) { | 
| 583 |  |  |  |  |  |  | # we are called from loop and hopefully async connect was succesful: | 
| 584 |  |  |  |  |  |  | # use getsockopt to check | 
| 585 | 15 |  |  |  |  | 244 | my $err = getsockopt($xxfd, SOL_SOCKET, SO_ERROR); | 
| 586 | 15 | 50 |  |  |  | 128 | $err = $err ? unpack('i',$err) : $!; | 
| 587 | 15 | 50 |  |  |  | 59 | if ($err) { | 
| 588 |  |  |  |  |  |  | # connection failed | 
| 589 | 0 |  |  |  |  | 0 | $! = $err; | 
| 590 | 0 |  |  |  |  | 0 | $self->_del_socket($fo, | 
| 591 |  |  |  |  |  |  | "connect to ".ip_sockaddr2string($peer)." failed: $!"); | 
| 592 | 0 |  |  |  |  | 0 | invoke_callback($callback,$err); | 
| 593 | 0 |  |  |  |  | 0 | return; | 
| 594 |  |  |  |  |  |  | } | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | # connect done: remove write handler | 
| 597 | 15 |  |  |  |  | 141 | $self->{loop}->delFD($xxfd, EV_WRITE); | 
| 598 | 15 | 100 |  |  |  | 96 | return _tls_connect($self,$fo,$callback) if $self->{tls}; | 
| 599 | 10 |  |  |  |  | 26 | delete $fo->{inside_connect}; | 
| 600 |  |  |  |  |  |  | } | 
| 601 |  |  |  |  |  |  |  | 
| 602 | 10 |  |  |  |  | 58 | _addreader2loop($self,$fo); | 
| 603 |  |  |  |  |  |  |  | 
| 604 |  |  |  |  |  |  | # if we have something to write continue in _tcp_send | 
| 605 | 10 | 50 |  |  |  | 86 | return _tcp_send($self,$fo,$callback) if $fo->{wbuf} ne ''; | 
| 606 |  |  |  |  |  |  |  | 
| 607 |  |  |  |  |  |  | # otherwise signal success via callback | 
| 608 | 0 |  |  |  |  | 0 | invoke_callback($callback,0) | 
| 609 |  |  |  |  |  |  | } | 
| 610 |  |  |  |  |  |  |  | 
| 611 |  |  |  |  |  |  | sub _tcp_send { | 
| 612 | 82 |  |  | 82 |  | 216 | my Net::SIP::SocketPool $self = shift; | 
| 613 | 82 |  |  |  |  | 468 | my ($fo,$callback,$xxfd) = @_; | 
| 614 | 82 |  |  |  |  | 289 | while ($fo->{wbuf} ne '') { | 
| 615 | 82 |  |  |  |  | 402 | $fo->{didit} = $self->{loop}->looptime; | 
| 616 | 82 | 50 |  |  |  | 4194 | if (my $n = syswrite($fo->{fd},$fo->{wbuf})) { | 
| 617 | 82 |  |  |  |  | 23854 | substr($fo->{wbuf},0,$n,''); | 
| 618 | 82 |  |  |  |  | 303 | next; | 
| 619 |  |  |  |  |  |  | } | 
| 620 |  |  |  |  |  |  |  | 
| 621 | 0 | 0 |  |  |  | 0 | next if $!{EINTR}; | 
| 622 | 0 | 0 | 0 |  |  | 0 | if ($!{EAGAIN} || $!{EWOULDBLOCK}) { | 
| 623 | 0 | 0 |  |  |  | 0 | return if $xxfd; # called from loop: write handler already set up | 
| 624 |  |  |  |  |  |  | # insert write handler | 
| 625 | 0 |  |  |  |  | 0 | $self->{loop}->addFD($fo->{fd}, EV_WRITE, | 
| 626 |  |  |  |  |  |  | [ \&_tcp_send, $self,$fo,$callback ]); | 
| 627 | 0 |  |  |  |  | 0 | return; | 
| 628 |  |  |  |  |  |  | } | 
| 629 |  |  |  |  |  |  |  | 
| 630 |  |  |  |  |  |  | # permanently failed to write | 
| 631 | 0 |  |  |  |  | 0 | my $err = $!; | 
| 632 | 0 |  |  |  |  | 0 | $self->_del_socket($fo, "write failed: $!"); | 
| 633 | 0 |  |  |  |  | 0 | invoke_callback($callback,$err); | 
| 634 | 0 |  |  |  |  | 0 | return; | 
| 635 |  |  |  |  |  |  | } | 
| 636 |  |  |  |  |  |  |  | 
| 637 |  |  |  |  |  |  | # write done: remove write handler if we are called from loop | 
| 638 | 82 | 50 |  |  |  | 213 | $DEBUG && DEBUG(90,"everything has been sent"); | 
| 639 | 82 | 50 |  |  |  | 202 | $self->{loop}->delFD($xxfd, EV_WRITE) if $xxfd; | 
| 640 |  |  |  |  |  |  |  | 
| 641 |  |  |  |  |  |  | # signal success via callback | 
| 642 | 82 |  |  |  |  | 430 | invoke_callback($callback,0) | 
| 643 |  |  |  |  |  |  | } | 
| 644 |  |  |  |  |  |  |  | 
| 645 |  |  |  |  |  |  | sub _tls_accept { | 
| 646 | 13 |  |  | 13 |  | 47 | my Net::SIP::SocketPool $self = shift; | 
| 647 | 13 |  |  |  |  | 44 | my ($fo,$xxfd) = @_; | 
| 648 | 13 | 100 |  |  |  | 62 | if (!$xxfd) { | 
| 649 | 5 | 50 |  |  |  | 30 | $DEBUG && DEBUG(40,"upgrade to SSL server"); | 
| 650 |  |  |  |  |  |  | IO::Socket::SSL->start_SSL($fo->{fd}, | 
| 651 | 5 | 50 |  |  |  | 33 | %{$self->{tls}{m}}, | 
|  | 5 |  |  |  |  | 149 |  | 
| 652 |  |  |  |  |  |  | SSL_startHandshake => 0, | 
| 653 |  |  |  |  |  |  | ) or die "upgrade to SSL socket failed: $SSL_ERROR"; | 
| 654 |  |  |  |  |  |  | } | 
| 655 |  |  |  |  |  |  |  | 
| 656 | 13 | 100 |  |  |  | 1275 | if ($fo->{fd}->accept_SSL()) { | 
| 657 | 5 | 50 |  |  |  | 44360 | if ($DEBUG) { | 
| 658 | 0 |  |  |  |  | 0 | my $peer_cert = $fo->{fd}->peer_certificate; | 
| 659 | 0 | 0 |  |  |  | 0 | DEBUG(40,"TLS accept success, %s", $peer_cert | 
| 660 |  |  |  |  |  |  | ? "peer="._dump_certificate($peer_cert) | 
| 661 |  |  |  |  |  |  | : 'no peer certificate'); | 
| 662 |  |  |  |  |  |  | } | 
| 663 | 5 |  |  |  |  | 31 | delete $fo->{inside_connect}; | 
| 664 | 5 | 100 |  |  |  | 52 | $self->{loop}->delFD($xxfd, EV_WRITE) if $xxfd; | 
| 665 | 5 |  |  |  |  | 52 | _addreader2loop($self,$fo); | 
| 666 | 5 |  |  |  |  | 40 | return; | 
| 667 |  |  |  |  |  |  | } | 
| 668 |  |  |  |  |  |  |  | 
| 669 | 8 | 50 |  |  |  | 65070 | if ($SSL_ERROR == $SSL_WANT_READ) { | 
|  |  | 0 |  |  |  |  |  | 
| 670 | 8 | 50 |  |  |  | 36 | $DEBUG && DEBUG(40,"TLS accept - want read"); | 
| 671 | 8 | 100 |  |  |  | 68 | $self->{loop}->delFD($xxfd, EV_WRITE) if $xxfd; | 
| 672 | 8 |  |  |  |  | 59 | $self->{loop}->addFD($fo->{fd}, EV_READ, [ \&_tls_accept, $self, $fo ]); | 
| 673 |  |  |  |  |  |  | } elsif ($SSL_ERROR == $SSL_WANT_WRITE) { | 
| 674 | 0 | 0 |  |  |  | 0 | $DEBUG && DEBUG(40,"TLS accept - want write"); | 
| 675 | 0 | 0 |  |  |  | 0 | $self->{loop}->delFD($xxfd, EV_READ) if $xxfd; | 
| 676 | 0 |  |  |  |  | 0 | $self->{loop}->addFD($fo->{fd}, EV_WRITE, | 
| 677 |  |  |  |  |  |  | [ \&_tls_accept, $self, $fo ]); | 
| 678 |  |  |  |  |  |  | } else { | 
| 679 |  |  |  |  |  |  | # permanent error | 
| 680 | 0 |  |  |  |  | 0 | _del_socket($self, $fo, | 
| 681 |  |  |  |  |  |  | "SSL accept failed: $SSL_ERROR"); | 
| 682 |  |  |  |  |  |  | } | 
| 683 |  |  |  |  |  |  | } | 
| 684 |  |  |  |  |  |  |  | 
| 685 |  |  |  |  |  |  |  | 
| 686 |  |  |  |  |  |  | sub _tls_connect { | 
| 687 | 15 |  |  | 15 |  | 55 | my Net::SIP::SocketPool $self = shift; | 
| 688 | 15 |  |  |  |  | 50 | my ($fo,$callback,$xxfd) = @_; | 
| 689 | 15 | 100 |  |  |  | 58 | if (!$xxfd) { | 
| 690 | 5 | 50 |  |  |  | 25 | $DEBUG && DEBUG(40,"upgrade to SSL client"); | 
| 691 |  |  |  |  |  |  | IO::Socket::SSL->start_SSL($fo->{fd}, | 
| 692 | 5 |  |  |  |  | 183 | %{$self->{tls}{c}}, | 
| 693 |  |  |  |  |  |  | SSL_startHandshake => 0, | 
| 694 |  |  |  |  |  |  | SSL_verifycn_name => $fo->{peer}{host}, | 
| 695 |  |  |  |  |  |  | SSL_hostname => $fo->{peer}{host}, | 
| 696 | 5 | 50 |  |  |  | 25 | ) or die "upgrade to SSL socket failed: $SSL_ERROR"; | 
| 697 |  |  |  |  |  |  | } | 
| 698 |  |  |  |  |  |  |  | 
| 699 | 15 | 100 |  |  |  | 1148 | if ($fo->{fd}->connect_SSL()) { | 
| 700 |  |  |  |  |  |  | $DEBUG && DEBUG(40,"TLS connect success peer cert=%s", | 
| 701 | 5 | 50 |  |  |  | 1312 | _dump_certificate($fo->{fd}->peer_certificate)); | 
| 702 | 5 |  |  |  |  | 36 | delete $fo->{inside_connect}; | 
| 703 | 5 | 50 |  |  |  | 67 | $self->{loop}->delFD($xxfd, EV_WRITE) if $xxfd; | 
| 704 | 5 |  |  |  |  | 47 | _addreader2loop($self,$fo); | 
| 705 | 5 | 50 |  |  |  | 82 | return _tcp_send($self,$fo,$callback) if $fo->{wbuf} ne ''; | 
| 706 | 0 |  |  |  |  | 0 | invoke_callback($callback,0); | 
| 707 | 0 |  |  |  |  | 0 | return; | 
| 708 |  |  |  |  |  |  | } | 
| 709 |  |  |  |  |  |  |  | 
| 710 | 10 | 50 |  |  |  | 10496 | if ($SSL_ERROR == $SSL_WANT_READ) { | 
|  |  | 0 |  |  |  |  |  | 
| 711 | 10 | 50 |  |  |  | 42 | $DEBUG && DEBUG(40,"TLS connect - want read"); | 
| 712 | 10 | 100 |  |  |  | 73 | $self->{loop}->delFD($xxfd, EV_WRITE) if $xxfd; | 
| 713 | 10 |  |  |  |  | 83 | $self->{loop}->addFD($fo->{fd}, EV_READ, | 
| 714 |  |  |  |  |  |  | [ \&_tls_connect, $self, $fo, $callback ]); | 
| 715 |  |  |  |  |  |  | } elsif ($SSL_ERROR == $SSL_WANT_WRITE) { | 
| 716 | 0 | 0 |  |  |  | 0 | $DEBUG && DEBUG(40,"TLS connect - want write"); | 
| 717 | 0 | 0 |  |  |  | 0 | $self->{loop}->delFD($xxfd, EV_READ) if $xxfd; | 
| 718 | 0 |  |  |  |  | 0 | $self->{loop}->addFD($fo->{fd}, EV_WRITE, | 
| 719 |  |  |  |  |  |  | [ \&_tls_connect, $self, $fo, $callback ]); | 
| 720 |  |  |  |  |  |  | } else { | 
| 721 |  |  |  |  |  |  | # permanent error | 
| 722 | 0 |  |  |  |  | 0 | _del_socket($self, $fo, | 
| 723 |  |  |  |  |  |  | "SSL connect failed: $SSL_ERROR"); | 
| 724 |  |  |  |  |  |  | } | 
| 725 |  |  |  |  |  |  | } | 
| 726 |  |  |  |  |  |  |  | 
| 727 |  |  |  |  |  |  |  | 
| 728 |  |  |  |  |  |  | sub _dump_certificate { | 
| 729 | 0 | 0 |  | 0 |  | 0 | my $cert = shift or return ''; | 
| 730 | 0 |  |  |  |  | 0 | my $issuer = Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_issuer_name($cert)); | 
| 731 | 0 |  |  |  |  | 0 | my $subject = Net::SSLeay::X509_NAME_oneline( Net::SSLeay::X509_get_subject_name($cert)); | 
| 732 | 0 |  |  |  |  | 0 | return "s:$subject i:$issuer"; | 
| 733 |  |  |  |  |  |  | } | 
| 734 |  |  |  |  |  |  |  | 
| 735 |  |  |  |  |  |  | 1; |