3

I can run a single command to see if a port is open:

portqry -n servername -e 80

I want to run this against a list of servers:

  1. DEV1 80
  2. DEV2 80
  3. DEV3 80
  4. TEST1 80
  5. TEST2 80
  6. PROD 80

I want to test them all using a single Windows batch script and see which portsare open and which ones are closed. (And I don't want it to fall over on the first closed port).

My question is: Is there a Windows batch script to see if a sequence of server/ports is open?

Additional: I'm aware there are other questions asking how to check if a port is open. None of them are about scripting it for a sequence of ports in a reliable way.

npocmaka
  • 55,367
  • 18
  • 148
  • 187
hawkeye
  • 34,745
  • 30
  • 150
  • 304
  • With a broad, non-specific-programming-related question like that, a good answer would simply be, "yes, there is." Because I know that you know that this isn't a code-writing service. – SomethingDark Apr 08 '15 at 23:10
  • With respect, I think a request for a two-line batch script example has plenty of precedent on this site. – hawkeye Apr 08 '15 at 23:46
  • How is this question non-specific-programming? Question asked: A Windows Batch script. Answer: Yes, use a `FOR` loop to run PortQry for the desired port and redirect output into a results file. -edit, Try `for /f %a in (port-list.txt) do PortQry -n server -e %a >> results.txt` – user4317867 Apr 08 '15 at 23:47
  • `for %%I in (dev1 dev2 dev3 test1 test2 prod) do portqry -n %%I -e 80` would let you loop through each of the servers. Or if you would like not only to check whether the port is open but also whether the web service is appropriately serving content, [the script in this cat's question](http://stackoverflow.com/q/15395490/1683264) can be tweaked for your needs pretty easily I think. – rojo Apr 08 '15 at 23:52
  • @hawkeye - and it shouldn't. Stack Overflow is not a code-writing service. Getting _help with_ a two-line batch script is fine. Requesting somebody to write a script _for you_ is not. – SomethingDark Apr 09 '15 at 02:18
  • @user4317867 - Directly from the off-topic flag text: "Questions asking us to recommend or **find** a book, **tool**, **software library**, tutorial or other off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it." This is a "write my code for me" question, which is _always_ off-topic. – SomethingDark Apr 09 '15 at 02:19
  • @SomethingDark You're not wrong. However, it seems to me that someone who has contributed so much to this site is worthy of a little indulgence now and again. Besides, providing a solution to his problem might help others in the future. – rojo Apr 09 '15 at 02:34
  • @rojo - Fair enough. – SomethingDark Apr 09 '15 at 02:51

3 Answers3

8

Here you go. Pay it forward. :) To answer your question directly, just use a for loop to loop through your servers and perform a portqry on each. Edit: That PowerShell snippet you found is useful for getting rid of the PortQry dependency.

@echo off
setlocal

set "servers=dev1 dev2 dev3 test1 test2 test2:8080 prod prod:443"

for %%I in (%servers%) do (
    for /f "tokens=1,2 delims=:" %%a in ("%%I") do (
        set "port=%%~b"
        if not defined port set "port=80"
        setlocal enabledelayedexpansion
        call :handshake "%%~a" "!port!" && (
            echo %%a port !port!: OK
        ) || (
            echo %%a port !port!: Error
        )
        endlocal
    )
)

goto :EOF

:handshake <server> <port>
powershell "$t=new-object Net.Sockets.TcpClient;$c=$t.BeginConnect('%~1',%~2,{},{});if($c.AsyncWaitHandle.WaitOne(1000)){$t.EndConnect($c);exit 0};exit 1"
exit /b %ERRORLEVEL%

Here's the original solution using PortQry 2.0:

@echo off
setlocal

set "servers=dev1 dev2 dev3 test1 test2 test2:8080 prod prod:443"

for %%I in (%servers%) do (
    for /f "tokens=1,2 delims=:" %%a in ("%%I") do (
        set "port=%%~b"
        if not defined port set "port=80"
        setlocal enabledelayedexpansion
        portqry -n "%%~a" -e "!port!" >NUL 2>NUL && (
            echo %%a port !port!: OK
        ) || (
            echo %%a port !port!: Error
        )
        endlocal
    )
)

If all you are testing are web services, it might make more sense to go about this in a different way. You can use the Microsoft.XMLHTTP COM object to get rid of that portqry dependency; and the responses acquired thusly will be more relevant to HTTP services. (For example, if you've got a VNC server running on port 8080 where you expect a web service to be listening instead, portqry would probably return success when you'd need it to return fail.)

Anyway, save this as a .bat script and salt to taste.

@if (@CodeSection == @Batch) @then

@echo off
setlocal

set "servers=dev1 dev2 dev3 test1 test2 test2:8080 prod prod:443"

for %%I in (%servers%) do (
    for /f "tokens=1,2 delims=:" %%a in ("%%I") do (
        set "port=%%~b"
        if not defined port set "port=80"
        setlocal enabledelayedexpansion
        cscript /nologo /e:JScript "%~f0" "%%~a" "!port!" && (
            echo %%a port !port!: OK
        ) || (
            echo %%a port !port!: Error
        )
        endlocal
    )
)

goto :EOF

@end // end batch / begin JScript chimera

var server = WSH.Arguments(0),
    port = WSH.Arguments(1),
    protocol = port == 443 ? 'https' : 'http',
    URL = protocol + '://' + server + ':' + port + '/',
    XHR = WSH.CreateObject('Microsoft.XMLHTTP');

XHR.open('GET', URL);
XHR.setRequestHeader('User-Agent','XMLHTTP/1.0');
XHR.send('');
while (XHR.readyState != 4) WSH.Sleep(25);
WSH.Quit(XHR.status - 200);
rojo
  • 24,000
  • 5
  • 55
  • 101
  • I really like your answer - but it assumes I'm contacting an http server, when in fact I only wanted to see if the port was open. What would need to be modified to make it so? – hawkeye Apr 09 '15 at 12:20
  • Try the first script in my edit. I left the second in case it's useful to someone else in the future, since it's pretty clever if I do say so myself. – rojo Apr 09 '15 at 12:35
  • Ok - the powershell one is awesome. – hawkeye Apr 09 '15 at 13:23
  • so you are checking non http/https port with XMLHTTP object...? – npocmaka Apr 18 '18 at 20:38
  • @npocmaka Not all web servers run on ports 80 and 443, and some users may wish to test whether a web server responds through a proxy. \*shrug\* – rojo Apr 18 '18 at 23:12
  • Hi Rojo, your script using Portquery is working like charm. But it gives only OK/Error as output, i need also whether port "Listening"/"Not listening". Could you please help me with that – Kiran Maroju May 11 '18 at 11:04
  • @KiranMaroju OK = Listening, Error = Not listening? – rojo May 11 '18 at 11:37
  • @rojo "Filtered" ports also showing as Error. I need output in 3 different statuses for 3 different responses from portquery – Kiran Maroju May 11 '18 at 13:37
  • @KiranMaroju Sorry, I've never actually used portqry and don't feel like downloading it. Does portqry have a different exit code between filtered and not listening? Use `echo %ERRORLEVEL%` to see after running a query. If not, you'll have to use a `for /F` loop to scrape the stdout output of portqry to determine listening, filtered, or closed. If you can't figure it out, create a new question post. – rojo May 11 '18 at 14:00
1

This is what I ended up replacing the VBScript above with - a file called porttest.ps1 which is run with powershell -f porttest.ps1

param(
    [string] $remoteHost = "arbitrary-remote-hostname",
    [int] $port = 23
     )

# Open the socket, and connect to the computer on the specified port
write-host "Connecting to $remoteHost on port $port"
$tcpobject = new-Object system.Net.Sockets.TcpClient 
$connect = $tcpobject.BeginConnect($remoteHost,$port,$null,$null) 
#Configure a timeout before quitting - time in milliseconds 
$wait = $connect.AsyncWaitHandle.WaitOne(1000,$false) 
If (-Not $Wait) {
    'Timeout'
    exit 1
} Else {
    $error.clear()
    $tcpobject.EndConnect($connect) | out-Null 
        If ($Error[0]) {
            Write-warning ("{0}" -f $error[0].Exception.Message)
            exit 1
        } Else {
            'Port open!'
            exit 0
        }
    }
hawkeye
  • 34,745
  • 30
  • 150
  • 304
  • Other one was definitely better - but just wanted to contribute some other thoughts to keep the ideas bubbling. – hawkeye Apr 09 '15 at 12:53
  • 1
    Nice idea using PowerShell for `Net.Sockets.TcpClient`. I incorporated that into my answer, if you're interested. – rojo Apr 09 '15 at 13:22
0

Unlike the above this does not require powershell at all and should work with all versions of windows provided the telnet client has been installed (dism /online /Enable-Feature /FeatureName:TelnetClient)

If someone can work out how to stop the telnet command outputting anything please add a comment, because if I add a redirect it refuses to run. I believe telnet requires a stdout and cannot be redirected.

For this to work just change the two 127.0.0.1 IP addresses to the host you are wanting to scan.

break>Results.txt & (for /l %a in (1,1,49152) do @taskkill /im telnet.exe /f >NUL 2>&1 & @start /b telnet.exe 127.0.0.1 %a & for /f %i in ('netstat -n ^| findstr /r /c:"TCP[ ][ ]*127.0.0.1:%a[ ][ ]*.*ESTABLISHED" ^| findstr /n ".*" ^| findstr "^1:"') do @echo "TCP Port Open: %a" >> Results.txt) & taskkill /im telnet.exe /f >NUL 2>&1

Once it has finished, to get the results you can type

type Results.txt
sparks
  • 271
  • 2
  • 3