Monday, January 23, 2006

How to update old Windows 2000 from WSUS server on non-standard port?

Given: Windows 2000 machine in a local network that badly needs updates. No internet access due to high risk of attacks to the exposed system. Windows update server in the same local network.
Problem:
Windows 2000 won't update from server in local network, because service runs on non-standard port for this OS. Windows 2000 update client supplied with SP4 (aka SUS client) can only update from WSUS service on port 80.

Why the problem: Usually server machines in local networks run all kinds of services including intranet web sites and web-services among them. Needless to say that port 80 is very popular among these due to a browser preference to treat it like default. No surprise that update service is running on different port, but Windows 2000 deliberately looks for this service on port 80 (where it was in old good times) and to change this ill behavior it needs an update. A typical chicken and egg problem and here is how to resolve it.

So, what?: After update SUS client becomes WSUS client, which is able to operate with any port, but to bootstrap the process you need to make WSUS service available somewhere on port 80. If port 80 on server is busy with another service you need to use port 80 from another available machine - i.e. forward or map port. Which machine? The most simple - the same machine client is running - localhost. Just forward WSUS service port (e.g. 8530) from remote server to port 80 on local machine and tell old client to use the latter.


How: An example:
WSUS service is located at http://intranet:8530

Setup port forwarding/mapping by using trivialproxy [1] or other portmapping tool (like portmapper [2])
Local Port : 80
Remote Port : 8530 Remote Host : intranet

Launch reg file setup_windows_update_localhost.reg to tell native SUS client update itself from localhost next time.

---[setup_windows_update_localhost.reg ]
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate]
"WUServer"="http://localhost"
"WUStatusServer"="http://localhost"

[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU]
"UseWUServer"=dword:00000001
"NoAutoUpdate"=dword:00000000
"AUOptions"=dword:00000003

---

Launch AUForceUpdate.cmd (slightly modified version of [3]) to force client start update ASAP.

---[AUForceUpdate.cmd]
@echo off
Echo This batch file will Force the Update Detection from the AU client by:
Echo 1. Stops the Automatic Updates Service (wuauserv)
Echo 2. Deletes the LastWaitTimeout registry key (if it exists)
Echo 3. Deletes the DetectionStartTime registry key (if it exists)
Echo 4. Deletes the NextDetectionTime registry key (if it exists)
Echo 5. Restart the Automatic Updates Service (wuauserv)

Pause
@echo on
net stop wuauserv
echo REGEDIT4 > temp.reg
echo. >> temp.reg
echo [HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update] >> temp.reg
echo "LastWaitTimeout"=- >> temp.reg
echo "DetectionStartTime"=- >> temp.reg
echo "NextDetectionTime"=- >> temp.reg
regedit /s temp.reg
del temp.reg
net start wuauserv

@echo off
Echo This AU client will now check for the Updates on the Local SUS Server.
Echo After 10-20 min have a look at C:\Window\Windows update.log
Pause

---

After 10-20 minutes check if update completes successfully in C:\WINNT\WindowsUpdate.log and restart machine (there are also some registry status keys you can monitor [4]).

After restart restore port mapping to make it possible for update to finish the job and issue the following command to speed up update process:
wuauclt /detectnow

If update icon flickers in system tray and doesn't propose to install new updates - stop WU service, delete C:\WINNT\SoftwareDistribution and start update process again. In short:
net stop wuauserv
rmdir /s /q C:\WINNT\SoftwareDistribution
net start wuauserv
wuauclt /detectnow

Copy setup_windows_update_localhost.reg to setup_windows_update_intranet.reg and edit the latter to use http://intranet:8530 (example server) for subsequent updates. Port mapping is not needed from now on, so shutdown the software.

In case of one-time update you probably do not need to keep link with WSUS server on this machine. Then after repeated restarts and updates to make sure everything is installed successfully, launch setup_windows_update_default.reg file to remove WSUS server settings from the registry.

---[setup_windows_update_localhost.reg]
Windows Registry Editor Version 5.00

[-HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate]

---


References:

[1] Trivial Proxy http://www.xrayapp.com/trivialproxy/
[2] AnalogX PortMapper http://www.analogx.com/contents/download/network/pmapper.htm
[3] WSUS: Script to force update detection http://support.microsoft.com/?kbid=555453
[4] Interpreting AUState Values http://susserver.com/FAQs/FAQ-InterpretingAUStateValues.asp

Monday, January 16, 2006

MSXML XSLT processor in JScript

Here is XSLT transformation engine for Windows in just 4k of JScript code using MSXML SDK. Copy the snippet below and paste into mbxsl.wsf file then launch it to get usage help.

MSXML is probably the fastest and the worst engine at the time being. Do not ask why - it's empirical. You can try to catch that feeling by getting Windows Scripting Host 5.6 SDK, MS XML 4.0 SDK and making a simple XSLT transformation engine that should output text result in specific encoding like windows-1251. There are several ways to make the engine and only one to make it work as expected. Ok, let's finish this fast:

.transformNode() always returns UTF-16 string (no way to convert/iconv it while writing)
.transformNodeToObject() requires output to be well-formed XML if the output is DOMDocument. I didn't find anything else in these SDK's to substitute in this field, so no luck with plain text output. You can use IStream interface described below, but you will not find neither IStream interface nor it's ADODB.Stream implementation reference in these SDKs. So, I didn't know anything about ADODB.Stream and thought there must be another method described in SDKs to do the task. I've found IXSLProcessor interface, which, unfortunately, doesn't allow me to save "encoding" header in xml declaration along with (correctly, btw) encoded data via opened TextStream. Even though I didn't need that header to output plain text at the first time, later it turned into a problem. How much did you understood so far? Consider how many garbage had filtered through my head before I came up with the solution.. Ok, after some google cache data mining on IXSLProcessor+IStream I've found Rob Shields page, which fortunately contained an example of IStream implementation. I probably stop for now and post a solution "how to implement a binary XSLT transformation engine in JScript with MSXML".

If you need to make correct XSLT tranformation in MSXML with correct encoding specified in xsl:output try this:
cscript mbxsl.wsf /xml:in.xml /xsl:filter.xsl /out:result.out

---cut----[mbxsl.wsf]

<?xml version="1.0" encoding="windows-1251"?>
<package xmlns="uri:wsf">
<job id="mbxsl">
<?job error="true" debug="false"?>
<runtime>
<description>
XSLT engine implementation in MS XML using Windows Scripting Host
by Max Belugin and anatoly techtonik
http://farplugins.svn.sourceforge.net/viewvc/farplugins/trunk/plugbase/
</description>
<named
name ="xml"
helpstring ="source xml"
type ="string"
required ="true"
/>
<named
name ="xsl"
helpstring ="template xsl"
type ="string"
required ="true"
/>
<named
name ="out"
helpstring ="output file/folder"
type ="string"
required ="false"
/>

<named
name ="split"
helpstring ="if true (+) then split output to files and place in "
type ="boolean"
required ="false"
/>
<example>
cscript //nologo mbxsl.wsf /xml:navy.hrd /xsl:hrd2css.xsl
</example>
</runtime>

<script language="JScript">
<![CDATA[
var xmlDoc // as DOMDocument
var xslDoc // as FreeThreadedDOMDocument (required by XSLTemplate)
var xslObj // as XSLTemplate
var xslProc // as XSLProcessor
var targetDoc // as DOMDocument
var args // as WshNamed
=WScript.Arguments.Named;
if(args.Exists("xml")&&args.Exists("xsl")){
// WScript.Echo(args("xml"));
// WScript.Echo(args("xsl"));
xmlDoc=new ActiveXObject("MSXML2.DOMDocument.4.0")
xslDoc=new ActiveXObject("MSXML2.FreeThreadedDOMDocument.4.0")
xmlDoc.async=false;
xslDoc.async=false;
xmlDoc.validateOnParse=false;
xslDoc.validateOnParse=false;
xmlDoc.load(args("xml"));
xslDoc.load(args("xsl"));
if (xmlDoc.parseError.errorCode != 0)
WScript.echo("XML parse error: " + "line " + xmlDoc.parseError.line +
" pos " + xmlDoc.parseError.linepos + " code " +
xmlDoc.parseError.errorCode + "\n" + xmlDoc.parseError.reason);
if (xslDoc.parseError.errorCode != 0)
WScript.echo("XSL parse error: " + "line " + xmlDoc.parseError.line +
" pos " + xmlDoc.parseError.linepos + " code "+
xmlDoc.parseError.errorCode + "\n" + xmlDoc.parseError.reason);

if(args("split")){
targetDoc=new ActiveXObject("MSXML2.DOMDocument.4.0")
targetDoc.async=false;
//targetDoc.preserveWhiteSpace=true;
xmlDoc.transformNodeToObject(xslDoc, targetDoc);
if(args.Exists("out")){
var fs // as FileSystemObject
=new ActiveXObject("Scripting.FileSystemObject");
var out // as Folder
=fs.GetFolder(args("out"));
var files // as IXMLDOMNodeList
=targetDoc.selectNodes("/files/file");
var i=new Enumerator(files);
for (;!i.atEnd();i.moveNext()){
var file// as IXMLDOMNode
=i.item();
var fileName=file.getAttribute("name");
//var outPath=fs.BuildPath(out.Path, fileName);
var outFile=out.CreateTextFile(fileName);
outFile.write(file.firstChild.xml);
outFile.close();
};

}else
WScript.echo(targetDoc.xml);
}else{
xslObj=new ActiveXObject("MSXML2.XSLTemplate.4.0")
xslObj.stylesheet=xslDoc;
xslProc=xslObj.createProcessor();
xslProc.input = xmlDoc;
// IStream is necessary to avoid conversion from to UTF-16 and
// losing encoding="windows-1251" attribute from xml declaration
if(!args.Exists("out")){
if (!xslProc.transform())
WScript.echo("transformation error");
WScript.echo(xslProc.output);
}else{
var oStream = new ActiveXObject ("ADODB.Stream");
oStream.Mode = 3; // adModeReadWrite
oStream.Type = 1; // adTypebinary
oStream.Open();
xslProc.output = oStream;
if (!xslProc.transform())
WScript.echo("transformatin error");
oStream.saveToFile(args("out"), 2); // adSaveCreateOverWrite
oStream.close();
}
};
} else {
WScript.Arguments.ShowUsage();
};
]]>
</script>
</job>
</package>

---cut----[mbxsl.wsf]

Problem description.
[1] Encoding issues using the MS XSLT engine
[2] transformNodeToObject method error

Tnx Rob Shields for ADODB.Stream usage example

Thursday, December 22, 2005

How to compile SQLite for Windows

Here you will see how to compile SQLite for Windows with MinGW and without TCL. For bots.

How to install MINGW
  1. Download necessary files from http://sourceforge.net/project/showfiles.php?group_id=2435
    At the moment these are:
    MSYS-1.0.10.EXE
    MinGW-5.0.0.EXE
  2. Install MSYS ("minimal system" - some kind of unix emulator) to D:\MSYS (for example). If you see prompt to execute post install instructions you may say no, because you probably do not have MinGW or MSYS previously installed.
  3. Install MinGW (with GPP option necessary for SQLite) to D:\MSYS\mingw
    Installator didn't work with my proxy, so each file used by MinGW-5.0.0.EXE was downloaded separately and placed into installator's directory. Some downloaded files with newer versions than installer could understand were renamed to make it happy. List of packages required by MinGW-5.0.0.exe installer:
    binutils-2.15.91-20040904-1.tar.gz
    gcc-core-3.4.2-20040916-1.tar.gz
    mingw-runtime-3.9.tar.gz (renamed to mingw-runtime-3.8.tar.gz)
    w32api-3.5.tar.gz (renamed to w32api-3.3.tar.gz)
    gcc-g++-3.4.2-20040916-1.tar.gz (C++ preprocessor required by SQLite configure)
  4. After installer finished your environment is ready. Launch MSYS to start working. Home directory in this case ("cd ~") is located at D:\MSYS\home\yourusername if you need to find out where to place you files from windows to be found in MSYS.
  5. Optionally if you will need to run additional developer tools such as autoconf or automake, these are available through separate msysDTK-1.0.1.exe installer from the sourceforge download page above.
How to compile SQLite
  1. Download SQLite and upack it into subdirectory "sqlite" (for example) of your MSYS home directory (something like D:\MSYS\home\techtonik in my case)
  2. Launch MSYS
  3. cd sqlite
  4. mkdir bld; cd bld
  5. ../configure --disable-tcl
  6. This will create "tsrc" directory with all necessary files to compile SQLite
    make target_source
    cd tsrc
  7. Since there is no TCL libraries, tclsqlite.c should be removed to avoid errors during compilation (this is purely optional)
    mv tclsqlite.c tclsqlite.c.unused
  8. Compile sources with a some optimization (-O2 flag).
    gcc -O2 -DNDEBUG=1 -DTHREADSAFE=1 -c *.c
  9. Link compiled object (*.o) files to make sqlite3.exe (-s tell gcc to strip additional info used for debug)
    gcc -s -o sqlite3 *.o
  10. Remove shell.o object file used as entry point for command line utility. Useless for DLL.
    rm shell.o
  11. Link compiled object (*.o) files to make .dll The command below tells GCC to link all object files into one shared library (for windows it is .dll) and make functions listed in .def available for use to external programs (export). sqlite3.def file can be found in official sqlitedll-x_x_x.zip distribution
    gcc -shared -s -o sqlite3.dll *.o *.def
  12. There is one more thing you may need in future if you would like to compile other programs with SQLite support. It is import library. To make one for MinGW (called libsqlite3.dll.a) link .dll with command
    gcc -shared -s -o sqlite3.dll *.o *.def -Wl,--out-implib,libsqlite3.dll.a
  13. Wrap release.
    mkdir ../release
    cp sqlite3.dll sqlite3.exe libsqlite3.dll.a sqlite3.h ../release
    cd ../release
That's all. If you want reduce the size of .dll by several kbytes use strip --strip-unneeded command. By default strip will remove all symbols from file, but with this key it doesn't remove information needed for .dll relocation.

Ready to use files for SQLite 3.3.17 are available at http://rainforce.org/sqlite/

Saturday, December 03, 2005

About Zend and PHP сollaboration

What you are going to read in this post (if you decide to continue) is my sole IMHO, gathered from those little bits of information I ocassionally catch on the web. I do not communicate with developes and my vision is often filtered through the prism of negative positivism and critic to make the dark side of the facts more visible (I am not original here). Somebody may call it FUD, but on the other side somebody has to be evil and spit acid on behalf of good. Here is the vision of the Zend story, which may or may not be true.

Zend makes money. That's all I can say. Not a bad goal, but it took wrong side IMHO. Perhaps it was since the beginning, when folks decided to sell engine itself and not PHP, but the situation changed with PHP took over the web and Zend has to admit it. Quickly guys realized that engine itself is not very popular and PHP quickly overruns the budget for its support and development on a full time basis. Perhaps it was the time when Zend appeared on the web scene proposing PHP services, such as famous Zend Studio. Well, I've never used Zend Studio, because it was just too expensive for me and simple Far Manager + Colorer was more than sufficient for my needs. Of course, Zend needs money to support all PHP.NET infrastructure, developers, to promote it's products, but.. it seems that Zend more concerned with the money than with anything else, making chaotic PHP users community a fortunate third party instrument for their goals. Perhaps they were dragged to the "money for money" road by these "enterprise" mindmakers and business suits, but this doesn't make things better. It is rather obvious that Zend is under the pressure these days. The great struggle enforces Zend to take a decision and I do not think this decision will be good for those, who was inspired by README.CVS-RULES to elaborate in the past. Neutrality was the primary guide of core PHP developers from start and we all were excited to participate and make this world a little better.

Global trust what could be easy to learn is also easy to lose. I do not believe PHP license was intended to hurt or protect anyone's rights, but rather expressed trust to core PHP developers and to their intentions from those, who released scripts with it's terms. But GPL folks from one side and lawsuits other both want to formalize development process to degree when human relations will be absolutely ruined with an aim to feed servants of law and a bigger players behind. After some experiments with communities I must admit that your professional skills do not play any significant role when there is a real person, who just doesn't like you on receiver side - but this is another story with some peanuts about commercial social engineering, though some aspects of it directly influence current state of business with Zend. I'd like to recommend this company to provide more visibility into future plans, feel responsibility for the buzzwords, support PHP.NET and collaboration from developer's side.

Detect installed MSXML version

MSXML version check can be done with the help of online MSXML version sniffer, which works from within IE. See this link for example. Unfortunately, it does not tell you the exact version of MSXML with service pack installed. If you need service pack info - you may use the following information from Microsoft and the script below.

----- msxml_detect_versions.js ---
// display versions of msxml .dll files

var fso = new ActiveXObject("Scripting.FileSystemObject");
var WindowsFolder = 0 , SystemFolder = 1, TemporaryFolder = 2;
var system32 = fso.GetSpecialFolder(SystemFolder);

function ReportSystem32File(filename)
{
var s = filename;
var system32file = fso.BuildPath(system32, filename);
if (fso.FileExists(system32file))
s += " \tversion\t " + fso.GetFileVersion(system32file);
else
s += " \tdoes not exist";
return(s);
}

WScript.Echo(
ReportSystem32File("msxml.dll") + "\n" +
ReportSystem32File("msxml2.dll") + "\n" +
ReportSystem32File("msxml3.dll") + "\n" +
ReportSystem32File("msxml4.dll") + "\n" +
ReportSystem32File("msxml5.dll") + "\n" +
ReportSystem32File("msxml6.dll") + "\n" +
"\n" + "For information about versions of MS XML parser " +
"\n" + "corresponding to above file version numbers see " +
"\n" + "http://support.microsoft.com/kb/269238/EN-US/"
);
// techtonik//php.net $Date$
----- msxml_detect_versions.js ---

Wednesday, October 26, 2005

Zend, Eclipse and DLTK

Why abandon PHP cause? I am a little bit concerned about Zend future and direction PHP takes from. I would like to see development of PHP itself more vibrant in terms of community support. Quality assurance technologies, new development instrumentation to overcome political PHP problems when new released versions are not compatible with the old ones - when API and quirks notices are growing bigger and bigger without any hope of refactoring. Majority of the tools available from PHP.NET site are outdated and even if considered minimally useful they definitely could be better. If only Zend could support full-time developers to support PHP.NET and community services like IBM does for Eclipse.

Zend is missing opportunities drowned in self-promotions in enterprise market. Should be good for them, but it needs to pay more attention to developers and PHP community. Zend seems to be a bunch of good folks, but also the ones who either very cautious, or do not have trust or resources to invest in communiy projects, or lack some essential management, market analysis skills used to keep in touch with the rest of the world to forecast and interpret the vision of PHP from outside.

Take a look at this initiatives that are quite dead and seem to be promoted only as a plug for community demands.

https://www.zend.com/php-collaboration/
http://www.zend.com/phpide/

I could only hope that Zend could crawl out from this crisis and receive good support from developers and financial companies to promote and bring joy of easy and fast application development to many of us. It is hard for a company who survives by selling products which ease the development, but another models of evolution are still possible.

I hope that the example of Eclipse success will make Zend think about elaboration on a global scale to reach common goals. Like support in the following project.
http://www.eclipse.org/proposals/dltk/

As for PHP freelancer and enthusiast if I'd be asked "Who is Zend?" I would tell "Zend's dead, baby. Zend's dead."

Tuesday, October 18, 2005

PHP PEAR File SearchReplace

If you're not a PHP programmer the following info is a complete waste of time. You've been warned.

To tell the truth I've almost finished writing another "Why I dislike PEAR?" article, but under a fit of creativity I've completely forgot what I want to tell in the first place. So I stripped all spicy stuff and left only fruits.

Let me introduce my tool http://pear.php.net/package/File_SearchReplace/ written by Richard Heyes (http://www.phpguru.org/ ). Very useful PHP library for filtering files, bunch of .html or sources all at once. It is a low-level analog of numerous "Search and Replace" utilities. Well written articles about how to use it are here:
http://www.pcworld.idg.com.au/index.php/id;1186595404;pp;2;fp;2;fpid;76768
http://pear.php.net/manual/en/package.filesystem.file-searchreplace.php

The main flaw in this library that it doesn't support Search without Replace. By calling doSearch() you immediately start write modifications. No preview, no read-only grep. This may become quite dangerous and was meant to be changed in version 2 or even in another new package that was proposed , but the proposal has been rejected. That's why I disliked PEAR. There is no way to change API even though this API is a bug in the first place. It was also not possible to release new refactored version with new API without the approval. You have to be nice and polite and even then nobody guarantees that your arguments are acceptable. The method for search without replace could be easily added if not this PEAR bureaucracy and backwards compatibility talks.

Since a lot of time has passed and I still can't release a package, it will be good to say something about planned modifications and let people use proper API to avoid possible conflicts in the future. I am not able to support PEAR packages anymore, because I've switched to SVN and Python (sorry), moreover and I do not know any convenient CVS tools that work through the proxy, but just in case somebody will get the courage to take over development my last advice to those who still use this library.

If you intend to use this package in future - use doReplace() method instead of doSearch(). doReplace() method is not documented, because 1.0.1 doesn't have it, but it more accurately describes what SearchReplace() do. It may happen that in futer doSearch() will be used just for search and statistics collection. Now that you've been warned you may edit your scripts with the goal for better future compatibility in mind (sorry, couldn't resist to include usual marketing crap).