I can reliably get a Winsock socket to connect() to itself if I connect to localhost with a port in the range of automatically assigned ephemeral ports (5000–65534). Specifically, Windows appears to have a system-wide rolling port number which is the next port that it will try to assign as a local port number for a client socket. If I create sockets until the assigned number is just below my target port number, and then repeatedly create a socket and attempt to connect to that port number, I can usually get the socket to connect to itself.
I first got it to happen in an application that repeatedly tries to connect to a certain port on localhost, and when the service is not listening it very rarely successfully establishes a connection and receives the message that it initially sent (which happens to be a Redis PING command).
An example, in Python (run with nothing listening to the target port):
import socket
TARGET_PORT = 49400
def mksocket():
return socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
while True:
sock = mksocket()
sock.bind(('127.0.0.1', 0))
host, port = sock.getsockname()
if port > TARGET_PORT - 10 and port < TARGET_PORT:
break
print port
while port < TARGET_PORT:
sock = mksocket()
err = None
try:
sock.connect(('127.0.0.1', TARGET_PORT))
except socket.error, e:
err = e
host, port = sock.getsockname()
if err:
print 'Unable to connect to port %d, used local port %d: %s' % (TARGET_PORT, port, err)
else:
print 'Connected to port %d, used local port %d' (TARGET_PORT, port)
On my Mac machine, this eventually terminates with Unable to connect to port 49400, used local port 49400. On my Windows 7 machine, a connection is successfully established and it prints Connected to port 49400, used local port 49400. The resulting socket receives any data that is sent to it.
Is this a bug in Winsock? Is this a bug in my code?
Edit: Here is a screenshot of TcpView with the offending connection shown:
This appears to be a 'simultaneous initiation' as described in #3.4 of RFC 793. See Figure 8. Note that neither side is in state LISTEN at any stage. In your case, both ends are the same: that would cause it to work exactly as described in the RFC.
It is a logic bug in your code.
First off, only newer versions of Windows use 5000–65534 as ephemeral ports. Older versions used 1025-5000 instead.
You are creating multiple sockets that are explicitly bound to random ephemeral ports until you have bound a socket that is within 10 ports less than your target port. However, if any of those sockets happen to actually bind to the actual target port, you ignore that and keep looping. So you may or may end up with a socket that is bound to the target port, and you may or may not end up with a final port value that is actually less than the target port.
After that, if port happens to be less than your target port (which is not guaranteed), you are then creating more sockets that are implicitly bound to different random available ephemeral ports when calling connect() (it does an implicit bind() internally if bind() has not been called yet), none of which will be the same ephemeral ports that you explicitly bound to since those ports are already in use and cannot be used again.
At no point do you have any given socket connecting from an ephemeral port to the same ephemeral port. And unless another app happens to have bound itself to your target port and is actively listening on that port, then there is no way that connect() can be successfully connecting to the target port on any of the sockets you create, since none of them are in the listening state. And getsockname() is not valid on an unbound socket, and a connecting socket is not guaranteed to be bound if connect() fails. So the symptoms you think are happening are actually physically impossible given the code you have shown. Your logging is simply making the wrong assumptions and thus is logging the wrong things, giving you a false state of being.
Try something more like this instead, and you will see what the real ports are:
import socket
TARGET_PORT = 49400
def mksocket():
return socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
while True:
sock = mksocket()
sock.bind(('127.0.0.1', 0))
host, port = sock.getsockname()
print 'Bound to local port %d' % (port)
if port > TARGET_PORT - 10 and port < TARGET_PORT:
break
if port >= TARGET_PORT:
print 'Bound port %d exceeded target port %d' % (port, TARGET_PORT)
else:
while port < TARGET_PORT:
sock = mksocket()
# connect() would do this internal anyway, so this is just to ensure a port is available for logging even if connect() fails
sock.bind(('127.0.0.1', 0))
err = None
try:
sock.connect(('127.0.0.1', TARGET_PORT))
except socket.error, e:
err = e
host, port = sock.getsockname()
if err:
print 'Unable to connect to port %d using local port %d' % (TARGET_PORT, port)
else:
print 'Connected to port %d using local port %d' % (TARGET_PORT, port)
Related
I would like to obtain local IP address on receiving UDP packets.
packetConn, _ := net.ListenPacket("udp", ":12345")
n, remoteAddr, _ := packetConn.ReadFrom(buf)
My server has secondary IP addresses and I would like to know to which address each client sends UDP packets.
packetConn.ReadFrom can get remote IP address, but I would like to get the local IP address of the UDP.
I tried packetConn.LocalAddr(), but this returns only local port number like :12345.
How can I get local IP address of UDP packet?
LocalAddr effectively returns the address value passed to ListenPacket. If this is not specific enough for you, you'll need to create multiple listeners, one for each IP address on your server.
I'm trying to write my own implementation of UDP in ruby for educational purposes using raw sockets.
Here's what I have so far:
require 'socket'
addr = Socket.pack_sockaddr_in(4567, '127.0.0.1')
socket = Socket.new(
Socket::PF_INET,
Socket::SOCK_RAW,
Socket::IPPROTO_RAW
)
socket.bind(addr)
socket.recvfrom(1024)
I am testing it like so:
require 'socket'
udp = UDPSocket.new
udp.send "Hello World", 0, "127.0.0.1", 4567
But the call to recvfrom is blocking indefinitely.
If I change it to this:
socket = Socket.new(
Socket::PF_INET,
Socket::SOCK_DGRAM,
Socket::IPPROTO_UDP
)
It of course works because this is the system-level way to accept UDP packets.
How can I receive UDP packets on a raw socket?
To be clear: I want to handle the actual UDP protocol (decoding the datagram & doing a checksum) myself!
Raw sockets work at IP level. You cannot bind a raw socket to a port. You can bind to a local address with bind or to an interface by setting the proper socket options (I don't know how to do it in Ruby, in C you call setsockopt. Instead of IPPROTO_RAW you should use IPPROTO_UDP in protocol.You will receive all UDP datagrams received on that IP address or that interface if the socket is bound to it.
I'm having trouble with udp broadcast transactions under boost::asio, related to the following code snippet. Since I'm trying to broadcast in this instance, so deviceIP = "255.255.255.255". devicePort is a specified management port for my device. I want to use an ephemeral local port, so I would prefer if at all possible not to have to socket.bind() after the connection, and the code supports this for unicast by setting localPort = 0.
boost::asio::ip::address_v4 targetIP = boost::asio::ip::address_v4::from_string(deviceIP);
m_targetEndPoint = boost::asio::ip::udp::endpoint(targetIP, devicePort);
m_ioServicePtr = boost::shared_ptr<boost::asio::io_service>(new boost::asio::io_service);
m_socketPtr = boost::shared_ptr<boost::asio::ip::udp::socket>(new boost::asio::ip::udp::socket(*m_ioServicePtr));
m_socketPtr->open(m_targetEndPoint.protocol());
m_socketPtr->set_option(boost::asio::socket_base::broadcast(true));
// If no local port is specified, default parameter is 0
// If local port is specified, bind to that port.
if(localPort != 0)
{
boost::asio::ip::udp::endpoint localEndpoint(boost::asio::ip::address_v4::any(), localPort);
m_socketPtr->bind(localEndpoint);
}
if(m_forceConnect)
m_socketPtr->connect(m_targetEndPoint);
this->AsyncReceive(); // Register Asynch Recieve callback and buffer
m_socketThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&MyNetworkBase::RunSocketThread, this))); // Start thread running io_service process
No matter what I do in terms of the following settings, the transmit is working fine, and I can use Wireshark to see the response packets coming back from the device as expected. These response packets are also broadcasts, as the device may be on a different subnet to the pc searching for it.
The issues are extremely strange to my mind, but are as follows:
If I specify the local port and set m_forceConnect=false, everything works fine, and my recieve callback fires appropriately.
If I set m_forceConnect = true in the constructor, but pass in a local port of 0, the transmit works fine, but my receive callback never fires. I would assume this is because the 'target' (m_targetEndpoint) is 255.255.255.255, and since the device has a real IP, the response packet gets filtered out.
(what I actually want) If m_forceConnect = false (and data is transmitted using a send_to call), and local port = 0, therefore taking an ephemeral port, my RX callback immediately fires with an error code 10022, which I believe is an "Invalid Argument" socket error.
Can anyone suggest why I can't use the connection in this manner (not explicitly bound and not explicitly connected)? I obviously don't want to use socket.connect() in this case, as I want to respond to anything I receive. I also don't want to use a predefined port, as I want the user to be able to construct multiple copies of this object without port conflicts.
As some people may have noticed, the overall aim of this is to use the same network-interface base-class to handle both the unicast and broadcast cases. Obviously for the unicast version, I can perfectly happily m_socket->connect() as I know the device's IP, and I receive the responses since they're from the connected IP address, therefore I set m_forceConnect = true, and it all just works.
As all my transmits use send_to, I have also tried to socket.connect(endpoint(ip::addressv4::any(), devicePort), but I get a 'The requested address is not valid in its context' exception when I try it.
I've tried a pretty serious hack:
boost::asio::ip::udp::endpoint localEndpoint(boost::asio::ip::address_v4::any(), m_socketPtr->local_endpoint().port());
m_socketPtr->bind(localEndpoint);
where I extract the initial ephemeral port number and attempt to bind to it, but funnily enough that throws an Invalid Argument exception when I try and bind.
OK, I found a solution to this issue. Under linux it's not necessary, but under windows I discovered that if you are neither binding nor connecting, you must have transmitted something before you make the call to asynch_recieve_from(), the call to which is included within my this->asynch_receive() method.
My solution, make a dummy transmission of an empty string immediately before making the asynch_receive call under windows, so the modified code becomes:
m_socketPtr->set_option(boost::asio::socket_base::broadcast(true));
// If no local port is specified, default parameter is 0
// If local port is specified, bind to that port.
if(localPort != 0)
{
boost::asio::ip::udp::endpoint localEndpoint(boost::asio::ip::address_v4::any(), localPort);
m_socketPtr->bind(localEndpoint);
}
if(m_forceConnect)
m_socketPtr->connect(m_targetEndPoint);
// A dummy TX is required for the socket to acquire the local port properly under windoze
// Transmitting an empty string works fine for this, but the TX must take place BEFORE the first call to Asynch_receive_from(...)
#ifdef WIN32
m_socketPtr->send_to(boost::asio::buffer("", 0), m_targetEndPoint);
#endif
this->AsyncReceive(); // Register Asynch Recieve callback and buffer
m_socketThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&MyNetworkBase::RunSocketThread, this)));
It's a bit of a hack in my book, but it is a lot better than implementing all the requirements to defer the call to the asynch recieve until after the first transmission.
Say a multi-horned PC has two network interface with IP address 192.168.1.100/24 and 192.168.2.100/24. If any other PC send a multicast message to any of them, I need to send a response back, but in the UDP data part of the response, I need to tell that PC my proper IP address which they can reach.
Say 192.168.1.123 send that multicast message, I can use ip route get to 192.168.1.123 to determine which one is the proper interface to use(which is 192.168.1.100), but I don't know how to do this in ruby code.
To invoke external process then parse the stdout is not what I want, cause it's too slow for a busy network, and to make it cross-platform is not that easy.
Currently I mimic the route resolving with these codes:
localifs = Socket.ip_address_list.keep_if { |addr| addr.ipv4? and not(addr.ipv4_loopback?) }
raise Error, "There is no alive ipv4 interface on local machine" if localifs.empty?
src_addr_i = IPAddr.new(src_addr).to_i
localifs = localifs.sort do |x,y|
(IPAddr.new(x.ip_address).to_i ^ src_addr_i) <=> (IPAddr.new(y.ip_address).to_i ^ src_addr_i)
end
host = localifs.first.ip_address
If you call DRb.start_service(nil, some_obj) and then DRb.uri, you get back the local URI, including a port number, that another process can use to make calls.
I'm looking to just have some code find a random available port and return that port number, instead of starting up a full-fledged DRb service. Is there a simple way to do this in Ruby?
Haven't tried it, but this may work.
From http://wiki.tcl.tk/2230
The process can let the system
automatically assign a port. For
both the Internet domain and the XNS
domain, specifying a port number of
0 before calling bind() requests the
system to do this.
Also see http://www.ruby-doc.org/stdlib/libdoc/socket/rdoc/classes/Socket.html#M003723
require 'socket'
# use Addrinfo
socket = Socket.new(:INET, :STREAM, 0)
socket.bind(Addrinfo.tcp("127.0.0.1", 0))
p socket.local_address #=> #<Addrinfo: 127.0.0.1:2222 TCP>
Note the use port 0 in socket.bind call. Expected behavior is the local_address will contain the random open port.
You can try random-port, a simple Ruby gem (I'm the author):
require 'random-port'
port = RandomPort::Pool.new.acquire
The best way, though, is to use the block:
RandomPort::Pool.new.acquire do |port|
# Use the port, it will be returned back
# to the pool afterward.
end
The pool is thread-safe and guarantees that the port won't be used by another thread or anywhere else in the app, until it's released.