The laptops in our company go to the network either about LAN (workplace) or about WLAN (conference room). Depending on how they go to the net, they get from DHCP different IPs.
Certain Perl application on a server, copies files from the client (e.g. laptop above) with Net::FTP. The piece of code looks like this:
# don't wait for ftp-timeout if the host is not reachable
my $alive = Net::Ping::External(host => $clnt_host);
if ($alive) {
$ftp = Net::FTP->new($clnt_host, Debug => 0, Timeout => 200)
or return "Cannot connect to $clnt_host: $#\n";
....
....
}
else {
dbgout(1, "Host $clnt_host unreachable.\n");
$st = "'FTPGETFAILED'";
return ($st);
}
Sometimes the code above doesn't work: Net::Ping::External() returns "alive", but Net::FTP->new() gets a "timeout".
Obviously "FTP" and "ping" resolve the hostname differently.
On the OS ping reslove as follows:
C:\Users\converter>ping -n 1 lap314-034
Ping wird ausgeführt für lap314-034.domain.de [10.140.12.110] mit 32 Bytes Daten:
Antwort von 10.140.12.110: Bytes=32 Zeit=2ms TTL=127
However, "nslookup" returns 2 possibilities:
C:\Users\converter>nslookup lap314-034
Server: domaincontroller.domain.de
Address: 123.123.123.123
Name: lap314-034.domain.de
Addresses: 10.192.3.145
10.140.12.110
The not active IP address is delivered from nslookup at the first place back.
I suppose that Net::FTP also uses this address to connect to the client.
How can I "convince" FTP to use the active DNS entry for the connection?
=============================================================
Thanks for your answers. I followed your suggestions. The solution bases on: http://code.activestate.com/lists/perl-win32-users/32624/
#------------------------------------------------------------------
sub getActiveIP {
#------------------------------------------------------------------
my $hostname = shift;
my $host = (gethostbyname ($hostname))[0] or return undef;
my #addr = gethostbyname ($host);
# delete the first 4 array elements
splice #addr, 0, 4;
foreach (#addr) {
my $IPstr = sprintf ("%s", &inet_ntoa ($_));
my $alive = ping(host => $IPstr);
if ($alive) {
return $IPstr;
}
}
return undef;
}
Nevertheless, I believe that a widespread Perl-library should not make such surprises to the user.
How can I "convince" FTP to use the active DNS entry for the connection?
There is no good way to let Net::FTP decide it.
Instead you should determine it outside of Net::FTP and then use the usable IP address instead of the hostname in Net::FTP.
Maybe you would be able to use only Net::FTP with the new versions (Net::FTP > 3.0) which can use IO::Socket::IP instead of IO::Socket::INET as the underlying module. This module can try all the IP addresses returned for a hostname until it gets a successful connection. But these retries will be done for every connection, that is the control connection and every data transfer. Since the connection to the inactive host only fails after some timeout everything will just be horribly slow.
The solution seems obvious: Get the IP addresses, ping them, figure out which one is live, and use the IP address instead of host name in the Net::FTP constructor.
Related
(Edited to provide a reduced test case as per comments below)
I'm facing an odd situation, where if I fork a "WSS" connection to send a message, the socket gets closed when the child exits. However, when I fork to process a "WS" connection, the connection remains open when the child exits.
Server Details: Perl 5.26, Ubuntu 16
Client Details: Perl 5.18, OSX
Server code:
use Net::WebSocket::Server;
use IO::Socket::SSL;
$SIG{CHLD}='IGNORE';
my $enable_ssl = 1; # If you make this one the problem reveals itself
# you need to point this to your own certs
my $ssl_cert_file = "/etc/letsencrypt/live/mydomain/fullchain.pem";
my $ssl_key_file = "/etc/letsencrypt/live/mydomain/privkey.pem";
# To show the problem, all I'm doing is I'm forking and sending current time
sub process {
my $serv = shift;
my $pid = fork();
if ($pid == 0 ) {
print ("fork start\n");
$_->send_utf8(time) for $serv->connections;
print ("fork end\n");
exit 0;
}
}
my $ssl_server;
if ($ssl_enable) {
$ssl_server = IO::Socket::SSL->new(
Listen => 10,
LocalPort => 9000,
Proto => 'tcp',
Reuse => 1,
ReuseAddr => 1,
SSL_cert_file => $ssl_cert_file,
SSL_key_file => $ssl_key_file
);
}
Net::WebSocket::Server->new(
listen => $enable_ssl? $ssl_server: 9000,
tick_period=>5,
on_tick=> sub {
my ($serv) = #_;
process($serv);
#$_->send_utf8(time) for $serv->connections;
},
)->start;
Here is the client code:
my $client = AnyEvent::WebSocket::Client->new;
# replace with your server
$client->connect("wss://myserver:9000")->cb(sub {
our $connection = eval { shift->recv };
if($#) {
print ("connection error");
warn $#;
return;
}
# recieve message from the websocket...
$connection->on(each_message => sub {
my($connection, $message) = #_;
my $msg = $message->body;
print ("GOT $msg\n");
});
});
AnyEvent->condvar->recv;
Expected behavior
The client will keep displaying timestamps
Observed behavior
The client gets the very first message and prints it.
When the server exits its fork, the client stops getting any more messages and the connection terminates
How to make it work
We have two options:
Don't fork in server. Send the message directly in process sub
Don't use SSL
Therefore, my conclusion is SSL+fork == problem.
Thoughts?
Therefore, my conclusion is SSL+fork == problem.
Yes, the problem is first doing the SSL handshake and then forking. This way a user space SSL state will be created in the parent and with the fork duplicated in the child and these two SSL states get out of sync on the first SSL data send or received. This means it is not possible to deal with the same SSL socket from two processes.
If it is really necessary that both parent and child process use the same SSL connection to the peer than the child must use the parent as a "proxy", i.e. the child does not communicate directly with the SSL peer but the child needs to communicate in plain with the parent (for example by using a socketpair) which then can forward the communication to the SSL peer. This way the SSL state is only maintained in the parent process.
But given that only a single message should be handled at a time for a single connection it might be possible instead to not fork for a single tick but fork a child for each connection which handles then all messages in this connection. In this case the SSL handshake can be done in full in the child by listening in the parent to a TCP and not SSL socket, forking on_connect and then upgrading the connection to SSL in the client using IO::Socket::start_SSL. This would also have the advantage that the blocking SSL handshake (which involves several round trips and thus takes some time) would be done in the forked child and would not make the parent block.
I was following a tutorial called "Black Hat Python" and got a "the requested address is not valid in its context" error. I'm Python IDE version: 2.7.12
This is my code:
import socket
import threading
bind_ip = "184.168.237.1"
bind_port = 21
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((bind_ip,bind_port))
server.listen(5)
print "[*] Listening on %s:%d" % (bind_ip,bind_port)
def handle_client(client_socket):
request = client_socket.rev(1024)
print "[*] Recieved: %s" % request
client_socket.close()
while True:
client,addr = server.accept()
print "[*] Accepted connection from: %s:%d" % (addr[0],addr[1])
client_handler = threading.Thread(target=handle_client,args=(client,))
client_handler.start()
and this is my error:
Traceback (most recent call last):
File "C:/Python34/learning hacking.py", line 9, in <module>
server.bind((bind_ip,bind_port))
File "C:\Python27\lib\socket.py", line 228, in meth
return getattr(self._sock,name)(*args)
error: [Errno 10049] The requested address is not valid in its context
>>>
You are trying to bind to an IP address that is not actually assigned to your network interface:
bind_ip = "184.168.237.1"
See the Windows Sockets Error Codes documentation:
WSAEADDRNOTAVAIL 10049
Cannot assign requested address.
The requested address is not valid in its context. This normally results from an attempt to bind to an address that is not valid for the local computer.
That may be an IP address that your router is listening to before using NAT (network address translation) to talk to your computer, but that doesn't mean your computer sees that IP address at all.
Either bind to 0.0.0.0, which will use all available IP addresses (both localhost and any public addresses configured):
bind_ip = "0.0.0.0"
or use any address that your computer is configured for; run ipconfig /all in a console to see your network configuration.
You probably also don't want to use ports < 1024; those are reserved for processes running as root only. You'll have to pick a higher number than that if you want to run an unprivileged process (and in the majority of tutorials programs, that is exactly what you want):
port = 5021 # arbitrary port number higher than 1023
I believe the specific tutorial you are following uses BIND_IP = '0.0.0.0' and BIND_PORT = 9090.
I was just getting this error while following this Python TCP example and the solution was to have my client connect using 'localhost' instead of '0.0.0.0'.
I'm writing a short script to query domain names from their respective whois servers - while in most cases, while the TCP connection via port 43 seems to be working for most whois servers, the queries to whois.markmonitor.com seems to be failing with an odd error that says Invalid query.
Here's the barebones of what I'm using:
#!/usr/bin/perl
#whois.pl
use strict;
use IO::Socket;
my $domain_name = "google.com";
my $query_socket = new IO::Socket::INET(
PeerAddr => 'whois.iana.org',
PeerPort => 43,
Proto => 'tcp');
print $query_socket "$domain_name ";
print $query_socket "\n\r";
while(my $this_line = <$query_socket>) {
print $this_line;
}
close($query_socket);
As seen above, the whois server used is whois.iana.org; this also works as expected with whois.internic.net as well. Only in the case of whois.markmonitor.com, the following error is seen:
$ perl whois.pl
Invalid query
Could someone help shed more light on how can I perhaps get a more verbose output to check if there are any errors in the query that is being made to the server?
As an added test, a normal connection via telnet seems to be working as expected as seen below:
$ telnet whois.markmonitor.com 43
Trying 64.124.14.21...
Connected to whois.markmonitor.com.
Escape character is '^]'.
google.com
Domain Name: google.com
Registry Domain ID: 2138514_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.markmonitor.com
Registrar URL: http://www.markmonitor.com
Updated Date: 2015-06-12T10:38:52-0700
Creation Date: 1997-09-15T00:00:00-0700
......<output truncated>......
which leads me to believe that the actual connections to the server via port 43 are being accepted on the server's side.
As artistoex notes - it's because there's a space in your domain name.
Change your print line to:
print {$query_socket} "$domain_name\n";
(Note - the curly braces are for style reasons and can be omitted - I prefer them to make clear this is a file handle)
Per RFC3912 the client is expected to communicate like that in whois protocol: "All requests are terminated with ASCII CR and then ASCII LF"
So in your code, instead of "\n\r" please use "\r\n". And remove the extra space like written in other replies.
Note however that whoisis not a well defined structured protocol: do not expect all whois servers to work in the same way nor to adhere to some kind of standards. You will find a lot of strange cases...
I'm currently starting a Tcl socket server like this:
socket -server Server 0
This lets the operating system pick an available port to start listening on. The question is that I don't want it to pick any port between 1025 and 64k, instead want to know if I can specify a range of ports? Something like this:
socket -server Server 40000-41000
And then the operating system would pick an available port between 40000 and 41000 for the server to listen on. Is there a way to do this? I can't find it in the Tcl API, but is there some nice API call way to do it rather than iterating through the port range until finding an available port?
The OS itself doesn't provide an API capable of doing that, and Tcl doesn't wrap one up for you as it is actually a pretty rare requirement. Conventionally, servers either listen on specific ports (so clients can know exactly what service to ask for; e.g., 21 for FTP, 22 for SSH, 25 for SMTP, 80 for HTTP, 161 for SNMP, 443 for HTTPS, 993 for secure IMAP) or the clients have some other way of being told what to ask for and genuinely any port will do (0 is the special “pick a card, any card” address). You can ask a Tcl server socket for what port it is actually using fconfigure:
set portNumber [lindex [fconfigure $socket -sockname] 2]
But to get a server socket on a port in a specific range? No API for that. We have to cook something ourselves:
for {set port 40000} {$port <= 41000} {incr port} {
if {![catch {
set sock [socket -server $yourHandler $port]
}]} then {
break
}
}
# If we failed...
if {![info exist sock]} {
error "No ports free in range 40k-41k"
}
This works because failing to bind the port will make the socket creation fail (neatly, catchably) and you can then try to bind the next port. It will take a while to scan over the port range, but it will work.
It's probably neater to wrap this up in a procedure. And Tcl 8.6's try construct will make the code a little less obscure:
proc portInRange {from to handler} {
for {set p $from} {$p <= $to} {incr p} {
try {
return [socket -server $handler $p]
} on error {} continue
}
error "No ports free in range $from-$to"
}
No, there's no API for that.
Generally servers listen on a specific port so that the clients can find the server. So such an API is not particularly useful.
Best to just write it yourself.
I am trying to configure exim to send mails directly over LAN without DNS, but I have no luck doing it...
On both ends there is a server with one Internet-facing interface and one local interface. I need to use the local interface.
When I telnet to port 25 from one server to another, it works like a charm. However, when I try to send mail via exim, it insist that I am trying to send the mail to myself.
I have added this router to server with local interface 11.11.0.1:
new_router:
driver = manualroute
domains = 11.11.0.2
transport = remote_smtp
route_list = * 11.11.0.1
and even forced the remote_smtp to use the correct interface:
remote_smtp:
driver = smtp
interface = 11.11.0.1
This is what I get if I try to send a mail form 11.11.0.1 to 11.11.0.2:
2014-03-16 22:11:38 1WPILK-0004YD-O1 == test#11.11.0.2 R=new_router defer (-1): remote host address is the local host
2014-03-16 22:11:39 1WPILK-0004YD-O1 Frozen
This should be the relevant part of the log:
--------> new_router router <--------
local_part=test domain=11.11.0.2
checking domains
11.11.0.2 in "11.11.0.2"? yes (matched "11.11.0.2")
calling new_router router
new_router router called for test#11.11.0.2
domain = 11.11.0.2
route_item = * 11.11.0.1
11.11.0.2 in "*"? yes (matched "*")
original list of hosts = "11.11.0.1" options =
expanded list of hosts = "11.11.0.1" options =
set transport remote_smtp
finding IP address for 11.11.0.1
calling host_find_byname
gethostbyname2(af=inet6) returned 1 (HOST_NOT_FOUND)
local host found for non-MX address
fully qualified name = 11.11.0.1
gethostbyname2 looked up these IP addresses:
name=11.11.0.1 address=11.11.0.1
LOG: MAIN
remote host address is the local host: 11.11.0.2
new_router router: defer for test#11.11.0.2
message: remote host address is the local host
added retry item for R:11.11.0.2: errno=-1 more_errno=0 flags=0
post-process test#11.11.0.2 (1)
LOG: MAIN
== test#11.11.0.2 R=new_router defer (-1): remote host address is the local host
Do you have any idea how to convince exim to not treat 11.11.0.2 as a local address?
Thanks,
Drasha
It seems your exim thinks 11.11.0.2 refers to your own machine, i.e. you have some configuration setting (local_interfaces, extra_local_interfaces or hosts_treat_as_local) that includes 11.11.0.2.
By default the manualroute router is intended to give you a remote host, but that can also be adjusted by adding
self = send
to your new_router configuration, although it would be better to fix the original problem instead of patching the symptoms as you might run into all kinds of other strange problems if you have messed up what is considered local hosts and not.
BTW, what does the command exim -bP | grep local show?