File: socks.rb

package info (click to toggle)
ruby-net-dns 0.9.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 452 kB
  • sloc: ruby: 3,944; makefile: 6
file content (148 lines) | stat: -rw-r--r-- 3,530 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
require 'socket'
require 'ipaddr'

class RawSocket # :nodoc:
  @@id_arr = []

  def initialize(src_addr, dest_addr)
    # Define socket
    begin
      @socket = Socket.new PF_INET, SOCK_RAW, IPPROTO_RAW
    rescue SystemCallError => e
      raise SystemCallError, "You must be root to use raw sockets! #{e}"
    end

    @socket.setsockopt IPPROTO_IP, IP_HDRINCL, 1

    # Checks addresses
    @src_addr  = check_addr src_addr
    @dest_addr = check_addr dest_addr

    # Source and destination port are zero
    @src_port  = 0
    @dest_port = 0

    # Set correct protocol version in the header
    @version = @dest_addr.ipv4? ? "0100" : "0110"

    # Total lenght: must be overridden by subclasses
    @tot_lenght = 20

    # Protocol: must be overridden by subclasses
    @protocol = 1 # ICMP by default

    # Generate a new id
    # @id = genID
    @id = 1234

    # Generate peer sockaddr
    @to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s
  end

  def send(payload = '')
    packet = make_ip_header([
      [@version + '0101', 'B8'],          # version, hlen
      [0, 'C'],                           # tos
      [@tot_lenght + payload.size, 'n'],  # total len
      [@id, 'n'],                         # id
      [0, 'n'],                           # flags, offset
      [64, 'C'],                          # ttl
      [@protocol, 'C'],                   # protocol
      [0, 'n'],                           # checksum
      [@src_addr.to_i, 'N'],              # source
      [@dest_addr.to_i, 'N'],             # destination
    ])
    packet << make_transport_header(payload.size)
    packet << [payload].pack("a*")
    @socket.send(packet, 0, @to)
  end

  private

  def check_addr(addr)
    case addr
    when String
      IPAddr.new(addr)
    when IPAddr
      addr
    else
      raise ArgumentError, "Wrong address format: #{addr}"
    end
  end

  def check_port(port)
    if (1..65_535).cover?(port) && port.is_a?(Integer)
      port
    else
      raise ArgumentError, "Port #{port} not valid"
    end
  end

  def genID
    while @@id_arr.include?(q = rand(65_535))
    end
    @@id_arr.push(q)
    q
  end

  def ipchecksum(data)
    checksum = data.unpack("n*").inject(0) { |s, x| s + x }
    ((checksum >> 16) + (checksum & 0xffff)) ^ 0xffff
  end

  def make_ip_header(parts)
    template = ''
    data = []
    parts.each do |part|
      data += part[0..-2]
      template << part[-1]
    end
    data_str = data.pack(template)
    checksum = ipchecksum(data_str)
    data[-3] = checksum
    data.pack(template)
  end

  def make_transport_header
    ""
  end
end

class UdpRawSocket < RawSocket # :nodoc:
  def initialize(src_addr, src_port, dest_addr, dest_port)
    super(src_addr, dest_addr)

    # Check ports
    @src_port  = check_port src_port
    @dest_port = check_port dest_port

    # Total lenght: must be overridden by subclasses
    @tot_lenght = 20 + 8 # 8 bytes => UDP Header

    # Protocol: must be overridden by subclasses
    @protocol = 17 # UDP protocol

    @to = Socket.pack_sockaddr_in @dest_port, @dest_addr.to_s
  end

  private

  def make_udp_header(parts)
    template = ''
    data = []
    parts.each do |part|
      data += part[0..-2]
      template << part[-1]
    end
    data.pack(template)
  end

  def make_transport_header(pay_size)
    make_udp_header([
      [@src_port, 'n'], # source port
      [@dest_port, 'n'],       # destination port
      [8 + pay_size, 'n'],     # len
      [0, 'n']                 # checksum (mandatory)
    ])
  end
end