Тема: CMD/BAT: DynDNS-сервер на связке .cmd & Netcat
решил сегодня накатать сервак динамического обновления ip для доменов клиентских узлов (в моем случае - GSM-роутеры),
интересует вот что, поскольку биться с темой, созданной ранее, я не стал и взялся за готовую слушалку (netcat, кто не слышал - советую,
исключительно локаничное и очень удобное решение для отладки всего, что связано с сеткой и tcp-портами в частности)
время шло своим чередом, я доедал третью тарелку с гречкой и даже начал забывать к ночи про сигареты, но вопрос вселенной,
смысла жизни и всего такого не давал мне покоя всё больше и больше: как сделать нормальную синхронизацию процесса обновления
инфы от нескольких клиентов одновременно, не просто WRITE_LOCK-ами (файлами), а адекватно, через какое-нибудь shared memory..
и самое главное - как делать спаун коннект и освобождать порт для новых клиентов сразу, как только подрубился последний, тоесть
чтобы работало как полноценный сервис, типо опача, эндажайнекс и вообще всего, что имеет established в протоколе!
так же надеюсь, что кому-нибудь данный кодес будет полезен! не работает в данной версии только тема с socket_count, т.к. пока в раздумьях,
стоит ли создавать пул слушалок на определённом port range, уж больно костыльно как-то, реал.. =\
любая критика принимается с радостью, как и предложения!
@echo off
REM ---------------
REM -- EXECUTION --
REM ---------------
REM common.config
set irz.ddns.fork.fastwrite_mode=yes
set irz.ddns.nc.listen.port=44044
set irz.ddns.nc.listen.ipaddr=127.0.0.1
set irz.ddns.nc.listen.socket_count=3
set irz.ddns.nc.fullpath=..\..\nc.exe
set irz.ddns.debug=yes
set irz.ddns.remote_debug=yes
set irz.ddns.debug.level=1
REM common.config.predefined
set irz.ddns.hosts_file=%systemroot%\system32\drivers\etc\hosts
REM common.preexec_wrappers
setlocal enabledelayedexpansion
if not "%1" == "" (
if "%1" == "--fork" (
call :fork %0 %2
) else if "%1" == "--listener" (
call :listener %2 %3
)
goto :EOF
)
REM 0.0 main.start
echo [main]:STARTED
echo ----------------
if "%irz.ddns.debug%" == "2" echo [main].env: nc execall = '%irz.ddns.nc.execall%'
echo [main]:preparing
if not exist ".\cache" mkdir "cache"
if not exist ".\cache" (
echo [main][cache]:[!] error, cannot create cache directory
goto :EOF
) else echo [main][cache]:directory existing
if %irz.ddns.debug% GTR 2 (
echo [main][info]:listener will bind on %irz.ddns.nc.listen.ipaddr%:%irz.ddns.nc.listen.port%
if "%irz.ddns.fork.fastwrite_mode%" == "yes" echo [main][info]:fastwrite mode enabled
if "%irz.ddns.fork.fastwrite_mode%" == "no" echo [main][info]:fastwrite mode disabled
)
echo ----------------
REM 0.1 creating cycled NC fork pool
set irz.ddns.forkpid=%random%
echo [main]:fork() #1 [PID=%irz.ddns.forkpid%]
start /b %0 --fork %irz.ddns.forkpid%
echo [main]:exit
exit
REM ---------------
REM -- FUNCTIONS --
REM ---------------
REM Threads
:fork
setlocal enabledelayedexpansion
echo ----------------
echo [fork.%2]:called
set irz.ddns.fork.%2.lpid=%random%
echo [fork.%2]:creating listener [LPID=!irz.ddns.fork.%2.lpid!]
rem %1 - executable .cmd, because of %0 == ':fork'; %2 - current PID
set irz.ddns.nc.execall=%irz.ddns.nc.fullpath%
set irz.ddns.nc.execall=%irz.ddns.nc.execall% -l -s %irz.ddns.nc.listen.ipaddr%
set irz.ddns.nc.execall=%irz.ddns.nc.execall% -p %irz.ddns.nc.listen.port%
set irz.ddns.nc.execall=%irz.ddns.nc.execall% -e "%1 --listener %2 !irz.ddns.fork.%2.lpid!"
rem Calling listening utility
%irz.ddns.nc.execall%
if %irz.ddns.debug.level% GTR 0 (
echo [fork.%2].[listener.!irz.ddns.fork.%2.lpid!] exited. respawning..
REM if %irz.ddns.debug.level% GTR 2 echo [fork.%2].[listener.!irz.ddns.fork.%2.lpid!] exited (return code = '%errorlevel%'). respawning..
)
goto :fork
echo [fork.%1]:EXIT
exit /b
REM Executable by NC
:listener
setlocal enabledelayedexpansion
rem [th.1.0: ]
if "%irz.ddns.remote_debug%" == "yes" (
echo [fork.%1].[listener.%2]:called
echo [fork.%1].[listener.%2]:asking client for DDNS INFO
)
set /p irz.ddns.fork.%1.imei=IMEI:
set /p nullvar1=
if "!irz.ddns.fork.%1.imei!" == "" (
if "%irz.ddns.remote_debug%" == "yes" (
echo [fork.%1].[listener.%2]:no IMEI/UNIT-NAME given. client rejected.
echo [fork.%1].[listener.%2]:EXIT
pause
)
exit /b
)
set /p irz.ddns.fork.%1.ip=IP:
set /p nullvar2=
if "!irz.ddns.fork.%1.ip!" == "" (
if "%irz.ddns.remote_debug%" == "yes" (
echo [fork.%1].[listener.%2]:no IP-address given. client rejected.
echo [fork.%1].[listener.%2]:EXIT
pause
)
exit /b
)
REM Debug info for remote host
if "%irz.ddns.remote_debug%" == "yes" (
if "%irz.ddns.debug.level%" == "1" echo [fork.%1].[listener.%2]:recieved DDNS-INFO
if "%irz.ddns.debug.level%" == "2" echo [fork.%1].[listener.%2]:recieved DDNS-INFO: [IMEI=!irz.ddns.fork.%1.imei!], [IP=!irz.ddns.fork.%1.ip!]
)
REM Fastwrite try (cache evasion, directly to HOSTS)
if "%irz.ddns.fork.fastwrite_mode%" == "yes" (
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%1].[listener.%2]:evasing cache ^(fastwrite^)
call :update_fastwrite !irz.ddns.fork.%1.imei! !irz.ddns.fork.%1.ip! %1 %2
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%1].[listener.%2]:fastwrite return code: %errorlevel%
if "%errorlevel%" == "0" exit /b 0 else exit /b -1
REM arg #1 - IMEI/UNIT-NAME, arg #2 - IP-address
)
REM Creating cache
set irz.ddns.fork.%1.listener.%2.cache-id=id-!irz.ddns.fork.%1.imei!.pid-%1.lpid-%2
if "%irz.ddns.remote_debug%" == "yes" (
if "%irz.ddns.debug.level%" == "1" echo [fork.%1].[listener.%2]:creating listener private cache
if "%irz.ddns.debug.level%" == "2" echo [fork.%1].[listener.%2]:creating listener private cache [CACHE-ID=!irz.ddns.fork.%1.listener.%2.cache-id!]
)
REM Writing vars to cache
if "%irz.ddns.remote_debug%" == "yes" (
if "%irz.ddns.debug.level%" == "1" echo [fork.%1].[listener.%2]:writing DDNS-INFO to cache
if "%irz.ddns.debug.level%" == "2" echo [fork.%1].[listener.%2]:writing DDNS-INFO to cache [".\cache\cache_!irz.ddns.fork.%1.listener.%2.cache-id!"]
)
echo !irz.ddns.fork.%1.ip!> ".\cache\cache_!irz.ddns.fork.%1.listener.%2.cache-id!"
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%1].[listener.%2]:sleeping
call :sleep 10 %1 %2
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%1].[listener.%2]:EXIT
pause
exit /b
REM Update HOSTS, using given call arguments
REM arg #1 - IMEI/UNIT-NAME, arg #2 - IP-address
:update_fastwrite
setlocal enabledelayedexpansion
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:called
set irz.ddns.tmpvar.updated=0
set irz.ddns.tmpvar.cut_comment=0
set irz.ddns.tmpvar.first_line=1
for /f "tokens=* delims= " %%a in (%irz.ddns.hosts_file%) do (
if "%irz.ddns.remote_debug%" == "yes" (
if %irz.ddns.debug.level% GTR 1 echo incycle start: irz.ddns.tmpvar.updated = !irz.ddns.tmpvar.updated!
)
set line=%%a
if "%irz.ddns.remote_debug%" == "yes" (
if %irz.ddns.debug.level% GTR 1 echo old line = !line!
)
for /f "tokens=1,2" %%b in ("!line!") do (
set token1=%%b
set token2=%%c
if !token2! == %1 (
rem echo host '%str-to-find%' found in string '!line!'
set line=%2 !token2!
set irz.ddns.tmpvar.updated=1
if %irz.ddns.debug.level% GTR 0 echo [fork.%3].[listener.%4].fastwrite:entry for host '%1' updated
) else if "!token1!" == "#" (
set irz.ddns.tmpvar.cut_comment=1
) else if "!token1!" == "" (
set irz.ddns.tmpvar.cut_comment=1
) else (
set irz.ddns.tmpvar.cut_comment=0
set line=!token1! !token2!
)
)
echo irz.ddns.tmpvar.cut_comment = !irz.ddns.tmpvar.cut_comment!
if "%irz.ddns.remote_debug%" == "yes" (
if %irz.ddns.debug.level% GTR 1 (
if "!irz.ddns.tmpvar.cut_comment!" == "0" (
echo updated line = !line!
echo.
) else (
echo this line will be removed, because it's a comment
echo.
)
)
)
echo after word parsing cycle: irz.ddns.tmpvar.cut_comment = !irz.ddns.tmpvar.cut_comment!
rem Storing old & updated content: no comments, no empty lines
if not "!irz.ddns.tmpvar.cut_comment!" == "1" (
if not "!irz.ddns.tmpvar.first_line!" == "1" (
echo !line!>> ".\cache\updated_hosts"
) else (
echo FIRSTLINE MOTHER FUCKER!!!!!!!!
echo !line!> ".\cache\updated_hosts"
set irz.ddns.tmpvar.first_line=0
)
)
if "%irz.ddns.remote_debug%" == "yes" (
if %irz.ddns.debug.level% GTR 1 echo incycle end: irz.ddns.tmpvar.updated = !irz.ddns.tmpvar.updated!
)
)
rem Creating new entry if needed
if "!irz.ddns.tmpvar.updated!"=="0" (
if "%irz.ddns.remote_debug%" == "yes" (
if %irz.ddns.debug.level% GTR 0 echo [fork.%3].[listener.%4].fastwrite:entry for host '%1' not found
if %irz.ddns.debug.level% GTR 0 echo [fork.%3].[listener.%4].fastwrite:creating new entry..
)
echo %2 %1 >> ".\cache\updated_hosts"
)
if not exist ".\cache\WRITE_LOCK" (
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:writelock not set. creating..
echo.>".\cache\WRITE_LOCK"
if not exist ".\cache\WRITE_LOCK" (
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:ERROR! unable to set writelock
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:EXIT
if "%irz.ddns.remote_debug%" == "yes" pause
exit /b -1
) else (
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:writelock set
copy %irz.ddns.hosts_file% ".\cache\backup_hosts" >nul
if not exist ".\cache\backup_hosts" (
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:ERROR! unable to backup HOSTS file
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:EXIT
if "%irz.ddns.remote_debug%" == "yes" pause
)
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:updating system HOSTS file..
REM move /y ".\cache\updated_hosts" %irz.ddns.hosts_file% >nul
del /f %irz.ddns.hosts_file% >nul
copy /y ".\cache\updated_hosts" %irz.ddns.hosts_file% >nul
REM Validation information in updated HOSTS file
if "%irz.ddns.remote_debug%" == "yes" (
if %irz.ddns.debug.level% GTR 1 (
echo pause for updated HOSTS validation tracing.
echo now edit address for '%1' in HOSTS file to wrong value not equal '%2'
pause
)
)
rem set errorlevel=0
echo errorlevel=%errorlevel%
echo findstr /i /p /x /c:"%1 %2" %irz.ddns.hosts_file%
findstr /x /c:"%1 %2" %irz.ddns.hosts_file%
echo errorlevel=%errorlevel%
if "%irz.ddns.remote_debug%" == "yes" (
if %irz.ddns.debug.level% GTR 1 (
echo validation result: errorlevel=%errorlevel%
pause
)
)
if not "%errorlevel%" == "0" (
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:ERROR! new HOSTS validation failed!
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:EXIT
if "%irz.ddns.remote_debug%" == "yes" pause
) else (
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:new HOSTS file validation succeeded
)
REM Unset WRITE_LOCK
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:removing writelock..
del /f ".\cache\WRITE_LOCK"
if not exist ".\cache\WRITE_LOCK" (
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:writelock removed
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:EXIT
exit /b -10
)
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:ERROR! unable to remove writelock!
if "%irz.ddns.remote_debug%" == "yes" echo [fork.%3].[listener.%4].fastwrite:EXIT
if "%irz.ddns.remote_debug%" == "yes" pause
exit /b -3
)
) else exit /b -1
exit /b 0
REM Timer for WRITE_LOCK file monitoring task
:sleep
setlocal enabledelayedexpansion
echo [fork.%2].[listener.%3].sleep():called
echo [fork.%2].[listener.%3].sleep():sleeping for %1 seconds..
exit /b
REM Message indention
:nfof
echo ^%1^(^): %2
exit /b
:nfop
echo ^[%1^]: %2
exit /b
:nfo_1
REM Echo given string with indent
for /f in (%1) (
echo %%i
)
exit /b
:EOF
echo [main]:EXIT