Test if a port on a remote system is reachable (without telnet)
Solution 1
Bash has been able to access TCP and UDP ports for a while. From the man page:
/dev/tcp/host/port
If host is a valid hostname or Internet address, and port is an integer port number
or service name, bash attempts to open a TCP connection to the corresponding socket.
/dev/udp/host/port
If host is a valid hostname or Internet address, and port is an integer port number
or service name, bash attempts to open a UDP connection to the corresponding socket.
So you could use something like this:
xenon-lornix:~> cat < /dev/tcp/127.0.0.1/22
SSH-2.0-OpenSSH_6.2p2 Debian-6
^C pressed here
Taa Daa!
Solution 2
Nice and verbose! From the man pages.
Single port:
nc -zv 127.0.0.1 80
Multiple ports:
nc -zv 127.0.0.1 22 80 8080
Range of ports:
nc -zv 127.0.0.1 20-30
Solution 3
Netcat is a useful tool:
nc 127.0.0.1 123 &> /dev/null; echo $?
Will output 0
if port 123 is open, and 1
if it's closed.
Solution 4
The simplest method, without making use of another tool, such as socat
, is as described in @lornix's answer above. This is just to add an actual example of how one would make use of the psuedo-device /dev/tcp/...
within Bash if you wanted to, say, test if another server had a given port accessible via the command line.
Examples
Say I have a host on my network named skinner
.
$ (echo > /dev/tcp/skinner/22) >/dev/null 2>&1 \
&& echo "It's up" || echo "It's down"
It's up
$ (echo > /dev/tcp/skinner/222) >/dev/null 2>&1 && \
echo "It's up" || echo "It's down"
It's down
The reason you want to wrap the echo > /dev/...
in parentheses like this, (echo > /dev/...)
is because if you don't, then with tests of connections that are down, you'll get these types of messages showing up.
$ (echo > /dev/tcp/skinner/223) && echo hi
bash: connect: Connection refused
bash: /dev/tcp/skinner/223: Connection refused
These can't simply be redirected to /dev/null
since they're coming from the attempt to write out data to the device /dev/tcp
. So we capture all that output within a sub-command, i.e. (...cmds...)
and redirect the output of the sub-command.
Solution 5
I found that curl
may get the job done in a similar way to telnet
, and curl
will even tell you which protocol the listener expects.
Construct an HTTP URI from the hostname and port as the first argument to curl
. If curl
can connect, it will report a protocol mismatch and exit (if the listener isn't a web service). If curl
cannot connect, it will time out.
For example, port 5672 on host 10.0.0.99 is either closed or blocked by a firewall:
$ curl http://10.0.0.99:5672
curl: (7) couldn't connect to host
However, from a different system, port 5672 on host 10.0.0.99 can be reached, and appears to be running an AMQP listener.
$ curl http://10.0.0.99:5672
curl: (56) Failure when receiving data from the peer
AMQP
It's important to distinguish between the different messages: the first failure was because curl
could not connect to the port. The second failure is a success test, though curl
expected an HTTP listener instead of an AMQP listener.
Related videos on Youtube
Steve HHH
Updated on September 18, 2022Comments
-
Steve HHH over 1 year
In the old days, we used
telnet
to see if a port on a remote host was open:telnet hostname port
would attempt to connect to any port on any host and give you access to the raw TCP stream.These days, the systems I work on do not have telnet installed (for security reasons), and all outbound connections to all hosts are blocked by default. Over time, it's easy to lose track of which ports are open to which hosts.
Is there another way to test if a port on a remote system is open – using a Linux system with a limited number of packages installed, and
telnet
is not available?-
Admin over 8 yearsRelated: check status of one port on remote host at SO
-
Admin over 4 yearsI was having this same issue. The answer by @Subhranath Chunder below helped. However, I then found out that installing Telnet was a small matter of running
brew install telnet
. So I expect Linux users can do the same withyum
andapt-get
. -
Admin over 3 yearsWhen "all outbound connections to all hosts are blocked by default" there will be no way to perform such a test - you are offline
-
-
Steve HHH almost 11 years
nmap
is a good tool, but not available on these systems. Rather than downloadnmap
, compile it, install it to my home directory, then copy it to all the other systems, I was hoping to find a way using existing tools available in most Linux installations. -
user almost 11 yearsIf curl isn't available, wget might be.
wget -qS -O- http://ip.add.re.ss:port
should effectively do the same thing. -
Steve HHH almost 11 yearsThis is a far more elegant and scriptable answer than my own. It is unfortunate for me that the security-conscious sysadmins who withheld
telnet
also withheldnc
(though – strangely – notcurl
orwget
). -
Igor Djukic almost 11 yearsYes that is completely arbitrary and silly.
-
Chad Harrison almost 11 yearsLet the
FOR
statements begin! -
에이바 about 10 yearsThis even works with a hostname, ex.
curl myhost:22
. -
slm over 9 years@Okuma.Tony - yes that's always an issue with Q's that have many answers 8-). Thanks for the feedback though, it's appreciated.
-
davidjb about 9 yearsPerfect! This outputs a clear connection succeeded/failed message with just the one line. Note that multiple ranges don't appear to work for my
nc
version; only the first range is tested. -
Mohammad Shahid Siddiqui almost 9 yearsThis may be incorrect. I am havng a tomcat service running, but getting 404 error. # curl -k 192.168.194.4:6443 <html><head><title>Apache Tomcat/7.0.34 - Error report</title><style><!--H1 --- HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 404 - /</h1><HR size="1" noshade="noshade"><p><b>type</b> Status report</p><p><b>message</b> <u>/</u></p><p><b>description</b> <u>The requested resource is not available.</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/7.0.34</h3></body></html>
-
Peter Mortensen almost 9 years
-
Peter Mortensen almost 9 yearsThis hanged when tried on Ubuntu 14.04 (Trusty Tahr) for a remote server (same LAN) for closed ports (it timed out after 127 seconds) - thus not very suitable in scripts. It did work though for a service that had a port open. Using option "-w2" could be the solution.
-
Peter Mortensen almost 9 yearsYes, this is better - timing out almost immediately for closed ports.
-
Peter Mortensen almost 9 yearsHowever on ports that were not open it timed out after 22 seconds (tried on Ubuntu 14.04 (Trusty Tahr) for a remote server). Interestingly, the timeout period is much shorter than the one for nc (see thnee's answer).
-
A B over 8 yearsI think
-G 2
would be more appropriate for TCP timeout -
Igor Djukic over 8 years@PeterMortensen Connection timeout is a generic network problem, nothing particular to netcat. Configure your desired timeout with the
-w
flag. -
kenorb over 8 yearsSee my post with similar approach.
-
kmoe over 8 yearsDoes this always use TCP or is there a way to get it to check UDP?
-
1.61803 over 8 years
-
connorbode over 8 yearsFor those who don't know bash well, what does the
&> /dev/null; echo $?
do? I noticed that without that addition, the command produces no output. -
Igor Djukic over 8 years
&> /dev/null
redirects all output so it wont show up, andecho $?
shows the resulting status code of the previous command executed. -
Nathan Basanese over 8 years// , Ohhh, this is one of those days I love this site. Here's the part of the bash manual for this answer: gnu.org/software/bash/manual/bashref.html#Redirections
-
Alexandr almost 8 years@lornix, can you please tell me, why nc works on my box, but your solution not working: $ cat < /dev/tcp/10.0.xx.xx/1433 cat: -: Connection reset by peer $ nc -zv -w5 10.0.xx.xx 1433 Connection to 10.0.xx.xx 1433 port [tcp/ms-sql-s] succeeded!
-
alpha almost 8 years@Alexandr, perhaps you're connecting in a different way. Your nc command includes the -z flag, indicating 'zero-I/O-mode'. cat may be connecting and requesting some sort of I/O, whereas the nc (with -z) usage just strokes the port to see if it's alive.
-
Alexandr almost 8 years@lornix, ok, but in this case I have to get the same result with use nc without -z option, but it still does not work: # nc -v -w5 127.0.0.1 18080 Connection to 127.0.0.1 18080 port [tcp/*] succeeded! # cat < /dev/tcp/127.0.0.1/18080 Just hangs without any result. Just want to understand when I can use "/dev/tcp/host/port" option
-
alpha almost 8 years@Alexandr... actually, "hangs without any result" is pretty much expected behavior. cat is waiting for input. nc has extra smarts to enable it to sense no-data pending and stops trying. cat isn't quite as smart. Try
cat < /dev/tcp/localhost/22
, you should get your sshd header. Evidently, your process on port 18080 waits for something to come in, before sending anything. Port 22 (ssh) greets you with it's version and whatnot. Try it out! -
Alexandr almost 8 years@lornix, thank you very much for explanation! Now the restriction is clear. I think using nc should be a preferred way to check ports.
-
Efren over 7 yearsUse -u option for UDP ports.
-
Daniel Andrei Mincă over 7 yearsNot working on Ubuntu 16.04.1
-
alpha over 7 years@MincăDanielAndrei Make sure you're using bash. No idea if this works on other shells, Ubuntu may have substituted a different shell as your default, depending on the whims of The Mark.
-
Daniel Andrei Mincă over 7 years@lornix I'm using GNU bash, version 4.3.46(1)-release (x86_64-pc-linux-gnu). The problem is that /dev/tcp/ does not exist. I got /dev everything except /tcp and /udp .
-
alpha over 7 yearsBash emulates the /dev/tcp and /dev/udp paths when used in redirections. (From Bash Man Page v4.4.0, line 1344. Should be in similar location in yours). Those paths don't exist for other programs or even as files. So 'cat /dev/tcp/localhost/22' will error out, while 'cat < /dev/tcp/localhost/22' will show your SSH server header.
-
Ismail Yavuz over 7 yearsHow to use this statement for a conditional -like if- operator? if[ WhatGoesHere == 1]
-
smishra about 7 yearsOn version 6.4 of ncat -z is not recognized. I was able to do without z
-
Elijah Lynn almost 7 yearsPow, this is the way to test Ansible's wait_for command on the command line => docs.ansible.com/ansible/wait_for_module.html
-
bobbel almost 7 yearsYou can check multiple ranges with:
nc -zv 127.0.0.1 22,80,8080,20-30,443-446
(nc Version: 1.107-4). -
Scott Chu almost 7 yearsdoesn't work under CentOS.
-
A Child of God about 6 yearsI like how the man page for nc says
The nc (or netcat) utility is used for just about anything under the sun involving TCP, UDP, or UNIX-domain sockets.
-
Michael Hobbs over 5 yearsThis was incredibly helpful when working with a docker container that had nothing installed. Was able to quickly verify that the container had access to non-containerized DB via DNS. Example: cat < /dev/tcp/hostname/5432
-
Tilo over 5 yearswhat is the timeout for closed ports?
-
Janac Meena over 5 yearsDoesn't seem to be bundled with Centos v6
-
Beege about 5 yearsLook further down in the "answers" and @sim has a great example of applying this answer. His bash fu is strong. (btw - this does work under CentOS.)
-
Petr almost 5 yearsthat's great, but only if nc is actually installed :P the accepted answer via bash works almost everywhere, more GNU/Linux servers have bash than nc
-
Mark Lybarger over 4 yearswhy would you set tool, only to use it later in the case statement. isn't it simpler to check which tool is available and use it immediately in the if block?
-
Indika K about 4 yearsThis should be marked as the answer.
-
Robert Boyd about 4 yearsGood point -- I threw that together in a hurry -- it does make sense to collapse the code as you suggested. On the other hand, setting tool could be useful if there's a need to do more with it in another part of the code.
-
Scott - Слава Україні over 3 years(1) I presume that you mean that the
curl -v telnet://
, the:
and the (final)/
are literal, and$host
and$port
are placeholders for the name/address of the host in question and the port number in question. What should the user use for$path
? (2) If the port is open, but it is not implementing HTTP, we can expect this command to fail. So how does one distinguish between a failure because the port isn’t open and a failure because the port isn’t HTTP? … (Cont’d) -
Scott - Слава Україні over 3 years(Cont’d) … (3)
curl http://
host
:
port
has already been given as an answer (which was clear on point #1 and discussed point #2). Are you saying thatcurl -v telnet:
is superior tocurl http:
Why? (4) And there’s another answer that suggestscurl http://
host
:
port
, and there’s even another one suggestingcurl telnet://
host
:
port
. What does your answer add to those earlier ones? … … … … … … … … … … … … … … … … … … … … … Please do not respond in comments; edit your answer to make it clearer and more complete. -
Marinaio over 2 yearsWorked for me on my RHEL7
-
Marinaio over 2 yearsThis works; I like it. However, I never see the output "" succeeded!"" like advertised. When I do a range, seems only the open port gets displayed. I'm on RHEL7. Just might have been a nice to have so I could grep in a script.
-
Marinaio over 2 yearsI like it. I made a script out of it for my own use!
-
Ajaib Singh over 2 yearsYes, this answer should be on top.
-
Benyamin Limanto about 2 yearsWell this works well with busybox... :) better for WSL2 Alpine
-
mgutt about 2 yearsIs it possible to suppress error messages?
-
mgutt about 2 yearsIsn't
echo -n
better to avoid sending the linebreak? -
slm about 2 years@mgutt try and see what happens
-
mgutt about 2 years@slm It works. It's more question of interest.
-
slm about 2 years@mgutt - from what I can recall when I did this I believe there were situations that could arise, such as the server not being available that necessitated the inclusion of the linebreak.
-
Admin about 2 yearsAdd -v to curl is better (curl -v telnet://127.0.0.1:22). If the server does not output anything such as web socket. You don't know which is success connection or time out.