Ruby TCPServer always delay on dns reverse lookup? - how to disable? - ruby

I created a TCPServer with ruby gserver.
Everytime I connect remotly to the server, it takes 2-4 seconds until connection is established.
This is only happen if I connect from remote machine.
Connection from same machine has running the service will send immidiate response.
For the connection on same machine there is no difference if I connect via localhost or via the machines ip.
I think delay depends on reverse lookup but can not localize why.
in gserver.rb it is line 263
client = #tcpServer.accept
Here the delay occurs, I do not know what is in this method.
I added all machines which are used during tests to the local hosts file. But that changed nothing.
Same happens when using Webrick, I tried to set also
BasicSocket.do_not_reverse_lookup = true
as well as direct on the resulting server socket
Socket.do_not_reverse_lookup = true
as well as on client connection socket
client.do_not_reverse_lookup = true
But that also changed nothing on delay.
Whenever connection is established, the values of remote_host and remote_ip are resolved and as defined in hosts file.
I tried that running ruby 2.2.1 on ubuntu 14.04 as well as ruby 1.9.3 running debian wheezy.
Same behavior - (long) delay on connecting service.
Q: How to fix that / disable lookup on TCPServer?

The problem depends on my client machine where I run on MAC OSX Mav.
The used telnet client tries to open IPv6 connection and afterwards IPv4.
To solve the delay, just open connection with
telnet -4 my-server 3333
I have build a small connect echo servive where you can check resolves and timings.
If you change NO_REVERSE_LOOKUP you will get IPs or ADDRESSes and if not resolveable, different response times.
require 'socket'
NO_REVERSE_LOOKUP = true
CONNECT_PORT = 3333
puts "#{Time.now} Starting service on port: #{CONNECT_PORT}"
# the full hell - just to test if anything meets what we want
TCPServer.do_not_reverse_lookup = NO_REVERSE_LOOKUP
BasicSocket.do_not_reverse_lookup = NO_REVERSE_LOOKUP
Socket.do_not_reverse_lookup = NO_REVERSE_LOOKUP
srv = TCPServer.open(CONNECT_PORT)
puts "#{Time.now} Waiting for client"
client = srv.accept
puts "#{Time.now} Client connected"
client.do_not_reverse_lookup = NO_REVERSE_LOOKUP
client.print "Hello connected\n"
# in case that we disabled reverse lookup, we should only receive IP Adresses
puts "#{Time.now} Getting server address infos"
puts "SERVER INFO:"
puts NO_REVERSE_LOOKUP ? client.addr(:numeric) : client.addr(:hostname)
puts ""
puts "#{Time.now} Getting remote client infos"
puts "REMOTE INFO:"
puts NO_REVERSE_LOOKUP ? client.peeraddr(:numeric) : client.peeraddr(:hostname)
###
puts "#{Time.now} Closing connection"
client.close
puts "#{Time.now} End"
Thanks to drbrain from #ruby-lang irc for pointing me to the IPv6 problem.

Related

Cannot connect from local machine to TCP server on AWS EC2 (Ubuntu)

I've coded up a basic TCP server/client, taking code from the first example on https://docs.python.org/3/library/socket.html#example
# Echo server program
import socket
HOST = '' # Symbolic name meaning all available interfaces
PORT = 50007 # Arbitrary non-privileged port
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
with conn:
print('Connected by', addr)
while True:
data = conn.recv(1024)
if not data: break
conn.sendall(data)
# Echo client program
import socket
HOST = '1.2.3.4' # The remote host (I change this with my box's IP)
PORT = 50007 # The same port as used by the server
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
data = s.recv(1024)
print('Received', repr(data))
NOTE: I'm using my actual EC2 IP not 1.2.3.4
If I run the client on my local machine and the server on a remote box (hosted by vultr) it works.
But if host the server on an AWS EC2 instance, it doesn't.
I've gone into the Security Group for the EC2 instance, and added inbound+outbound "Custom TCP+UDP" rules. I've tried allowing the specific port the server is using, as well as putting 0-65535.
If I run a client on the same EC2 instance, that works.
I can't think what else to try.
Is anyone able to get this working on EC2?
I was using my AMI's private IP (which I use to ssh into the box).
I needed to use the PUBLIC IP.
It seems on vultr they are the same, which is why it worked.

Connection refused when using Rack Proxy

I'm trying to catch all *.dev requests on port 80 and send them to the right Rack project by using Rack Proxy. I'm able to catch the requests and based on the URI I'll look for a config.ru in a specific folder. When I'm able to find one I'll boot up the server on port 3000.
After that, whenever I recieve a request on port 80, I try to set the HTTP_HOST to localhost:3000, but I'm getting the message Unexpected error while processing request: Connection refused - connect(2) for "localhost" port 3000. I am able to access the application through localhost:3000, but not through a *.dev domain. I already tried using different ports, but that's not working either, so I guess it has something to do with the user that's running it. However, I hope someone can help me with this.
require 'rack-proxy'
class AppProxy < Rack::Proxy
def rewrite_env(env)
request = Rack::Request.new(env)
site = request.host[0..-5]
uid = File.stat(__FILE__).uid
path = Etc.getpwuid(uid).dir + '/Software/Applications/'
front_controller = "#{path}#{site}/config.ru"
if File.file?(front_controller)
system "rackup -p 3000 -D #{front_controller} "
env["HTTP_HOST"] = "localhost:3000"
else
raise Exception.new "Not found"
end
env
end
end
run AppProxy.new
EDIT: I've checked if there is something listening on port 3000. After running the server and sudo lsof -i -n -P | grep TCP I get the following result for the ports 80 and 3000:
ruby 56247 root 10u IPv4 0x727d74bd0b95bd9b 0t0 TCP *:80 (LISTEN)
ruby 56247 root 11u IPv4 0x727d74bd0a3b9bfb 0t0 TCP 127.0.0.1:80->127.0.0.1:52773 (ESTABLISHED)
ruby 56255 root 12u IPv6 0x727d74bd094e3c8b 0t0 TCP [::1]:3000 (LISTEN)
I'm not sure if this is any useful because I don't know the exact meaning of this.
Solved it. The problem was that booting up the server was a bit slower then setting the HTTP_HOST to a different location and port 3000 was not in use yet. Waiting for a second solved the problem. My code looks like this now:
require 'rack-proxy'
class AppProxy < Rack::Proxy
def rewrite_env(env)
request = Rack::Request.new(env)
site = request.host[0..-5]
uid = File.stat(__FILE__).uid
path = Etc.getpwuid(uid).dir + '/Software/Applications/'
front_controller = "#{path}#{site}/config.ru"
if File.file?(front_controller)
system "rackup -D -p 3000 #{front_controller} "
sleep(1)
env["HTTP_HOST"] = "localhost:3000"
else
raise Exception.new "Not found"
end
env
end
end
run AppProxy.new

Ruby TCP chat server

Recently Iv'e been trying to program a simple TCP server to later build into a chat room. But every time I launch the server (server.rb), and then I try to use the client (client.rb) I get this error:
Sam#ANDERSAMERPC C:\Users\Sam\Documents\Coding
> client.rb
C:/Users/Sam/Documents/Coding/client.rb:6:in `initialize': No connection could be made because the target machine actively refused it. - connect(2) for "localhost" port 2001 (Errno::ECONNREFUSED)
from C:/Users/Sam/Documents/Coding/client.rb:6:in `open'
from C:/Users/Sam/Documents/Coding/client.rb:6:in `<main>'
I am using CMD to run this and I've tried turning off firewall briefly.
Here is the code for both of the programs...
This is server.rb
require 'socket'
server = TCPServer.open(2000) # Socket to listen on port 2000
loop {
Thread.start(server.accept) do |client|
client.puts(Time.now.ctime) # Send time to the client
client.puts "Closing connection. Bye!"
client.close
end
}
Here's client.rb:
require 'socket'
hostname = "localhost"
port = 2000
s = TCPSocket.open(hostname, port)
while line = s.gets # Reads lines from socket
puts line.chop # And print with platform line terminator
end
s.close # Close socket when done
(This code is from http://www.tutorialspoint.com/ruby/ruby_socket_programming.htm just so you know.)
I guess there are 2 options:
Run your server.rb before client.rb.
Are you sure those are the exact same files that you are running. Because the error message says it can not connect to port 2001, whereas in both of your files they refer to 2000. Is it possible that you are editing and running different files? You will be surprised, how common this is here on SO :)

Ruby TCPSocket - No connection could be made because the target machine actively refused it

Can't make TCPSocket connect to TCPServer:
Client
require 'socket'
require 'io/console'
TCPSocket.open('127.0.0.1', '2000') { |socket|
#socket.puts('abcdefghijklmn')
puts socket.read
}
Server
require 'socket' # Get sockets from stdlib
server = TCPServer.open(2000) # Socket to listen on port 2000
loop { # Servers run forever
Thread.start(server.accept) do |client|
client.puts(Time.now.ctime) # Send the time to the client
client.puts "Closing the connection. Bye!"
client.close # Disconnect from the client
end
}
Error:
send_socket.rb:4:in `initialize': No connection could be made because the target machine actively refused it. - connect(2) (Errno::ECONNREFUSED)
from send_socket.rb:4:in `open'
from send_socket.rb:4:in `<main>'
When connecting to port 80 where IIS is listeninig, TCPSocket works fine, so I suppose there is something wrong with TCPServer example.
Switching the firewall off doesn't help. Moreover - when I stop IIS and set TCPServer to port 80 error is the same.

Run MySQL query over multiple SSH tunnels?

I have a situation somewhat similar to "How to create a ssh tunnel in ruby and then connect to mysql server on the remote host."
From my local machine, I want to run a query against a production MySQL database host.
My local machine cannot directly connect to the database host. However, if I SSH to a gateway machine, I can run the MySQL client and connect to the database host.
I'm trying to figure out how to programmatically to run a query from my machine that is forwarded to the gateway machine, which forwards it to the database server, and have results returned. Basically I want to have LOCAL => GATEWAY => DB_HOST forwarding.
I have a hacky solution that runs ssh.exec and MySQL on the command line, shown below, but, I'm trying to find a way that does port forwarding twice.
I tried, but haven't been successful so far.
require 'rubygems'
require 'mysql2'
require 'net/ssh/gateway'
gateway = Net::SSH::Gateway.new(
$gateway_host, $gateway_user,:password => $gateway_pass)
# This works
ssh = gateway.ssh($db_host, $db_login_user)
data = ssh.exec!("mysql -u#{$db_user} -p#{$db_pass} #{$db_name} -e '#{$sql_query}'")
puts "#{data.class} is the class type"
puts data
# Want to do something like this with port forwarding
# client = Mysql2::Client.new(:host => $db_host,
# :username => $db_user,
# :password => $db_pass,
# :database => $db_name,
# :port => port)
# results = client.query($sql_query)
puts "end stuff"
ssh.close
Any suggestions?
Your diagram lays it out fairly well, you would need a tunnel from the Gateway to the Db_Host; and a second tunnel from your local machine to the gateway. The two tunnels would effectively connect your local machine to the db_host by way of the gateway.
Here's a reference specific to tunneling MySQL over SSH

Resources