Class: PosixPsutil::PsutilHelper::Connection

Inherits:
Object
  • Object
show all
Includes:
NetworkConstance
Defined in:
lib/posixpsutil/linux/helper.rb

Overview

A wrapper on top of /proc/net/* files, retrieving per-process and system-wide open connections (TCP, UDP, UNIX) similarly to “netstat -an”.

Note: in case of UNIX sockets we’re only able to determine the local endpoint/path, not the one it’s connected to. According to [1] it would be possible but not easily.

1

serverfault.com/a/417946

Constant Summary

Constants included from NetworkConstance

NetworkConstance::AF_INET, NetworkConstance::AF_INET6, NetworkConstance::AF_UNIX, NetworkConstance::CONN_CLOSE, NetworkConstance::CONN_CLOSE_WAIT, NetworkConstance::CONN_CLOSING, NetworkConstance::CONN_ESTABLISHED, NetworkConstance::CONN_FIN_WAIT1, NetworkConstance::CONN_FIN_WAIT2, NetworkConstance::CONN_LAST_ACK, NetworkConstance::CONN_LISTEN, NetworkConstance::CONN_NONE, NetworkConstance::CONN_SYN_RECV, NetworkConstance::CONN_SYN_SENT, NetworkConstance::CONN_TIME_WAIT, NetworkConstance::SOCK_DGRAM, NetworkConstance::SOCK_STREAM, NetworkConstance::TCP_STATUSES

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConnection



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/posixpsutil/linux/helper.rb', line 81

def initialize()
  # proc_filename, family, type
  tcp4 = ["tcp", AF_INET, SOCK_STREAM]
  tcp6 = ["tcp6", AF_INET6, SOCK_STREAM]
  udp4 = ["udp", AF_INET, SOCK_DGRAM]
  udp6 = ["udp6", AF_INET6, SOCK_DGRAM]
  unix = ["unix", AF_UNIX, nil]
  @tmap = {
    all: [tcp4, tcp6, udp4, udp6, unix],
    tcp: [tcp4, tcp6],
    tcp4: [tcp4],
    tcp6: [tcp6],
    udp: [udp4, udp6],
    udp4: [udp4],
    udp6: [udp6],
    unix: [unix],
    inet: [tcp4, tcp6, udp4, udp6],
    inet4: [tcp4, udp4],
    inet6: [tcp6, udp6]
  }
end

Instance Attribute Details

#tmapObject (readonly)

Returns the value of attribute tmap.



79
80
81
# File 'lib/posixpsutil/linux/helper.rb', line 79

def tmap
  @tmap
end

Instance Method Details

#decode_address(addr, family) ⇒ Object

Accept an “ip:port” address as displayed in /proc/net/* and convert it into a human readable form, like: “0500000A:0016” -> (“10.0.0.5”, 22) “0000000000000000FFFF00000100007F:9E49” -> (“::ffff:127.0.0.1”, 40521) The IP address portion is a little or big endian four-byte hexadecimal number; that is, the least significant byte is listed first, so we need to reverse the order of the bytes to convert it to an IP address. The port is represented as a two-byte hexadecimal number. Reference: linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/posixpsutil/linux/helper.rb', line 180

def decode_address(addr, family)
  ip, port = addr.split(':')
  # this usually refers to a local socket in listen mode with
  # no end-points connected
  return [] if !port || port == '0000'
  # convert /proc style ip and port 
  # to addrinfo string according to family and endian
  if '\x00\x01'.unpack('S') == '\x00\x01'.unpack('S<') # little endian
    # first going decoding the hexadecimal
    # then converting to 32 bit integers in small endian
    # encoding these integers with big endian
    # encoding the result in hexadecimal
    ip = [ip].pack('H*').unpack('N*').pack('V*').unpack('H*').first.hex
  end
  port = port.to_i 16
  ip = IPAddr.new(ip, family).to_s
  [ip, port]
end

#process_inet(fn, family, type, inodes, filter_pid = nil) ⇒ Object



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
# File 'lib/posixpsutil/linux/helper.rb', line 104

def process_inet(fn, family, type, inodes, filter_pid=nil)
  # if IPv6 not supported
  return [] if fn.end_with?('6') && !File.exists?(fn)
  f = File.new(fn)
  f.readline()
  ret = []
  f.readlines.each do |line|
    line = line.split(' ')
    inode = line[9].to_i
    if inodes.key?(inode)
      # We assume inet sockets are unique, 
      # but actually there are some sockets share the same inode
      pid, fd = inodes[inode][0]
    else
      #  set pid to nil and fd to -1 for those inodes without relative pid/fd. 
      #  Mostly because Permission denied
      pid, fd = nil, -1
    end

    if filter_pid.nil? || filter_pid == pid
      inet_list = {
        inode: inode, laddr: decode_address(line[1], family), 
        raddr: decode_address(line[2], family), family: family, 
        type: type, status: CONN_NONE, pid: pid, fd: fd
      }
      inet_list[:status] = TCP_STATUSES[line[3]] if type == SOCK_STREAM
      inet_list = OpenStruct.new inet_list
      ret.push(inet_list)
    end # if inode included
  end # each lines
  ret
end

#process_unix(fn, family, inodes, filter_pid = nil) ⇒ Object

parse /proc/net/unix



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/posixpsutil/linux/helper.rb', line 138

def process_unix(fn, family, inodes, filter_pid=nil)
  f = File.new(fn)
  f.readline()
  ret = []
  f.readlines.each do |line|
    line = line.split(' ')
    inode = line[6].to_i
    #  set pid to nil and fd to -1 for those inodes without relative pid/fd. 
    #  Mostly because Permission denied
    if inodes.key?(inode)
      pairs = inodes[inode]
    else
      pairs = [[nil, -1]]
    end

    pairs.each do |pid, fd|
      if filter_pid.nil? || filter_pid == pid
        inet_list = {
          inode: inode, raddr: nil, family: family, type: line[4].to_i, 
          status: CONN_NONE, laddr: '', fd: fd, pid: pid
        }
        inet_list[:laddr] = line[-1] if line.size == 8
        inet_list = OpenStruct.new inet_list
        ret.push(inet_list)
      end # if inode included
    end

  end # each lines
  ret
end