How to respond conditionally based on the request when using netcat
Asked Answered
B

2

7

I am trying to set up a web server using only windows batch scripting.

I have already come up with the following script:

@echo off
@setlocal  enabledelayedexpansion

for /l %%a in (1,0,2) do (
  type tempfile.txt | nc -w 1 -l -p 80  | findstr mystring
  if !ERRORLEVEL! == 0 (
    echo found > tempfile.txt
  ) else (
    echo not-found > tempfile.txt
  )
)

However, the response is always one request behind, I mean, if I type something like this into the browser:

REQUEST: localhost/mystring

I will get the following response:

RESPONSE: not-found

Only in the next request I will receive the correct answer for the request presented above.

This is happening because as soon as netcat receives a request it responds with the current content of the tempfile.txt which has not been updated yet based on the request.

Is there any way to block the response until the tempfile.txt is updated or any other method which accomplish the expected result?

Bluebeard answered 17/10, 2016 at 8:34 Comment(7)
You're overwriting the contents of tempfile.txt with either the string "found" or "not-found"; your code shouldn't be working at all.Plantaineater
@Plantaineater I believe that was intentional. Those aren't intended to be logs. They're meant to contain HTTP content. What OP is asking is how to make the nc TCP output conditional based on the browser's request header content.Miscall
@Miscall - he's overwriting a file that he's piping to netcat. Netcat should be vomiting errors left and right.Plantaineater
@Plantaineater Netcat execution has completed by the time findstr executes. Netcat couldn't care less about what happens to tempfile.txt until the next loop iteration.Miscall
And after the first iteration, tempfile.txt will contain nothing but the string "found" or "not found". I don't understand how the script successfully runs more than once.Plantaineater
@Plantaineater type opens tempfile.txt for reading and sends its contents to stdout, then closes the file handle. netcat takes the piped stdout data and prepares to send it to the next client that connects to TCP port 80. When a client connects, netcat vomits the browser request headers to the console and responds with its prepared piped-in payload, then its thread terminates. find sifts through the vomit for the requested URL. Then some logic happens, and the loop repeats. Only netcat doesn't work the way OP reckons it ought to.Miscall
@SomethingDark, that's pretty much what rojo has said :)Bluebeard
M
3

The problem is, as far as I can tell, nc can't perform a callback to tailor its output based on client input. Once you have...

stdout generation | nc -l

... blocking and waiting for a connection, its output is already determined. That output is static at that point.

The only workaround which occurs to me is rather inefficient. It basically involves the following logic:

  1. Listen for a connection prepared to make the client perform a reload
  2. Scrape the GET address from the previous request's headers.
  3. Serve relevant content on client's second connection

Example code:

@echo off & setlocal

rem // macro for netcat command line and args
set "nc=\cygwin64\bin\nc.exe -w 1 -l 80"

rem // macro for sending refresh header
set "refresh=^(echo HTTP/1.1 200 OK^&echo Refresh:0;^)^| %nc%"

for /L %%# in (1,0,2) do (
    rem // run refresh macro and capture client's requested URL
    for /f "tokens=2" %%I in ('%refresh% ^| findstr "^GET"') do set "URL=%%I"

    rem // serve content to the client
    setlocal enabledelayedexpansion
    echo URL: !URL! | %nc%
    endlocal
)

As a side note, delayed expansion can mutilate variable values set with exclamation marks if it's enabled at the time of setting. Best to wait to enable delayed expansion until retrieval.

Also, when performing a boolean check on %ERRORLEVEL% it's more graceful to employ conditional execution. But that has nothing to do with my solution. :)

And finally, instead of doing type filename.html | nc -l, consider using <filename.html nc -l (or nc -l <filename.html) to avoid the useless use of type.

Miscall answered 22/10, 2016 at 1:34 Comment(0)
K
5

Checkout the -e option, you could write a script that does the processing and then execute

nc -L -w1 -p 80 -eexec.bat

which would pipe stdin and stdout back and forth from nc to the script like you want.

exec.bat could be something like (somewhat pseudo code):

findstr mystring
if not errorlevel 1 (echo found) else (echo not-found)

or maybe a loop (also somewhat pseudo code):

:top
set /p input=
if input wasn't "" echo %input% >> output.dat && goto top
findstr /C:"mystring" output.dat
if not errorlevel 1 (echo found) else (echo not-found)
Koffler answered 25/10, 2016 at 1:48 Comment(4)
Can you provide any working code as proof of concept? If so, which build of netcat did you use that your code works with? I've tried every permutation I can think of using netcat 1.12 for Win32/64, and with neither the x86 nor the x64 build could I get the -e switch to run a batch script and send its output to the client. Firebug always shows the GET request status as "Aborted".Miscall
yeah im on a linux top but will get back to you in about 4 and a half hours.Koffler
ha make that 6 and a half. doing it now.Koffler
huh, its a documented feature and yet for the life of me i can't seem to get it working. it works with executables just fine, i wonder if its a problem with the batch script not being run in an interactive session? i'm really sorry, it's a documented feature... i'm still looking into it.Koffler
M
3

The problem is, as far as I can tell, nc can't perform a callback to tailor its output based on client input. Once you have...

stdout generation | nc -l

... blocking and waiting for a connection, its output is already determined. That output is static at that point.

The only workaround which occurs to me is rather inefficient. It basically involves the following logic:

  1. Listen for a connection prepared to make the client perform a reload
  2. Scrape the GET address from the previous request's headers.
  3. Serve relevant content on client's second connection

Example code:

@echo off & setlocal

rem // macro for netcat command line and args
set "nc=\cygwin64\bin\nc.exe -w 1 -l 80"

rem // macro for sending refresh header
set "refresh=^(echo HTTP/1.1 200 OK^&echo Refresh:0;^)^| %nc%"

for /L %%# in (1,0,2) do (
    rem // run refresh macro and capture client's requested URL
    for /f "tokens=2" %%I in ('%refresh% ^| findstr "^GET"') do set "URL=%%I"

    rem // serve content to the client
    setlocal enabledelayedexpansion
    echo URL: !URL! | %nc%
    endlocal
)

As a side note, delayed expansion can mutilate variable values set with exclamation marks if it's enabled at the time of setting. Best to wait to enable delayed expansion until retrieval.

Also, when performing a boolean check on %ERRORLEVEL% it's more graceful to employ conditional execution. But that has nothing to do with my solution. :)

And finally, instead of doing type filename.html | nc -l, consider using <filename.html nc -l (or nc -l <filename.html) to avoid the useless use of type.

Miscall answered 22/10, 2016 at 1:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.