ssl_scan 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,773 @@
1
+ # -*- coding: binary -*-
2
+ require 'socket'
3
+ require 'thread'
4
+ require 'resolv'
5
+ require 'ssl_scan/exceptions'
6
+
7
+ module SSLScan
8
+
9
+ ###
10
+ #
11
+ # Base class for all sockets.
12
+ #
13
+ ###
14
+ module Socket
15
+
16
+ module Comm
17
+ end
18
+
19
+ require 'ssl_scan/socket/parameters'
20
+ require 'ssl_scan/socket/tcp'
21
+ require 'ssl_scan/socket/tcp_server'
22
+
23
+ require 'ssl_scan/socket/comm'
24
+ require 'ssl_scan/socket/comm/local'
25
+
26
+ require 'ssl_scan/socket/switch_board'
27
+ require 'ssl_scan/socket/subnet_walker'
28
+ require 'ssl_scan/socket/range_walker'
29
+
30
+ ##
31
+ #
32
+ # Factory methods
33
+ #
34
+ ##
35
+
36
+ #
37
+ # Create a socket instance using the supplied parameter hash.
38
+ #
39
+ def self.create(opts = {})
40
+ return create_param(SSLScan::Socket::Parameters.from_hash(opts))
41
+ end
42
+
43
+ #
44
+ # Create a socket using the supplied SSLScan::Socket::Parameter instance.
45
+ #
46
+ def self.create_param(param)
47
+ return param.comm.create(param)
48
+ end
49
+
50
+ #
51
+ # Create a TCP socket using the supplied parameter hash.
52
+ #
53
+ def self.create_tcp(opts = {})
54
+ return create_param(SSLScan::Socket::Parameters.from_hash(opts.merge('Proto' => 'tcp')))
55
+ end
56
+
57
+ #
58
+ # Create a TCP server socket using the supplied parameter hash.
59
+ #
60
+ def self.create_tcp_server(opts = {})
61
+ return create_tcp(opts.merge('Server' => true))
62
+ end
63
+
64
+ #
65
+ # Create a UDP socket using the supplied parameter hash.
66
+ #
67
+ def self.create_udp(opts = {})
68
+ return create_param(SSLScan::Socket::Parameters.from_hash(opts.merge('Proto' => 'udp')))
69
+ end
70
+
71
+ #
72
+ # Create a IP socket using the supplied parameter hash.
73
+ #
74
+ def self.create_ip(opts = {})
75
+ return create_param(SSLScan::Socket::Parameters.from_hash(opts.merge('Proto' => 'ip')))
76
+ end
77
+
78
+
79
+ #
80
+ # Common Regular Expressions
81
+ #
82
+
83
+ MATCH_IPV6 = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/
84
+
85
+ MATCH_IPV4 = /^\s*(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))\s*$/
86
+
87
+ MATCH_IPV4_PRIVATE = /^\s*(?:10\.|192\.168|172.(?:1[6-9]|2[0-9]|3[01])\.|169\.254)/
88
+
89
+ ##
90
+ #
91
+ # Serialization
92
+ #
93
+ ##
94
+
95
+
96
+ # Cache our IPv6 support flag
97
+ @@support_ipv6 = nil
98
+
99
+ #
100
+ # Determine whether we support IPv6
101
+ #
102
+ def self.support_ipv6?
103
+ return @@support_ipv6 if not @@support_ipv6.nil?
104
+
105
+ @@support_ipv6 = false
106
+
107
+ if (::Socket.const_defined?('AF_INET6'))
108
+ begin
109
+ s = ::Socket.new(::Socket::AF_INET6, ::Socket::SOCK_DGRAM, ::Socket::IPPROTO_UDP)
110
+ s.close
111
+ @@support_ipv6 = true
112
+ rescue
113
+ end
114
+ end
115
+
116
+ return @@support_ipv6
117
+ end
118
+
119
+ #
120
+ # Determine whether this is an IPv4 address
121
+ #
122
+ def self.is_ipv4?(addr)
123
+ ( addr =~ MATCH_IPV4 ) ? true : false
124
+ end
125
+
126
+ #
127
+ # Determine whether this is an IPv6 address
128
+ #
129
+ def self.is_ipv6?(addr)
130
+ ( addr =~ MATCH_IPV6 ) ? true : false
131
+ end
132
+
133
+ #
134
+ # Checks to see if the supplied address is in "dotted" form
135
+ #
136
+ def self.dotted_ip?(addr)
137
+ # Match IPv6
138
+ return true if (support_ipv6? and addr =~ MATCH_IPV6)
139
+
140
+ # Match IPv4
141
+ return true if (addr =~ MATCH_IPV4)
142
+
143
+ false
144
+ end
145
+
146
+ #
147
+ # Return true if +addr+ is within the ranges specified in RFC1918, or
148
+ # RFC5735/RFC3927
149
+ #
150
+ def self.is_internal?(addr)
151
+ if self.dotted_ip?(addr)
152
+ addr =~ MATCH_IPV4_PRIVATE
153
+ else
154
+ false
155
+ end
156
+ end
157
+
158
+ # Get the first address returned by a DNS lookup for +hostname+.
159
+ #
160
+ # @see .getaddresses
161
+ #
162
+ # @param (see .getaddresses)
163
+ # @return [String] ASCII IP address
164
+ def self.getaddress(hostname, accept_ipv6 = true)
165
+ getaddresses(hostname, accept_ipv6).first
166
+ end
167
+
168
+ #
169
+ # Wrapper for +::Socket.gethostbyname+ that takes special care to see if the
170
+ # supplied address is already an ASCII IP address. This is necessary to
171
+ # prevent blocking while waiting on a DNS reverse lookup when we already
172
+ # have what we need.
173
+ #
174
+ # @param hostname [String] A hostname or ASCII IP address
175
+ # @return [Array<String>]
176
+ def self.getaddresses(hostname, accept_ipv6 = true)
177
+ if hostname =~ MATCH_IPV4 or (accept_ipv6 and hostname =~ MATCH_IPV6)
178
+ return [hostname]
179
+ end
180
+
181
+ res = ::Socket.gethostbyname(hostname)
182
+ return [] if not res
183
+
184
+ # Shift the first three elements out, leaving just the list of
185
+ # addresses
186
+ res.shift # name
187
+ res.shift # alias hostnames
188
+ res.shift # address_family
189
+
190
+ # Rubinius has a bug where gethostbyname returns dotted quads instead of
191
+ # NBO, but that's what we want anyway, so just short-circuit here.
192
+ if res[0] =~ MATCH_IPV4 || res[0] =~ MATCH_IPV6
193
+ unless accept_ipv6
194
+ res.reject!{ |ascii| ascii =~ MATCH_IPV6 }
195
+ end
196
+ else
197
+ unless accept_ipv6
198
+ res.reject!{ |nbo| nbo.length != 4 }
199
+ end
200
+ res.map!{ |nbo| self.addr_ntoa(nbo) }
201
+ end
202
+
203
+ res
204
+ end
205
+
206
+ #
207
+ # Wrapper for Socket.gethostbyname which takes into account whether or not
208
+ # an IP address is supplied. If it is, then reverse DNS resolution does
209
+ # not occur. This is done in order to prevent delays, such as would occur
210
+ # on Windows.
211
+ #
212
+ def self.gethostbyname(host)
213
+ if (is_ipv4?(host))
214
+ return [ host, [], 2, host.split('.').map{ |c| c.to_i }.pack("C4") ]
215
+ end
216
+
217
+ if is_ipv6?(host)
218
+ # pop off the scopeid since gethostbyname isn't smart enough to
219
+ # deal with it.
220
+ host, _ = host.split('%', 2)
221
+ end
222
+
223
+ ::Socket.gethostbyname(host)
224
+ end
225
+
226
+ #
227
+ # Create a sockaddr structure using the supplied IP address, port, and
228
+ # address family
229
+ #
230
+ def self.to_sockaddr(ip, port)
231
+
232
+ if (ip == '::ffff:0.0.0.0')
233
+ ip = support_ipv6?() ? '::' : '0.0.0.0'
234
+ end
235
+
236
+ return ::Socket.pack_sockaddr_in(port, ip)
237
+ end
238
+
239
+ #
240
+ # Returns the address family, host, and port of the supplied sockaddr as
241
+ # [ af, host, port ]
242
+ #
243
+ def self.from_sockaddr(saddr)
244
+ port, host = ::Socket::unpack_sockaddr_in(saddr)
245
+ af = ::Socket::AF_INET
246
+ if (support_ipv6?() and is_ipv6?(host))
247
+ af = ::Socket::AF_INET6
248
+ end
249
+ return [ af, host, port ]
250
+ end
251
+
252
+ #
253
+ # Resolves a host to raw network-byte order.
254
+ #
255
+ def self.resolv_nbo(host)
256
+ self.gethostbyname( SSLScan::Socket.getaddress(host, true) )[3]
257
+ end
258
+
259
+ #
260
+ # Resolves a host to raw network-byte order.
261
+ #
262
+ def self.resolv_nbo_list(host)
263
+ SSLScan::Socket.getaddresses(host).map{|addr| self.gethostbyname(addr)[3] }
264
+ end
265
+
266
+ #
267
+ # Resolves a host to a network-byte order ruby integer.
268
+ #
269
+ def self.resolv_nbo_i(host)
270
+ addr_ntoi(resolv_nbo(host))
271
+ end
272
+
273
+ #
274
+ # Resolves a host to a list of network-byte order ruby integers.
275
+ #
276
+ def self.resolv_nbo_i_list(host)
277
+ resolv_nbo_list(host).map{|addr| addr_ntoi(addr) }
278
+ end
279
+
280
+ #
281
+ # Converts an ASCII IP address to a CIDR mask. Returns
282
+ # nil if it's not convertable.
283
+ #
284
+ def self.addr_atoc(mask)
285
+ mask_i = resolv_nbo_i(mask)
286
+ cidr = nil
287
+ 0.upto(32) do |i|
288
+ if ((1 << i)-1) << (32-i) == mask_i
289
+ cidr = i
290
+ break
291
+ end
292
+ end
293
+ return cidr
294
+ end
295
+
296
+ #
297
+ # Resolves a CIDR bitmask into a dotted-quad. Returns
298
+ # nil if it's not convertable.
299
+ #
300
+ def self.addr_ctoa(cidr)
301
+ return nil unless (0..32) === cidr.to_i
302
+ addr_itoa(((1 << cidr)-1) << 32-cidr)
303
+ end
304
+
305
+ #
306
+ # Resolves a host to a dotted address.
307
+ #
308
+ def self.resolv_to_dotted(host)
309
+ addr_ntoa(addr_aton(host))
310
+ end
311
+
312
+ #
313
+ # Converts a ascii address into an integer
314
+ #
315
+ def self.addr_atoi(addr)
316
+ resolv_nbo_i(addr)
317
+ end
318
+
319
+ #
320
+ # Converts a ascii address into a list of addresses
321
+ #
322
+ def self.addr_atoi_list(addr)
323
+ resolv_nbo_i_list(addr)
324
+ end
325
+
326
+ #
327
+ # Converts an integer address into ascii
328
+ #
329
+ # @param (see #addr_iton)
330
+ # @return (see #addr_ntoa)
331
+ def self.addr_itoa(addr, v6=false)
332
+ nboa = addr_iton(addr, v6)
333
+
334
+ addr_ntoa(nboa)
335
+ end
336
+
337
+ #
338
+ # Converts a ascii address to network byte order
339
+ #
340
+ def self.addr_aton(addr)
341
+ resolv_nbo(addr)
342
+ end
343
+
344
+ #
345
+ # Converts a network byte order address to ascii
346
+ #
347
+ # @param addr [String] Packed network-byte-order address
348
+ # @return [String] Human readable IP address.
349
+ def self.addr_ntoa(addr)
350
+ # IPv4
351
+ if (addr.length == 4)
352
+ return addr.unpack('C4').join('.')
353
+ end
354
+
355
+ # IPv6
356
+ if (addr.length == 16)
357
+ return compress_address(addr.unpack('n8').map{ |c| "%x" % c }.join(":"))
358
+ end
359
+
360
+ raise RuntimeError, "Invalid address format"
361
+ end
362
+
363
+ #
364
+ # Implement zero compression for IPv6 addresses.
365
+ # Uses the compression method from Marco Ceresa's IPAddress GEM
366
+ #
367
+ # @see https://github.com/bluemonk/ipaddress/blob/master/lib/ipaddress/ipv6.rb
368
+ #
369
+ # @param addr [String] Human readable IPv6 address
370
+ # @return [String] Human readable IPv6 address with runs of 0s removed
371
+ def self.compress_address(addr)
372
+ return addr unless is_ipv6?(addr)
373
+ addr = addr.dup
374
+ while true
375
+ break if addr.sub!(/\A0:0:0:0:0:0:0:0\Z/, '::')
376
+ break if addr.sub!(/\b0:0:0:0:0:0:0\b/, ':')
377
+ break if addr.sub!(/\b0:0:0:0:0:0\b/, ':')
378
+ break if addr.sub!(/\b0:0:0:0:0\b/, ':')
379
+ break if addr.sub!(/\b0:0:0:0\b/, ':')
380
+ break if addr.sub!(/\b0:0:0\b/, ':')
381
+ break if addr.sub!(/\b0:0\b/, ':')
382
+ break
383
+ end
384
+ addr.sub(/:{3,}/, '::')
385
+ end
386
+
387
+ #
388
+ # Converts a network byte order address to an integer
389
+ #
390
+ def self.addr_ntoi(addr)
391
+
392
+ bits = addr.unpack("N*")
393
+
394
+ if (bits.length == 1)
395
+ return bits[0]
396
+ end
397
+
398
+ if (bits.length == 4)
399
+ val = 0
400
+ bits.each_index { |i| val += ( bits[i] << (96 - (i * 32)) ) }
401
+ return val
402
+ end
403
+
404
+ raise RuntimeError, "Invalid address format"
405
+ end
406
+
407
+ #
408
+ # Converts an integer into a network byte order address
409
+ #
410
+ # @param addr [Numeric] The address as a number
411
+ # @param v6 [Boolean] Whether +addr+ is IPv6
412
+ def self.addr_iton(addr, v6=false)
413
+ if(addr < 0x100000000 && !v6)
414
+ return [addr].pack('N')
415
+ else
416
+ w = []
417
+ w[0] = (addr >> 96) & 0xffffffff
418
+ w[1] = (addr >> 64) & 0xffffffff
419
+ w[2] = (addr >> 32) & 0xffffffff
420
+ w[3] = addr & 0xffffffff
421
+ return w.pack('N4')
422
+ end
423
+ end
424
+
425
+ #
426
+ # Converts a colon-delimited MAC address into a 6-byte binary string
427
+ #
428
+ def self.eth_aton(mac)
429
+ mac.split(":").map{|c| c.to_i(16) }.pack("C*")
430
+ end
431
+
432
+ #
433
+ # Converts a 6-byte binary string into a colon-delimited MAC address
434
+ #
435
+ def self.eth_ntoa(bin)
436
+ bin.unpack("C6").map{|x| "%.2x" % x }.join(":").upcase
437
+ end
438
+
439
+ #
440
+ # Converts a CIDR subnet into an array (base, bcast)
441
+ #
442
+ def self.cidr_crack(cidr, v6=false)
443
+ tmp = cidr.split('/')
444
+
445
+ tst,scope = tmp[0].split("%",2)
446
+ scope = "%" + scope if scope
447
+ scope ||= ""
448
+
449
+ addr = addr_atoi(tst)
450
+
451
+ bits = 32
452
+ mask = 0
453
+ use6 = false
454
+
455
+ if (addr > 0xffffffff or v6 or cidr =~ /:/)
456
+ use6 = true
457
+ bits = 128
458
+ end
459
+
460
+ mask = (2 ** bits) - (2 ** (bits - tmp[1].to_i))
461
+ base = addr & mask
462
+
463
+ stop = base + (2 ** (bits - tmp[1].to_i)) - 1
464
+ return [self.addr_itoa(base, use6) + scope, self.addr_itoa(stop, use6) + scope]
465
+ end
466
+
467
+ #
468
+ # Converts a netmask (255.255.255.240) into a bitmask (28). This is the
469
+ # lame kid way of doing it.
470
+ #
471
+ def self.net2bitmask(netmask)
472
+
473
+ nmask = resolv_nbo(netmask)
474
+ imask = addr_ntoi(nmask)
475
+ bits = 32
476
+
477
+ if (imask > 0xffffffff)
478
+ bits = 128
479
+ end
480
+
481
+ 0.upto(bits-1) do |bit|
482
+ p = 2 ** bit
483
+ return (bits - bit) if ((imask & p) == p)
484
+ end
485
+
486
+ 0
487
+ end
488
+
489
+ #
490
+ # Converts a bitmask (28) into a netmask (255.255.255.240)
491
+ #
492
+ def self.bit2netmask(bitmask, ipv6=false)
493
+ if bitmask > 32 or ipv6
494
+ i = ((~((2 ** (128 - bitmask)) - 1)) & (2**128-1))
495
+ n = SSLScan::Socket.addr_iton(i, true)
496
+ return SSLScan::Socket.addr_ntoa(n)
497
+ else
498
+ [ (~((2 ** (32 - bitmask)) - 1)) & 0xffffffff ].pack('N').unpack('CCCC').join('.')
499
+ end
500
+ end
501
+
502
+
503
+ def self.portspec_crack(pspec)
504
+ portspec_to_portlist(pspec)
505
+ end
506
+
507
+ #
508
+ # Converts a port specification like "80,21-23,443" into a sorted,
509
+ # unique array of valid port numbers like [21,22,23,80,443]
510
+ #
511
+ def self.portspec_to_portlist(pspec)
512
+ ports = []
513
+
514
+ # Build ports array from port specification
515
+ pspec.split(/,/).each do |item|
516
+ start, stop = item.split(/-/).map { |p| p.to_i }
517
+
518
+ start ||= 0
519
+ stop ||= item.match(/-/) ? 65535 : start
520
+
521
+ start, stop = stop, start if stop < start
522
+
523
+ start.upto(stop) { |p| ports << p }
524
+ end
525
+
526
+ # Sort, and remove dups and invalid ports
527
+ ports.sort.uniq.delete_if { |p| p < 1 or p > 65535 }
528
+ end
529
+
530
+ #
531
+ # Converts a port list like [1,2,3,4,5,100] into a
532
+ # range specification like "1-5,100"
533
+ #
534
+ def self.portlist_to_portspec(parr)
535
+ ranges = []
536
+ range = []
537
+ lastp = nil
538
+
539
+ parr.uniq.sort{|a,b| a<=>b}.map{|a| a.to_i}.each do |n|
540
+ next if (n < 1 or n > 65535)
541
+ if not lastp
542
+ range = [n]
543
+ lastp = n
544
+ next
545
+ end
546
+
547
+ if lastp == n - 1
548
+ range << n
549
+ else
550
+ ranges << range
551
+ range = [n]
552
+ end
553
+ lastp = n
554
+ end
555
+
556
+ ranges << range
557
+ ranges.delete(nil)
558
+ ranges.uniq.map{|x| x.length == 1 ? "#{x[0]}" : "#{x[0]}-#{x[-1]}"}.join(",")
559
+ end
560
+
561
+ ##
562
+ #
563
+ # Utility class methods
564
+ #
565
+ ##
566
+
567
+ #
568
+ # This method does NOT send any traffic to the destination, instead, it uses a
569
+ # "bound" UDP socket to determine what source address we would use to
570
+ # communicate with the specified destination. The destination defaults to
571
+ # Google's DNS server to make the standard behavior determine which IP
572
+ # we would use to communicate with the internet.
573
+ #
574
+ def self.source_address(dest='8.8.8.8', comm = ::SSLScan::Socket::Comm::Local)
575
+ begin
576
+ s = self.create_udp(
577
+ 'PeerHost' => dest,
578
+ 'PeerPort' => 31337,
579
+ 'Comm' => comm
580
+ )
581
+ r = s.getsockname[1]
582
+ s.close
583
+
584
+ # Trim off the trailing interface ID for link-local IPv6
585
+ return r.split('%').first
586
+ rescue ::Exception
587
+ return '127.0.0.1'
588
+ end
589
+ end
590
+
591
+ #
592
+ # Identifies the link-local address of a given interface (if IPv6 is enabled)
593
+ #
594
+ def self.ipv6_link_address(intf)
595
+ r = source_address("FF02::1%#{intf}")
596
+ return nil if r.nil? || r !~ /^fe80/i
597
+ r
598
+ end
599
+
600
+ #
601
+ # Identifies the mac address of a given interface (if IPv6 is enabled)
602
+ #
603
+ def self.ipv6_mac(intf)
604
+ r = ipv6_link_address(intf)
605
+ return if not r
606
+ raw = addr_aton(r)[-8, 8]
607
+ (raw[0,3] + raw[5,3]).unpack("C*").map{|c| "%.2x" % c}.join(":")
608
+ end
609
+
610
+ #
611
+ # Create a TCP socket pair.
612
+ #
613
+ # sf: This create a socket pair using native ruby sockets and will work
614
+ # on Windows where ::Socket.pair is not implemented.
615
+ # Note: OpenSSL requires native ruby sockets for its io.
616
+ #
617
+ # Note: Even though sub-threads are smashing the parent threads local, there
618
+ # is no concurrent use of the same locals and this is safe.
619
+ def self.tcp_socket_pair
620
+ lsock = nil
621
+ rsock = nil
622
+ laddr = '127.0.0.1'
623
+ lport = 0
624
+ threads = []
625
+ mutex = ::Mutex.new
626
+
627
+ threads << SSLScan::ThreadFactory.spawn('TcpSocketPair', false) {
628
+ server = nil
629
+ mutex.synchronize {
630
+ threads << SSLScan::ThreadFactory.spawn('TcpSocketPairClient', false) {
631
+ mutex.synchronize {
632
+ rsock = ::TCPSocket.new( laddr, lport )
633
+ }
634
+ }
635
+ server = ::TCPServer.new(laddr, 0)
636
+ if (server.getsockname =~ /127\.0\.0\.1:/)
637
+ # JRuby ridiculousness
638
+ caddr, lport = server.getsockname.split(":")
639
+ caddr = caddr[1,caddr.length]
640
+ lport = lport.to_i
641
+ else
642
+ # Sane implementations where Socket#getsockname returns a
643
+ # sockaddr
644
+ lport, caddr = ::Socket.unpack_sockaddr_in( server.getsockname )
645
+ end
646
+ }
647
+ lsock, _ = server.accept
648
+ server.close
649
+ }
650
+
651
+ threads.each { |t| t.join }
652
+
653
+ return [lsock, rsock]
654
+ end
655
+
656
+ #
657
+ # Create a UDP socket pair using native ruby UDP sockets.
658
+ #
659
+ def self.udp_socket_pair
660
+ laddr = '127.0.0.1'
661
+
662
+ lsock = ::UDPSocket.new
663
+ lsock.bind( laddr, 0 )
664
+
665
+ rsock = ::UDPSocket.new
666
+ rsock.bind( laddr, 0 )
667
+
668
+ rsock.connect( *lsock.addr.values_at(3,1) )
669
+
670
+ lsock.connect( *rsock.addr.values_at(3,1) )
671
+
672
+ return [lsock, rsock]
673
+ end
674
+
675
+
676
+ ##
677
+ #
678
+ # Class initialization
679
+ #
680
+ ##
681
+
682
+ #
683
+ # Initialize general socket parameters.
684
+ #
685
+ def initsock(params = nil)
686
+ if (params)
687
+ self.peerhost = params.peerhost
688
+ self.peerport = params.peerport
689
+ self.localhost = params.localhost
690
+ self.localport = params.localport
691
+ self.context = params.context || {}
692
+ self.ipv = params.v6 ? 6 : 4
693
+ end
694
+ end
695
+
696
+ #
697
+ # By default, all sockets are themselves selectable file descriptors.
698
+ #
699
+ def fd
700
+ self
701
+ end
702
+
703
+ #
704
+ # Returns local connection information.
705
+ #
706
+ def getsockname
707
+ Socket.from_sockaddr(super)
708
+ end
709
+
710
+ #
711
+ # Wrapper around getsockname
712
+ #
713
+ def getlocalname
714
+ getsockname
715
+ end
716
+
717
+ #
718
+ # Return peer connection information.
719
+ #
720
+ def getpeername
721
+ return Socket.from_sockaddr(super)
722
+ end
723
+
724
+ #
725
+ # Returns a string that indicates the type of the socket, such as 'tcp'.
726
+ #
727
+ def type?
728
+ raise NotImplementedError, "Socket type is not supported."
729
+ end
730
+
731
+ #
732
+ # The peer host of the connected socket.
733
+ #
734
+ attr_reader :peerhost
735
+ #
736
+ # The peer port of the connected socket.
737
+ #
738
+ attr_reader :peerport
739
+ #
740
+ # The local host of the connected socket.
741
+ #
742
+ attr_reader :localhost
743
+ #
744
+ # The local port of the connected socket.
745
+ #
746
+ attr_reader :localport
747
+ #
748
+ # The IP version of the socket
749
+ #
750
+ attr_reader :ipv
751
+ #
752
+ # Contextual information that describes the source and other
753
+ # instance-specific attributes. This comes from the param.context
754
+ # attribute.
755
+ #
756
+ attr_reader :context
757
+
758
+ protected
759
+
760
+ attr_writer :peerhost, :peerport, :localhost, :localport # :nodoc:
761
+ attr_writer :context # :nodoc:
762
+ attr_writer :ipv # :nodoc:
763
+
764
+ end
765
+
766
+ end
767
+
768
+ #
769
+ # Globalized socket constants
770
+ #
771
+ SHUT_RDWR = ::Socket::SHUT_RDWR
772
+ SHUT_RD = ::Socket::SHUT_RD
773
+ SHUT_WR = ::Socket::SHUT_WR