File Coverage

blib/lib/Net/SIP/SocketPool.pm
Criterion Covered Total %
statement 249 339 73.4
branch 124 248 50.0
condition 30 69 43.4
subroutine 25 29 86.2
pod 4 4 100.0
total 432 689 62.7


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;