Ruby IO from a service at port 6557 in Sinatra - ruby

I have to take a dump of a service in sinatra and display it in the content area of the webpage.
The Service I have to access via code runs on server at port 6557. It doesnt use any encryption or authentication. Its a plain readonly request response thingy like http.
Here is what works in teminal
$ echo "GET hosts" | nc 192.168.1.1 6557
gives me the intended output. I need to do something similar using the sinatra application.
I wrote this code but is grossly incorrect. Can sombody help me with code or lookup materials or examples.
get '/' do
host = "192.168.1.1"
port = 6557
dat = ""
#socket = TCPSocket.open (host, port)
while(true)
if(IO.select([],[],[#socket],0))
socket.close
return
end
begin
while( (data = #socket.recv_nonblock(100)) != "")
dat = dat+ data
end
rescue Errno::EAGAIN
end
begin
#str = "GET hosts"
#socket.puts(#str);
rescue Errno::EAGAIN
rescue EOFError
exit
end
IO.select([#socket], [#socket], [#socket])
end
#line = dat
erb :info
end
The code on execution just hangs up.
Also if possible please give some links to read up to get a conceptual context of the problem.

I think the Ruby equivalent to your shell command should be as simple as:
require "socket"
socket = TCPSocket.new "192.168.1.1", 6557
socket.puts "GET hosts"
socket.read
According to the docs, #read should close the socket automatically, so you don't need to worry about doing that manually.

You can execute shell commands directly from ruby using backticks or the system command. Something like this may work for you:
get "/" do
#line = `echo "GET hosts" | nc 192.168.1.1 6557`
erb :info
end
Check out the ruby docs for Kernel#system for more info.

Related

socket conneted using localhost but not 127.0.0.1

i have the following code
require 'socket'
def connect(socket)
while line = socket.gets # Read lines from socket
puts line # and print them
end
socket.close # close socket when done
end
def serve(server)
loop do
client = server.accept # Wait for a client to connect
client.puts "Hello !"
client.puts "Time is #{Time.now}"
client.close
end
end
if ARGV[0] == "-s"
ip_port = ARGV[1]
server = TCPServer.new ip_port
serve(server)
elsif ARGV.length == 2
ip_address = ARGV[0]
ip_port = ARGV[1]
puts ip_address
puts ip_port
socket = TCPSocket.new ip_address , ip_port
connect(socket)
else
puts "PLease enter an IP address and IP port"
end
The code above is a basic server and client. use the -s flag to tell the program to act like a server.
This code works when I use localhost as a address but does not work when I use 127.0.0.1 as the address. I receive a No connection could be made because the target machine actively refused it. - connect(2) error when i use 127.0.0.1. was wondering if anyone knows what the problem is.
I don't think there's anything wrong with the code itself, this looks like more of an issue due to firewall settings or perhaps something else on the machine.
You could always try opening the port and then attempting to telnet into it from a different terminal telnet 127.0.0.1 port. You can also use netstat -atun to check if the port is indeed open and to which address it is bound (0.0.0.0 means accessible by all IP addresses).

How to test SSH connection using Ruby

This is what I got so far. This works great, the problem being I can't input a password for the ssh login, I need to have shared ssh keys in order for this to work:
def ssh_conn(user, host, &block)
begin
ping_output = []
timeout(20) do
ping_output = IO.popen("ssh #{user}##{host} 'echo \"success\"'", "w+")
end
ping = ping_output.readlines.join[/success/] ? true : false
rescue Timeout::Error
ping = false
rescue
ping = false
end
ping_output.close
if block_given? && ping
yield
end
return ping
end
The question here is: How can I do something similar to this, but with password input through the arguments passed to the method? Preferably using ruby native Classes/Methods without installing any "external" gems.
By searching a bit in StackOverflow I've found this thread
and I was able to solve my problem doing this:
def ssh_try(user, host, pass)
puts "SSHing #{host} ..."
Net::SSH.start( host.to_s, user.to_s, :password => pass.to_s ) do |ssh|
puts ssh.exec!('date')
puts "Logging out..."
end
end
Anyone who is facing a similar problem can try this method, works great to test/use ssh connection in ruby.
I believe you cannot do that with ssh itself, but that's what sshpass it's for, as you can read in this serverfault answer. In Ubuntu:
$ sudo apt-get install sshpass
And then change your IO call like this:
ping_output = IO.popen("sshpass -p ssh #{user}##{host} 'echo \"success\"'", "w+")
An alternative would be to rewrite your code to use Ruby SSH client, such as net-ssh, instead of using the system command. This is actually my recommendation, since it'll allow you to work at a higher abstraction level and not deal with system issues. Also, the result looks more beautiful! Check this (untested) code:
require 'net/ssh'
def ssh_conn(user, host, password, &block)
authentication_successful = Net::SSH::Authentication::Session.authenticate(host, user, password)
authentication_successful && (yield if block_given?)
authentication_successful
end

How to read from a TCPServer socket in ruby using read, readpartial and read_nonblock

I have a 2 part question on reading from sockets and how is it managed on Ruby servers like Unicorn or Mongrel
I've learnt that to read from a socket is different from reading a file and that there are no distinct EOF message sent and the data is an endless stream. So how do you know when to stop reading? My TCPServer for example in this case when I hit my server by accessing http://localhost:9799 from a browser, it hangs after there is no more data to read and it won't throw the EOFError either.
require 'socket'
READ_CHUNK = 1024
socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
addr = Socket.pack_sockaddr_in(9799, '127.0.0.1')
socket.bind(addr)
socket.listen(Socket::SOMAXCONN)
socket.setsockopt(:SOCKET, :REUSEADDR, true)
puts "Server is listening on port = 9799"
loop do
connection, addr_info = socket.accept
data_buffer = ""
loop do
begin
connection.read_nonblock(READ_CHUNK, data_buffer)
puts "Buffer = #{data_buffer}"
rescue Errno::EAGAIN => e
IO.select([connection])
retry
rescue EOFError
break
end
end
connection.write("HTTP/1.1 200 \r\n")
connection.write("Content-Type: text/html\r\n")
connection.write("Status 200 \r\n")
connection.write("Connection: close \r\n")
connection.write("Hello World \r\n")
connection.close
end
I'd like to know whats the best practice/standard approach used by Ruby servers. I see the Unicorn uses read_nonblock from kgio library and mongrel uses readpartial (I'm not sure about these but going through the code this is what I feel is the approach adopted.) Even with checks for \r\n how does the server know the input is complete.
Could explain how this should be done (and I think gets is not the approach - its with read, readpartial, read_nonblock).
2). I would really appreciate a few lines on how this is achieved in servers like unicorn or passenger
Thank you.
It's done in unicorn here
https://github.com/defunkt/unicorn/blob/master/lib/unicorn/http_request.rb#L69-L71
There is add_parse method(read the comments above methods)
https://github.com/defunkt/unicorn/blob/master/ext/unicorn_http/unicorn_http.rl#L760-L778
Also take a look at some explanations here http://www.ruby-forum.com/topic/2267632#1014288
Here is your working code using http_parser.rb https://gist.github.com/4136962
gem install http_parser.rb
require 'socket'
require "http/parser"
READ_CHUNK = 1024 * 4
socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM)
addr = Socket.pack_sockaddr_in(9799, '127.0.0.1')
socket.bind(addr)
socket.listen(Socket::SOMAXCONN)
socket.setsockopt(:SOCKET, :REUSEADDR, true)
puts "Server is listening on port = 9799"
loop do
connection, addr_info = socket.accept
parser = Http::Parser.new
begin
data = connection.readpartial(READ_CHUNK)
puts "Buffer = #{data}"
parser << data
end until parser.headers
connection.write("HTTP/1.1 200 \r\n")
connection.write("Content-Type: text/html\r\n")
connection.write("Status 200 \r\n")
connection.write("Connection: close \r\n")
connection.write("\r\n\r\n")
connection.write("Hello World \r\n")
connection.close
end

How can I only read one line of data from a TCPSocket in Ruby?

I'm using the following code to connect to a network service i'm writing (thats backed by EventMachine) and I'm having a bit of trouble getting into a situation allowing me to use one socket connection to execute multiple commands.
#!/usr/bin/env ruby
require 'socket'
opts = {
:address => "0.0.0.0",
:port => 2478
}
connection = TCPSocket.open opts[:address], opts[:port]
# Get ID
connection.print "ID something"
puts connection.read
# Status
connection.print "STATUS"
puts connection.read
# Close the connection
connection.close
Here's what my EventMachine server hander looks like...
module ConnectionHandler
def receive_data data
send_data "Some output #{data}"
end
end
However, my first ruby script hangs when it executes connection.read as I presume its waiting for the connection to close so it knows its got all of the data? This is not what I want to happen.
My socket server will just take one command (on one line) and return one line of output.
Any ideas how I can do this? Thanks.
It turns out the connection.gets method will return a line of data received if the server sends a response ending in a \n character. So I just added \n to the end of my send_data call and switch to using puts connection.gets and it worked great!

Ruby - See if a port is open

I need a quick way to find out if a given port is open with Ruby. I currently am fiddling around with this:
require 'socket'
def is_port_open?(ip, port)
begin
TCPSocket.new(ip, port)
rescue Errno::ECONNREFUSED
return false
end
return true
end
It works great if the port is open, but the downside of this is that occasionally it will just sit and wait for 10-20 seconds and then eventually time out, throwing a ETIMEOUT exception (if the port is closed). My question is thus:
Can this code be amended to only wait for a second (and return false if we get nothing back by then) or is there a better way to check if a given port is open on a given host?
Edit: Calling bash code is acceptable also as long as it works cross-platform (e.g., Mac OS X, *nix, and Cygwin), although I do prefer Ruby code.
Something like the following might work:
require 'socket'
require 'timeout'
def is_port_open?(ip, port)
begin
Timeout::timeout(1) do
begin
s = TCPSocket.new(ip, port)
s.close
return true
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
return false
end
end
rescue Timeout::Error
end
return false
end
All other existing answer are undesirable. Using Timeout is discouraged. Perhaps things depend on ruby version. At least since 2.0 one can simply use:
Socket.tcp("www.ruby-lang.org", 10567, connect_timeout: 5) {}
For older ruby the best method I could find is using non-blocking mode and then select. Described here:
https://spin.atomicobject.com/2013/09/30/socket-connection-timeout-ruby/
More Ruby idiomatic syntax:
require 'socket'
require 'timeout'
def port_open?(ip, port, seconds=1)
Timeout::timeout(seconds) do
begin
TCPSocket.new(ip, port).close
true
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
false
end
end
rescue Timeout::Error
false
end
I recently came up with this solution, making use of the unix lsof command:
def port_open?(port)
!system("lsof -i:#{port}", out: '/dev/null')
end
Just for completeness, the Bash would be something like this:
$ netcat $HOST $PORT -w 1 -q 0 </dev/null && do_something
-w 1 specifies a timeout of 1 second, and -q 0 says that, when connected, close the connection as soon as stdin gives EOF (which /dev/null will do straight away).
Bash also has its own built-in TCP/UDP services, but they are a compile-time option and I don't have a Bash compiled with them :P
My slight variation to Chris Rice's answer. Still handles timing out on a single attempt but also allows multiple retries until you give up.
def is_port_open?(host, port, timeout, sleep_period)
begin
Timeout::timeout(timeout) do
begin
s = TCPSocket.new(host, port)
s.close
return true
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
sleep(sleep_period)
retry
end
end
rescue Timeout::Error
return false
end
end
All *nix platforms:
try nc / netcat command as follow.
`nc -z -w #{timeout_in_seconds} -G #{timeout_in_seconds} #{host} #{port}`
if $?.exitstatus == 0
#port is open
else
#refused, port is closed
end
The -z flag can be used to tell nc to report open ports, rather than initiate a connection.
The -w flag means Timeout for connects and final net reads
The -G flag is connection timeout in seconds
Use -n flag to work with IP address rather than hostname.
Examples:
# `nc -z -w 1 -G 1 google.com 80`
# `nc -z -w 1 -G 1 -n 123.234.1.18 80`
My solution is derived from the posted solutions.
require 'socket'
def is_port_open?(ip, port)
begin
s = Socket.tcp(ip, port, connect_timeout: 5)
s.close
return true
rescue => e
# possible exceptions:
# - Errno::ECONNREFUSED
# - Errno::EHOSTUNREACH
# - Errno::ETIMEDOUT
puts "#{e.class}: #{e.message}"
return false
end
end

Resources