… | |
… | |
70 | $port or $host =~ s/:([^:]+)$// and $port = $1; |
70 | $port or $host =~ s/:([^:]+)$// and $port = $1; |
71 | my $_proto = _proto($proto); |
71 | my $_proto = _proto($proto); |
72 | my $_port = _port($port, $proto); |
72 | my $_port = _port($port, $proto); |
73 | |
73 | |
74 | # optimize this a bit for a common case |
74 | # optimize this a bit for a common case |
75 | if ($host =~ /^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9][0-9]?) |
75 | if (Coro::Util::dotted_quad $host) { |
76 | \.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9][0-9]?) |
|
|
77 | \.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9][0-9]?) |
|
|
78 | \.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9][0-9]?)$/) { |
|
|
79 | return pack_sockaddr_in($_port, inet_aton $host); |
76 | return pack_sockaddr_in ($_port, inet_aton $host); |
80 | } else { |
77 | } else { |
81 | my (undef, undef, undef, undef, @host) = Coro::Util::gethostbyname $host |
78 | my (undef, undef, undef, undef, @host) = Coro::Util::gethostbyname $host |
82 | or croak "unknown host: $host"; |
79 | or croak "unknown host: $host"; |
83 | map pack_sockaddr_in($_port,$_), @host; |
80 | map pack_sockaddr_in ($_port,$_), @host; |
84 | } |
81 | } |
85 | } |
82 | } |
86 | |
83 | |
87 | =item $fh = new Coro::Socket param => value, ... |
84 | =item $fh = new Coro::Socket param => value, ... |
88 | |
85 | |
… | |
… | |
98 | $fh = new Coro::Socket PeerHost => "localhost", PeerPort => 'finger'; |
95 | $fh = new Coro::Socket PeerHost => "localhost", PeerPort => 'finger'; |
99 | |
96 | |
100 | =cut |
97 | =cut |
101 | |
98 | |
102 | sub _prepare_socket { |
99 | sub _prepare_socket { |
103 | my ($class, $arg) = @_; |
100 | my ($self, $arg) = @_; |
104 | my $fh; |
|
|
105 | |
101 | |
106 | socket $fh, PF_INET, $arg->{Type}, _proto($arg->{Proto}) |
102 | $self |
107 | or return; |
|
|
108 | |
|
|
109 | $fh = bless Coro::Handle->new_from_fh ( |
|
|
110 | $fh, |
|
|
111 | timeout => $arg->{Timeout}, |
|
|
112 | forward_class => $arg->{forward_class}, |
|
|
113 | ), $class |
|
|
114 | or return; |
|
|
115 | |
|
|
116 | if ($arg->{ReuseAddr}) { |
|
|
117 | $fh->setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) |
|
|
118 | or croak "setsockopt(SO_REUSEADDR): $!"; |
|
|
119 | } |
|
|
120 | |
|
|
121 | if ($arg->{ReusePort}) { |
|
|
122 | $fh->setsockopt(SOL_SOCKET, SO_REUSEPORT, 1) |
|
|
123 | or croak "setsockopt(SO_REUSEPORT): $!"; |
|
|
124 | } |
|
|
125 | |
|
|
126 | if ($arg->{LocalPort} || $arg->{LocalHost}) { |
|
|
127 | my @sa = _sa($arg->{LocalHost} || "0.0.0.0", $arg->{LocalPort} || 0, $arg->{Proto}); |
|
|
128 | $fh->bind($sa[0]) |
|
|
129 | or croak "bind($arg->{LocalHost}:$arg->{LocalPort}): $!"; |
|
|
130 | } |
|
|
131 | |
|
|
132 | $fh; |
|
|
133 | } |
103 | } |
134 | |
104 | |
135 | sub new { |
105 | sub new { |
136 | my $class = shift; |
|
|
137 | my %arg = @_; |
106 | my ($class, %arg) = @_; |
138 | my $fh; |
|
|
139 | |
107 | |
140 | $arg{Proto} ||= 'tcp'; |
108 | $arg{Proto} ||= 'tcp'; |
141 | $arg{LocalHost} ||= delete $arg{LocalAddr}; |
109 | $arg{LocalHost} ||= delete $arg{LocalAddr}; |
142 | $arg{PeerHost} ||= delete $arg{PeerAddr}; |
110 | $arg{PeerHost} ||= delete $arg{PeerAddr}; |
143 | defined ($arg{Type}) or $arg{Type} = $arg{Proto} eq "tcp" ? SOCK_STREAM : SOCK_DGRAM; |
111 | defined ($arg{Type}) or $arg{Type} = $arg{Proto} eq "tcp" ? SOCK_STREAM : SOCK_DGRAM; |
144 | |
112 | |
|
|
113 | socket my $fh, PF_INET, $arg{Type}, _proto ($arg{Proto}) |
|
|
114 | or return; |
|
|
115 | |
|
|
116 | my $self = bless Coro::Handle->new_from_fh ( |
|
|
117 | $fh, |
|
|
118 | timeout => $arg{Timeout}, |
|
|
119 | forward_class => $arg{forward_class}, |
|
|
120 | ), $class |
|
|
121 | or return; |
|
|
122 | |
|
|
123 | $self->configure (\%arg) |
|
|
124 | } |
|
|
125 | |
|
|
126 | sub configure { |
|
|
127 | my ($self, $arg) = @_; |
|
|
128 | |
|
|
129 | if ($arg->{ReuseAddr}) { |
|
|
130 | $self->setsockopt (SOL_SOCKET, SO_REUSEADDR, 1) |
|
|
131 | or croak "setsockopt(SO_REUSEADDR): $!"; |
|
|
132 | } |
|
|
133 | |
|
|
134 | if ($arg->{ReusePort}) { |
|
|
135 | $self->setsockopt (SOL_SOCKET, SO_REUSEPORT, 1) |
|
|
136 | or croak "setsockopt(SO_REUSEPORT): $!"; |
|
|
137 | } |
|
|
138 | |
|
|
139 | if ($arg->{LocalPort} || $arg->{LocalHost}) { |
|
|
140 | my @sa = _sa($arg->{LocalHost} || "0.0.0.0", $arg->{LocalPort} || 0, $arg->{Proto}); |
|
|
141 | $self->bind ($sa[0]) |
|
|
142 | or croak "bind($arg->{LocalHost}:$arg->{LocalPort}): $!"; |
|
|
143 | } |
|
|
144 | |
145 | if ($arg{PeerHost}) { |
145 | if ($arg->{PeerHost}) { |
146 | my @sa = _sa($arg{PeerHost}, $arg{PeerPort}, $arg{Proto}); |
146 | my @sa = _sa($arg->{PeerHost}, $arg->{PeerPort}, $arg->{Proto}); |
147 | |
147 | |
148 | for (@sa) { |
148 | for (@sa) { |
149 | $fh = $class->_prepare_socket(\%arg) |
|
|
150 | or return; |
|
|
151 | |
|
|
152 | $! = 0; |
149 | $! = 0; |
153 | |
150 | |
154 | if ($fh->connect($_)) { |
151 | if ($self->connect ($_)) { |
155 | next unless writable $fh; |
152 | next unless writable $self; |
156 | $! = unpack "i", $fh->getsockopt(SOL_SOCKET, SO_ERROR); |
153 | $! = unpack "i", $self->getsockopt(SOL_SOCKET, SO_ERROR); |
157 | } |
154 | } |
158 | |
155 | |
159 | $! or last; |
156 | $! or last; |
160 | |
157 | |
161 | $!{ECONNREFUSED} or $!{ENETUNREACH} or $!{ETIMEDOUT} or $!{EHOSTUNREACH} |
158 | $!{ECONNREFUSED} or $!{ENETUNREACH} or $!{ETIMEDOUT} or $!{EHOSTUNREACH} |
162 | or return; |
159 | or return; |
163 | |
|
|
164 | undef $fh; |
|
|
165 | } |
160 | } |
166 | } else { |
161 | } else { |
167 | $fh = $class->_prepare_socket(\%arg) |
|
|
168 | or return; |
|
|
169 | if (exists $arg{Listen}) { |
162 | if (exists $arg->{Listen}) { |
170 | $fh->listen($arg{Listen}) |
163 | $self->listen ($arg->{Listen}) |
171 | or return; |
164 | or return; |
172 | } |
165 | } |
173 | } |
166 | } |
174 | |
167 | |
175 | $fh; |
168 | $self |
176 | } |
169 | } |
177 | |
170 | |
178 | =item connect, listen, bind, getsockopt, setsockopt, |
171 | =item connect, listen, bind, getsockopt, setsockopt, |
179 | send, recv, peername, sockname, shutdown |
172 | send, recv, peername, sockname, shutdown |
180 | |
173 | |