--- /dev/null
+
+CC =cl -nologo
+CFLAGS =-Ox -W3 -MD
+LD =link -nologo
+LDFLAGS=-dll
+
+DEFS =-DUSE_TCL_STUBS
+INC =-Ic:\opt\tcl\include
+LIBS =c:\opt\tcl\lib\tclstub84.lib ws2_32.lib
+
+all: resolver.exe resolvchk.exe tclresolver.dll tclresolver2.dll tclresolver3.dll
+
+resolver.exe: resolver.c
+ $(CC) $(CFLAGS) -o $@ $**
+
+resolvchk.exe: resolvchk.c
+ $(CC) $(CFLAGS) -o $@ $**
+
+tclresolver.dll: tclresolver.obj
+ $(LD) $(LDFLAGS) -out:$@ $(LIBS) $**
+
+tclresolver2.dll: tclresolver2.obj
+ $(LD) $(LDFLAGS) -out:$@ $(LIBS) $**
+
+tclresolver3.dll: tclresolver3.obj
+ $(LD) $(LDFLAGS) -out:$@ $(LIBS) $**
+
+.c.obj::
+ $(CC) $(CFLAGS) $(DEFS) $(INC) -c @<<
+$<
+<<
+
+clean:
+ @if exist tclresolver.obj del tclresolver.obj
+ @if exist tclresolver2.obj del tclresolver2.obj
+ @if exist tclresolver3.obj del tclresolver3.obj
+
+realclean: clean
+ @if exist tclresolver.dll del tclresolver.dll
+ @if exist tclresolver.exp del tclresolver.exp
+ @if exist tclresolver.lib del tclresolver.lib
+ @if exist tclresolver2.dll del tclresolver2.dll
+ @if exist tclresolver2.exp del tclresolver2.exp
+ @if exist tclresolver2.lib del tclresolver2.lib
+ @if exist tclresolver3.dll del tclresolver3.dll
+ @if exist tclresolver3.exp del tclresolver3.exp
+ @if exist tclresolver3.lib del tclresolver3.lib
+ @if exist resolver.exe del resolver.exe
+ @if exist resolvchk.exe del resolvchk.exe
--- /dev/null
+if {[file exists tclresolver3.dll]} {
+ load tclresolver3.dll Tclresolver
+} else {
+ package require Tclresolver
+}
+
+set hostname [info hostname]
+set uid 0
+
+proc every {aidname n args} {
+ upvar #0 $aidname aid
+ catch $args
+ set aid [after $n [eval list every $aidname $n $args]]
+}
+
+proc display {s} {
+ if {[info exists ::tk_library]} {
+ .t insert end $s
+ .t see end
+ } else {
+ puts -nonewline $s ; flush stdout
+ }
+}
+
+proc lookup {} {
+ global hostname
+ set aidname ::aid[incr ::uid]
+ upvar $aidname aid
+ every $aidname 25 display .
+ set ips [resolve $hostname]
+ after cancel $aid
+ unset aid
+ display "\n$ips\n"
+}
+
+proc gui {} {
+ wm title . "Tclresolver [package provide Tclresolver]"
+
+ frame .f
+ label .l -text Hostname -underline 0
+ entry .e -textvariable ::hostname
+ button .h -text Lookup -width -12 -underline 0 -command lookup \
+ -default active
+ button .x -text Exit -width -12 -underline 1 -command {destroy .}
+ text .t -yscrollcommand {.s set} -height 10
+ scrollbar .s -command {.t yview}
+
+ pack .l -in .f -side left
+ pack .e -in .f -side left -expand 1 -fill x -padx 4
+ pack .h .x -in .f -side left
+
+ grid .t .s -sticky news
+ grid .f - -sticky news
+ grid rowconfigure . 0 -weight 1
+ grid columnconfigure . 0 -weight 1
+
+ bind . <Alt-h> {focus .e}
+ bind . <Alt-l> {focus .h}
+ bind . <Alt-x> {focus .x}
+ bind . <Return> {.h invoke}
+ bind . <Escape> {.x invoke}
+ if {[string equal [tk windowingsystem] "win32"]} {
+ bind . <Control-o> {console show}
+ }
+
+ tkwait visibility .t
+ after 100 {set ::wait 1} ; tkwait variable ::wait
+ focus .e
+ tkwait window .
+ return
+}
+
+
+if {[package provide Tk] != {}} {
+ gui
+} else {
+ puts "tclresolver [package provide Tclresolver]"
+ set hostname [lindex $argv 0]
+ lookup
+}
--- /dev/null
+# resolver.tcl - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
+#
+#
+#
+# -------------------------------------------------------------------------
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+# -------------------------------------------------------------------------
+# $Id$
+
+namespace eval ::resolver {
+ variable version 1.0.0
+ variable channel
+ namespace export gethostbyname
+}
+
+# -------------------------------------------------------------------------
+
+proc ::resolver::Read {} {
+ variable done
+ variable channel
+ if {[eof $channel]} {
+ fileevent $channel readable {}
+ return
+ }
+ set done [gets $channel]
+}
+
+proc ::resolver::Write {host} {
+ variable channel
+ fileevent $channel writable {}
+ puts $channel $host
+ flush $channel
+}
+
+proc ::resolver::Init {} {
+ variable done
+ variable channel
+ set cmd [auto_execok resolver]
+ set channel [open |$cmd w+]
+ fconfigure $channel -buffering line -blocking 1
+}
+
+proc ::resolver::Shutdown {} {
+ variable channel
+ catch {close $channel} msg
+}
+
+proc ::resolver::gethostbyname {host} {
+ variable done
+ variable channel
+ set done {}
+ fileevent $channel readable [list [namespace origin Read]]
+ fileevent $channel writable [list [namespace origin Write] $host]
+ vwait [namespace current]::done
+ return $done
+}
+
+# package initialization - we will create the child resolver process on loading
+# this package.
+#
+namespace eval ::resolver {
+ if {![info exists channel]} {
+ Init
+ }
+}
+
+# -------------------------------------------------------------------------
+
+package provide resolver $::resolver::version
+
+# -------------------------------------------------------------------------
+
+if {! $tcl_interactive} {
+ foreach host {binky aol.com www.microsoft.com slashdot.org} {
+ set a [resolver::gethostbyname $host]
+ foreach h $a {
+ puts [list $host $h]
+ }
+ }
+
+ # We don't need to do this explicitly. It'll close when the app exits.
+ # Resolver::Shutdown
+}
--- /dev/null
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define BUFSIZE 4096
+
+typedef struct {
+ HANDLE hReady;
+ HANDLE hDone;
+ HANDLE hIn;
+ HANDLE hOut;
+ LPBYTE pData;
+ BOOL bRunning;
+} LookupInfo;
+
+static LPCTSTR LookupHostname(LookupInfo * pLookupInfo, LPCTSTR szHostname);
+static DWORD WINAPI Reader(LPVOID clientData);
+static BOOL CreateChildProcess(HANDLE hChildStdoutWr, HANDLE hChildStdinRd);
+static void ErrorExit(LPTSTR);
+static void Win32Error(FILE *fp, LPCTSTR szMessage, HRESULT hr);
+
+DWORD
+_tmain(int argc, LPCTSTR argv[])
+{
+ HANDLE hChildStdinWr, hChildStdinWrDup, hChildStdoutRd, hChildStdoutRdDup;
+ HANDLE hChildStdinRd, hChildStdoutWr;
+ LookupInfo lookupinfo;
+ HANDLE hReaderThread = INVALID_HANDLE_VALUE;
+ DWORD idReaderThread = 0;
+ SECURITY_ATTRIBUTES saAttr;
+ BOOL fSuccess;
+ LPCTSTR szAddress;
+ int n;
+
+ if (argc < 2) {
+ ErrorExit(_T("usage: resolvchk hostname ?hostname ...?"));
+ }
+
+ // Set the bInheritHandle flag so pipe handles are inherited.
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ // Create a pipe for the child process's STDOUT.
+ if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
+ ErrorExit("Stdout pipe creation failed\n");
+
+ // Create noninheritable read handle and close the inheritable read
+ // handle.
+ fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
+ GetCurrentProcess(), &hChildStdoutRdDup,
+ 0, FALSE,
+ DUPLICATE_SAME_ACCESS);
+ if( !fSuccess )
+ ErrorExit("DuplicateHandle failed");
+ CloseHandle(hChildStdoutRd);
+
+ // Create a pipe for the child process's STDIN.
+ if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
+ ErrorExit("Stdin pipe creation failed\n");
+
+ // Duplicate the write handle to the pipe so it is not inherited.
+ fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
+ GetCurrentProcess(), &hChildStdinWrDup,
+ 0, FALSE, // not inherited
+ DUPLICATE_SAME_ACCESS);
+ if (! fSuccess)
+ ErrorExit("DuplicateHandle failed");
+
+ CloseHandle(hChildStdinWr);
+
+ // Create an event for lookup synch.
+ lookupinfo.hReady = CreateEvent(NULL, FALSE, FALSE, NULL);
+ lookupinfo.hDone = CreateEvent(NULL, FALSE, FALSE, NULL);
+ lookupinfo.hIn = hChildStdoutRdDup;
+ lookupinfo.hOut = hChildStdinWrDup;
+ lookupinfo.bRunning = TRUE;
+ lookupinfo.pData = (LPBYTE)malloc(BUFSIZE);
+
+ // Create reader thread.
+ hReaderThread = CreateThread(NULL, 0, Reader, &lookupinfo,
+ 0, &idReaderThread);
+
+ // Now create the child process.
+ fSuccess = CreateChildProcess(hChildStdoutWr, hChildStdinRd);
+ if (! fSuccess)
+ ErrorExit("Create process failed");
+
+ // Perform the address lookup.
+ for (n = 1; n < argc; n++) {
+ szAddress = LookupHostname(&lookupinfo, argv[n]);
+ _ftprintf(stdout, _T("'%s'\n"), szAddress);
+ }
+
+ lookupinfo.bRunning = FALSE;
+ free((LPBYTE)lookupinfo.pData);
+
+ // Close the pipe handle so the child process stops reading and exits.
+ if (! CloseHandle(hChildStdinWrDup))
+ ErrorExit("Close pipe failed");
+ if (!CloseHandle(hChildStdoutWr))
+ ErrorExit("CloseHandle failed");
+
+ return 0;
+}
+
+static LPCTSTR
+LookupHostname(LookupInfo *pLookupInfo, LPCTSTR szHostname)
+{
+ DWORD dwWrote;
+ BOOL br;
+
+ ZeroMemory(pLookupInfo->pData, BUFSIZE);
+ lstrcpy((LPTSTR)pLookupInfo->pData, szHostname);
+ lstrcat((LPTSTR)pLookupInfo->pData, _T("\n"));
+ br = WriteFile(pLookupInfo->hOut, pLookupInfo->pData,
+ lstrlen((LPTSTR)pLookupInfo->pData) * sizeof(TCHAR),
+ &dwWrote, NULL);
+ if (br)
+ br = FlushFileBuffers(pLookupInfo->hOut);
+ if (br) {
+ ZeroMemory(pLookupInfo->pData, BUFSIZE);
+ SetEvent(pLookupInfo->hReady);
+ WaitForSingleObject(pLookupInfo->hDone, INFINITE);
+ }
+ if (!br) {
+ Win32Error(stderr, _T("lookup hostname"), GetLastError());
+ }
+ return (LPCTSTR)pLookupInfo->pData;
+}
+
+static DWORD WINAPI
+Reader(LPVOID clientData)
+{
+ LookupInfo *pLookupInfo = (LookupInfo *)clientData;
+ DWORD dwErr = 0, dwRead = 0;
+ BOOL br;
+
+ while (pLookupInfo->bRunning) {
+ dwErr = WaitForSingleObject(pLookupInfo->hReady, 500);
+ if (dwErr == WAIT_OBJECT_0) {
+ br = ReadFile(pLookupInfo->hIn, pLookupInfo->pData,
+ BUFSIZE, &dwRead, NULL);
+
+ if (dwRead > 0) {
+ LPTSTR p = (LPTSTR)pLookupInfo->pData;
+ for (p = p + lstrlen(p) - 1;
+ p > pLookupInfo->pData && _istspace(*p);
+ p--)
+ ;
+ *++p = 0;
+ }
+ SetEvent(pLookupInfo->hDone);
+ } else {
+ Win32Error(stderr, _T("reader wait"), dwErr);
+ }
+ }
+ return 0;
+}
+
+static BOOL
+CreateChildProcess(HANDLE hChildStdoutWr, HANDLE hChildStdinRd)
+{
+ PROCESS_INFORMATION piProcInfo;
+ STARTUPINFO siStartInfo;
+ BOOL bFuncRetn = FALSE;
+
+ // Set up members of the PROCESS_INFORMATION structure.
+ ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
+
+ // Set up members of the STARTUPINFO structure.
+ ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.hStdError = hChildStdoutWr;
+ siStartInfo.hStdOutput = hChildStdoutWr;
+ siStartInfo.hStdInput = hChildStdinRd;
+ siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ // Create the child process.
+ bFuncRetn = CreateProcess(NULL,
+ "resolver", // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ 0, // creation flags
+ NULL, // use parent's environment
+ NULL, // use parent's current directory
+ &siStartInfo, // STARTUPINFO pointer
+ &piProcInfo); // receives PROCESS_INFORMATION
+
+ if (bFuncRetn == 0) {
+ ErrorExit("CreateProcess failed");
+ return FALSE;
+ } else {
+ CloseHandle(piProcInfo.hProcess);
+ CloseHandle(piProcInfo.hThread);
+ return bFuncRetn;
+ }
+}
+
+static void
+ErrorExit (LPTSTR lpszMessage)
+{
+ _ftprintf(stderr, _T("%s\n"), lpszMessage);
+ ExitProcess(0);
+}
+
+static void
+Win32Error(FILE *fp, LPCTSTR szMessage, HRESULT hr)
+{
+ LPTSTR lpBuffer = NULL;
+ DWORD dwLen = 0;
+
+ dwLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, (DWORD)hr, LANG_NEUTRAL,
+ (LPTSTR)&lpBuffer, 0, NULL);
+ if (dwLen < 1) {
+ dwLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ _T("code 0x%1!08X!%n"), 0, LANG_NEUTRAL,
+ (LPTSTR)&lpBuffer, 0, (va_list *)&hr);
+ }
+ _ftprintf(fp, _T("%s"), szMessage);
+ if (dwLen > 0) {
+ _ftprintf(fp, _T(": "));
+ _ftprintf(fp, lpBuffer);
+ }
+ LocalFree((HLOCAL)lpBuffer);
+}
--- /dev/null
+/* tclresolver.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
+ *
+ *
+ *
+ * $Id$
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include <tcl.h>
+
+#if _MSC_VER >= 1000
+#pragma comment(lib, "ws2_32")
+#endif
+
+#define BUFSIZE 4096
+#undef TCL_STORAGE_CLASS
+#define TCL_STORAGE_CLASS DLLEXPORT
+
+static const char ASSOC_KEY[] = "tclresolver_key";
+
+EXTERN int Tclresolver_Init(Tcl_Interp *interp);
+EXTERN int Tclresolver_Unload(Tcl_Interp *interp);
+EXTERN int TclGetHostByName(LPSOCKADDR_IN saddrPtr,
+ const char *hostname, int port);
+
+typedef struct {
+ HANDLE hReader;
+ DWORD idReader;
+ HANDLE hReady;
+ HANDLE hDone;
+ LPBYTE pData;
+ BOOL bRunning;
+ Tcl_ThreadId tclid;
+ Tcl_Command command;
+} LOOKUPINFO, *LPLOOKUPINFO;
+
+typedef struct {
+ Tcl_Event header;
+ LPLOOKUPINFO info;
+} RESOLVEREVENT, *LPRESOLVEREVENT;
+
+static Tcl_ObjCmdProc TclresolverObjCmd;
+static Tcl_ExitProc Terminate;
+static int Initialize(Tcl_Interp *interp, LPLOOKUPINFO pLookupInfo);
+static LPCTSTR LookupHostname(LPLOOKUPINFO pLookupInfo, LPCTSTR szHostname);
+static DWORD WINAPI Reader(LPVOID clientData);
+static void QueueEvent(LPLOOKUPINFO pLookupInfo);
+static int ResolverEventProc(Tcl_Event *evPtr, int flags);
+static int GetAddress(char *query);
+static Tcl_Obj *Win32Error(const char * szPrefix, HRESULT hr);
+
+/* ---------------------------------------------------------------------- */
+
+int
+Tclresolver_Init(Tcl_Interp *interp)
+{
+ int initialized = 0;
+ LPLOOKUPINFO pLookupInfo = NULL;
+
+#ifdef USE_TCL_STUBS
+ if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
+ return TCL_ERROR;
+ }
+#endif
+
+ pLookupInfo = (LPLOOKUPINFO)Tcl_Alloc(sizeof(LOOKUPINFO));
+ if (pLookupInfo == NULL) {
+ Tcl_SetResult(interp, "out of memory", TCL_STATIC);
+ return TCL_ERROR;
+ }
+ ZeroMemory(pLookupInfo, sizeof(LOOKUPINFO));
+
+ if (Initialize(interp, pLookupInfo) != TCL_OK) {
+ Tcl_Free((char*)pLookupInfo);
+ return TCL_ERROR;
+ }
+ pLookupInfo->tclid = Tcl_GetCurrentThread();
+ pLookupInfo->command = Tcl_CreateObjCommand(interp, "resolve",
+ TclresolverObjCmd,
+ (ClientData)pLookupInfo,
+ (Tcl_CmdDeleteProc *)NULL);
+ Tcl_CreateExitHandler(Terminate, (ClientData)pLookupInfo);
+ Tcl_SetAssocData(interp, ASSOC_KEY, NULL, (ClientData)pLookupInfo);
+ return Tcl_PkgProvide(interp, "Tclresolver", "1.2.0");
+}
+
+EXTERN int
+Tclresolver_SafeInit(Tcl_Interp *interp)
+{
+ return Tclresolver_Init(interp);
+}
+
+int
+Tclresolver_Unload(Tcl_Interp *interp)
+{
+ LPLOOKUPINFO pLookupInfo = NULL;
+ pLookupInfo = (LPLOOKUPINFO)Tcl_GetAssocData(interp, ASSOC_KEY, NULL);
+ Tcl_DeleteCommandFromToken(interp, pLookupInfo->command);
+ Terminate((ClientData)pLookupInfo);
+ Tcl_SetAssocData(interp, ASSOC_KEY, NULL, (ClientData)NULL);
+ return TCL_OK;
+}
+
+static int
+TclresolverObjCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[])
+{
+ LPLOOKUPINFO pLookupInfo = (LPLOOKUPINFO)clientData;
+ Tcl_Obj *resObj = NULL;
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, objc, objv, "hostname");
+ return TCL_ERROR;
+ }
+ resObj = Tcl_NewStringObj(LookupHostname(pLookupInfo,
+ Tcl_GetString(objv[1])), -1);
+ Tcl_SetObjResult(interp, resObj);
+ return TCL_OK;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int
+Initialize(Tcl_Interp *interp, LPLOOKUPINFO pLookupInfo)
+{
+ WSADATA wsd;
+
+ OutputDebugString(_T("Initialize\n"));
+
+ /* Initialize windows sockets. */
+ if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
+ Tcl_Obj *errObj = Win32Error("WSAStartup error", GetLastError());
+ Tcl_SetObjResult(interp, errObj);
+ return TCL_ERROR;
+ }
+
+ if (LOBYTE(wsd.wVersion) != 2 ||
+ HIBYTE(wsd.wVersion) != 2) {
+ Tcl_SetResult(interp,
+ "error: failed to load a compatible winsock version", TCL_STATIC);
+ WSACleanup();
+ return TCL_ERROR;
+ }
+
+ /* Create synchronization events */
+ pLookupInfo->hReady = CreateEvent(NULL, FALSE, FALSE, NULL);
+ pLookupInfo->hDone = CreateEvent(NULL, FALSE, FALSE, NULL);
+ pLookupInfo->bRunning = TRUE;
+ pLookupInfo->pData = (LPBYTE)Tcl_Alloc(BUFSIZE);
+
+ /* Create the reader thread */
+ pLookupInfo->hReader = CreateThread(NULL, 0, Reader, pLookupInfo,
+ 0, &pLookupInfo->idReader);
+
+ return TCL_OK;
+}
+
+static void
+Terminate(ClientData clientData)
+{
+ LPLOOKUPINFO pLookupInfo = (LPLOOKUPINFO)clientData;
+ OutputDebugString(_T("Terminate\n"));
+ pLookupInfo->bRunning = FALSE;
+ Tcl_DeleteExitHandler(Terminate, clientData);
+ WaitForSingleObject(pLookupInfo->hReader, 400);
+ CloseHandle(pLookupInfo->hReader);
+ CloseHandle(pLookupInfo->hReady);
+ CloseHandle(pLookupInfo->hDone);
+ Tcl_Free(pLookupInfo->pData);
+ Tcl_Free((LPBYTE)pLookupInfo);
+ WSACleanup();
+}
+
+static LPCTSTR
+LookupHostname(LPLOOKUPINFO pLookupInfo, LPCTSTR szHostname)
+{
+ DWORD dwWait;
+
+ /* copy the hostname into out buffer */
+ ZeroMemory(pLookupInfo->pData, BUFSIZE);
+ lstrcpy((LPTSTR)pLookupInfo->pData, szHostname);
+
+ /* notify the reader thread that it should begin reading */
+ SetEvent(pLookupInfo->hReady);
+
+ /* Run a tcl event loop while we wait for the answer */
+ while (1) {
+ Tcl_DoOneEvent(0); /* when tk loaded */
+ //Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT);
+ OutputDebugString("done one event\n");
+ dwWait = WaitForSingleObject(pLookupInfo->hDone, 0);
+ if (dwWait == WAIT_OBJECT_0)
+ break;
+ }
+
+ return (LPCTSTR)pLookupInfo->pData;
+}
+
+static DWORD WINAPI
+Reader(LPVOID clientData)
+{
+ LPLOOKUPINFO pLookupInfo = (LPLOOKUPINFO)clientData;
+ DWORD dwErr = 0, dwRead = 0;
+
+ while (pLookupInfo->bRunning) {
+ dwErr = WaitForSingleObject(pLookupInfo->hReady, 500);
+ if (dwErr == WAIT_OBJECT_0) {
+ GetAddress((char *)pLookupInfo->pData);
+ QueueEvent(pLookupInfo);
+ }
+ }
+ return 0;
+}
+
+static void
+QueueEvent(LPLOOKUPINFO pLookupInfo)
+{
+ LPRESOLVEREVENT evPtr;
+ OutputDebugString(_T("QueueEvent\n"));
+ evPtr = (LPRESOLVEREVENT)Tcl_Alloc(sizeof(RESOLVEREVENT));
+ evPtr->header.proc = ResolverEventProc;
+ evPtr->header.nextPtr = NULL;
+ evPtr->info = pLookupInfo;
+ Tcl_ThreadQueueEvent(pLookupInfo->tclid,
+ (Tcl_Event *)evPtr, TCL_QUEUE_TAIL);
+}
+
+static int
+ResolverEventProc(Tcl_Event *evPtr, int flags)
+{
+ LPRESOLVEREVENT revPtr = (LPRESOLVEREVENT)evPtr;
+ OutputDebugString(_T("ResolverEventProc\n"));
+ SetEvent(revPtr->info->hDone);
+ return 1;
+}
+
+static Tcl_Obj *
+Win32Error(const char * szPrefix, HRESULT hr)
+{
+ Tcl_Obj *msgObj = NULL;
+ char * lpBuffer = NULL;
+ DWORD dwLen = 0;
+
+ dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, (DWORD)hr, LANG_NEUTRAL,
+ (LPTSTR)&lpBuffer, 0, NULL);
+ if (dwLen < 1) {
+ dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ "code 0x%1!08X!%n", 0, LANG_NEUTRAL,
+ (LPTSTR)&lpBuffer, 0, (va_list *)&hr);
+ }
+
+ msgObj = Tcl_NewStringObj(szPrefix, -1);
+ if (dwLen > 0) {
+ char *p = lpBuffer + dwLen - 1; /* remove cr-lf at end */
+ for ( ; p && *p && isspace(*p); p--)
+ ;
+ *++p = 0;
+ Tcl_AppendToObj(msgObj, ": ", 2);
+ Tcl_AppendToObj(msgObj, lpBuffer, -1);
+ }
+ LocalFree((HLOCAL)lpBuffer);
+ return msgObj;
+}
+
+static int
+GetAddress(char *query)
+{
+ struct addrinfo hints = {0};
+ struct addrinfo *res = NULL;
+ char *hostname, *p, *q;
+ int r = 0, nc = 0;
+
+ /* trim whitespace */
+ for (p = query; *p && isspace(*p); p++)
+ ;
+ for (q = p + strlen(p) - 1; q > p && *q && isspace(*q); q--)
+ ;
+ *++q = 0;
+ hostname = p;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_socktype = SOCK_STREAM;
+ r = getaddrinfo(hostname, "", &hints, &res);
+ *query = 0;
+
+ if (r != 0) {
+ Tcl_Obj *o = Win32Error("error", WSAGetLastError());
+ strcpy(query, Tcl_GetString(o));
+ Tcl_DecrRefCount(o);
+ } else {
+ struct addrinfo *resPtr = res;
+ for (resPtr = res; resPtr != NULL; resPtr = resPtr->ai_next) {
+ char name[NI_MAXHOST];
+
+ getnameinfo(resPtr->ai_addr, resPtr->ai_addrlen,
+ name, NI_MAXHOST,
+ NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+
+ if (resPtr != res)
+ strcat(query, " ");
+ strcat(query, name);
+ }
+ freeaddrinfo(res);
+ }
+ return r;
+}
+
+
--- /dev/null
+/* tclresolver.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
+ *
+ * Non-blocking name resolution.
+ *
+ * $Id$
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include <tcl.h>
+
+#if _MSC_VER >= 1000
+#pragma comment(lib, "ws2_32")
+#pragma comment(lib, "user32")
+#endif
+
+#define BUFSIZE 4096
+#define XWM_RESOLVE WM_APP + 1
+
+#undef TCL_STORAGE_CLASS
+#define TCL_STORAGE_CLASS DLLEXPORT
+
+static const char ASSOC_KEY[] = "tclresolver_key";
+
+EXTERN int Tclresolver_Init(Tcl_Interp *interp);
+EXTERN int Tclresolver_Unload(Tcl_Interp *interp);
+EXTERN int TclGetHostByName(LPSOCKADDR_IN saddrPtr,
+ const char *hostname, int port);
+
+typedef struct {
+ HANDLE hReader;
+ DWORD idReader;
+ Tcl_ThreadId tclid;
+ Tcl_Command command;
+} PKGINFO, *LPPKGINFO;
+
+typedef struct {
+ HANDLE lock;
+ LONG cookie;
+ char data[BUFSIZE];
+} QUERYINFO, *LPQUERYINFO;
+
+typedef struct {
+ Tcl_Event header;
+ LPQUERYINFO query;
+} RESOLVEREVENT, *LPRESOLVEREVENT;
+
+static Tcl_ObjCmdProc TclresolverObjCmd;
+static Tcl_ExitProc Terminate;
+static int Initialize(Tcl_Interp *interp, LPPKGINFO pkgPtr);
+static Tcl_Obj *LookupHostname(LPPKGINFO pkgPtr, const char *hostname);
+static DWORD WINAPI Reader(LPVOID clientData);
+static void QueueEvent(Tcl_ThreadId id, LPQUERYINFO pQuery);
+static int ResolverEventProc(Tcl_Event *evPtr, int flags);
+static int GetAddress(char *query);
+static Tcl_Obj *Win32Error(const char * szPrefix, HRESULT hr);
+
+/* ---------------------------------------------------------------------- */
+
+int
+Tclresolver_Init(Tcl_Interp *interp)
+{
+ int initialized = 0;
+ LPPKGINFO pkgPtr = NULL;
+
+#ifdef USE_TCL_STUBS
+ if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
+ return TCL_ERROR;
+ }
+#endif
+
+ pkgPtr = (LPPKGINFO)Tcl_Alloc(sizeof(PKGINFO));
+ if (pkgPtr == NULL) {
+ Tcl_SetResult(interp, "out of memory", TCL_STATIC);
+ return TCL_ERROR;
+ }
+ ZeroMemory(pkgPtr, sizeof(PKGINFO));
+
+ if (Initialize(interp, pkgPtr) != TCL_OK) {
+ Tcl_Free((char*)pkgPtr);
+ return TCL_ERROR;
+ }
+ pkgPtr->tclid = Tcl_GetCurrentThread();
+ pkgPtr->command = Tcl_CreateObjCommand(interp, "resolve",
+ TclresolverObjCmd,
+ (ClientData)pkgPtr,
+ (Tcl_CmdDeleteProc *)NULL);
+ Tcl_CreateExitHandler(Terminate, (ClientData)pkgPtr);
+ Tcl_SetAssocData(interp, ASSOC_KEY, NULL, (ClientData)pkgPtr);
+ return Tcl_PkgProvide(interp, "Tclresolver", "1.3.0");
+}
+
+EXTERN int
+Tclresolver_SafeInit(Tcl_Interp *interp)
+{
+ return Tclresolver_Init(interp);
+}
+
+int
+Tclresolver_Unload(Tcl_Interp *interp)
+{
+ LPPKGINFO pkgPtr = NULL;
+ pkgPtr = (LPPKGINFO)Tcl_GetAssocData(interp, ASSOC_KEY, NULL);
+ Tcl_DeleteCommandFromToken(interp, pkgPtr->command);
+ Terminate((ClientData)pkgPtr);
+ Tcl_SetAssocData(interp, ASSOC_KEY, NULL, (ClientData)NULL);
+ return TCL_OK;
+}
+
+static int
+TclresolverObjCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[])
+{
+ LPPKGINFO pkgPtr = (LPPKGINFO)clientData;
+ Tcl_Obj *resObj = NULL;
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, objc, objv, "hostname");
+ return TCL_ERROR;
+ }
+ resObj = LookupHostname(pkgPtr, Tcl_GetString(objv[1]));
+ Tcl_SetObjResult(interp, resObj);
+ return TCL_OK;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int
+Initialize(Tcl_Interp *interp, LPPKGINFO pkgPtr)
+{
+ WSADATA wsd;
+
+ OutputDebugStringA("Initialize\n");
+
+ /* Initialize windows sockets. */
+ if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
+ Tcl_Obj *errObj = Win32Error("WSAStartup error", GetLastError());
+ Tcl_SetObjResult(interp, errObj);
+ return TCL_ERROR;
+ }
+
+ if (LOBYTE(wsd.wVersion) != 2 ||
+ HIBYTE(wsd.wVersion) != 2) {
+ Tcl_SetResult(interp,
+ "error: failed to load a compatible winsock version", TCL_STATIC);
+ WSACleanup();
+ return TCL_ERROR;
+ }
+
+ /* Create the reader thread */
+ pkgPtr->hReader = CreateThread(NULL, 0, Reader, pkgPtr,
+ 0, &pkgPtr->idReader);
+
+ return TCL_OK;
+}
+
+static void
+Terminate(ClientData clientData)
+{
+ LPPKGINFO pkgPtr = (LPPKGINFO)clientData;
+ OutputDebugStringA("Terminate\n");
+ PostThreadMessage(pkgPtr->idReader, WM_QUIT, 0, 0L);
+ Tcl_DeleteExitHandler(Terminate, clientData);
+ WaitForSingleObject(pkgPtr->hReader, 400);
+ CloseHandle(pkgPtr->hReader);
+ Tcl_Free((LPBYTE)pkgPtr);
+ WSACleanup();
+}
+
+static Tcl_Obj *
+LookupHostname(LPPKGINFO pkgPtr, const char * hostname)
+{
+ Tcl_Obj *resObj = NULL;
+ DWORD dwWait;
+ LPQUERYINFO queryPtr;
+ static LONG cookie;
+ char sz[128];
+
+ /* copy the hostname into out buffer */
+ queryPtr = (LPQUERYINFO)Tcl_Alloc(sizeof(QUERYINFO));
+ memset(queryPtr, 0, sizeof(QUERYINFO));
+ queryPtr->lock = CreateEvent(NULL, FALSE, FALSE, NULL);
+ queryPtr->cookie = InterlockedIncrement(&cookie);
+ strcpy(queryPtr->data, hostname);
+
+ sprintf(sz, "query %d begin\n", queryPtr->cookie);
+ OutputDebugString(sz);
+
+ /* notify the reader thread that it should begin reading */
+ PostThreadMessage(pkgPtr->idReader, XWM_RESOLVE, 0, (LPARAM)queryPtr);
+
+ /* Run a tcl event loop while we wait for the answer */
+ while (1) {
+ Tcl_DoOneEvent(0); /* when tk loaded */
+ //Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT);
+ dwWait = WaitForSingleObject(queryPtr->lock, 0);
+ if (dwWait == WAIT_OBJECT_0) {
+ sprintf(sz, "query %d complete\n", queryPtr->cookie);
+ OutputDebugString(sz);
+ break;
+ }
+ }
+
+ resObj = Tcl_NewStringObj(queryPtr->data, -1);
+ CloseHandle(queryPtr->lock);
+ Tcl_Free((char *)queryPtr);
+ return resObj;
+}
+
+static DWORD WINAPI
+Reader(LPVOID clientData)
+{
+ LPPKGINFO pkgPtr = (LPPKGINFO)clientData;
+ LPQUERYINFO queryPtr = NULL;
+ MSG msg;
+ DWORD dwErr = 0, dwRead = 0;
+
+ while(GetMessage(&msg, 0, 0, 0)) {
+ if (msg.hwnd == NULL) {
+ switch (msg.message) {
+ case XWM_RESOLVE:
+ queryPtr = (LPQUERYINFO)msg.lParam;
+ GetAddress(queryPtr->data);
+ QueueEvent(pkgPtr->tclid, queryPtr);
+ break;
+ }
+ }
+ DispatchMessage(&msg);
+ }
+
+ OutputDebugStringA("Reader exiting.\n");
+ return 0;
+}
+
+static void
+QueueEvent(Tcl_ThreadId id, LPQUERYINFO queryPtr)
+{
+ LPRESOLVEREVENT evPtr;
+ OutputDebugStringA("QueueEvent\n");
+ evPtr = (LPRESOLVEREVENT)Tcl_Alloc(sizeof(RESOLVEREVENT));
+ evPtr->header.proc = ResolverEventProc;
+ evPtr->header.nextPtr = NULL;
+ evPtr->query = queryPtr;
+ Tcl_ThreadQueueEvent(id, (Tcl_Event *)evPtr, TCL_QUEUE_TAIL);
+}
+
+static int
+ResolverEventProc(Tcl_Event *evPtr, int flags)
+{
+ LPRESOLVEREVENT revPtr = (LPRESOLVEREVENT)evPtr;
+ OutputDebugStringA("ResolverEventProc\n");
+ SetEvent(revPtr->query->lock);
+ return 1;
+}
+
+static Tcl_Obj *
+Win32Error(const char * szPrefix, HRESULT hr)
+{
+ Tcl_Obj *msgObj = NULL;
+ char * lpBuffer = NULL;
+ DWORD dwLen = 0;
+
+ dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, (DWORD)hr, LANG_NEUTRAL,
+ (LPTSTR)&lpBuffer, 0, NULL);
+ if (dwLen < 1) {
+ dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ "code 0x%1!08X!%n", 0, LANG_NEUTRAL,
+ (LPTSTR)&lpBuffer, 0, (va_list *)&hr);
+ }
+
+ msgObj = Tcl_NewStringObj(szPrefix, -1);
+ if (dwLen > 0) {
+ char *p = lpBuffer + dwLen - 1; /* remove cr-lf at end */
+ for ( ; p && *p && isspace(*p); p--)
+ ;
+ *++p = 0;
+ Tcl_AppendToObj(msgObj, ": ", 2);
+ Tcl_AppendToObj(msgObj, lpBuffer, -1);
+ }
+ LocalFree((HLOCAL)lpBuffer);
+ return msgObj;
+}
+
+static int
+GetAddress(char *query)
+{
+ struct addrinfo hints = {0};
+ struct addrinfo *res = NULL;
+ char *hostname, *p, *q;
+ int r = 0, nc = 0;
+
+ /* trim whitespace */
+ for (p = query; *p && isspace(*p); p++)
+ ;
+ for (q = p + strlen(p) - 1; q > p && *q && isspace(*q); q--)
+ ;
+ *++q = 0;
+ hostname = p;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_INET; /* use PF_UNSPEC if we want IPv6 too */
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_socktype = SOCK_STREAM;
+ r = getaddrinfo(hostname, "", &hints, &res);
+ *query = 0;
+
+ if (r != 0) {
+ Tcl_Obj *o = Win32Error("error", WSAGetLastError());
+ strcpy(query, Tcl_GetString(o));
+ Tcl_DecrRefCount(o);
+ } else {
+ struct addrinfo *resPtr = res;
+ for (resPtr = res; resPtr != NULL; resPtr = resPtr->ai_next) {
+ char name[NI_MAXHOST];
+
+ getnameinfo(resPtr->ai_addr, resPtr->ai_addrlen,
+ name, NI_MAXHOST,
+ NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+
+ if (resPtr != res)
+ strcat(query, " ");
+ strcat(query, name);
+ }
+ freeaddrinfo(res);
+ }
+ return r;
+}
+
+
--- /dev/null
+/* w32cat.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sourceforge.net>
+ *
+ * This illustrates a line-oriented cat program. Each line read from the
+ * console is echoed to the output.
+ *
+ * NB: only reads from the CONSOLE though. Not piped input.
+ *
+ * $Id$
+ */
+
+#if defined(_UNICODE) && !defined(UNICODE)
+#define UNICODE
+#endif
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h>
+
+void Win32Error(HANDLE hFile, LPCTSTR sz, HRESULT hr);
+
+int
+_tmain(int argc, LPCTSTR argv[])
+{
+ HANDLE hStdin, hStdout, hStderr;
+ DWORD dwMode;
+
+ if ((hStderr = GetStdHandle(STD_ERROR_HANDLE)) == INVALID_HANDLE_VALUE) {
+ return -2;
+ }
+
+ if ((hStdout = GetStdHandle(STD_OUTPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
+ Win32Error(hStderr, _T("get stdout handle"), GetLastError());
+ return -1;
+ }
+
+ if ((hStdin = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
+ Win32Error(hStderr, _T("get stdin handle"), GetLastError());
+ return -1;
+ }
+
+ /*
+ * By default the console input is processed line oriented so we
+ * don't need to do anything special.
+ */
+
+ GetConsoleMode(hStdin, &dwMode);
+ dwMode &= ~ENABLE_LINE_INPUT;
+ SetConsoleMode(hStdin, dwMode);
+ GetConsoleMode(hStdout, &dwMode);
+ dwMode &= ~ENABLE_LINE_INPUT;
+ SetConsoleMode(hStdout, dwMode);
+
+ while (1) {
+ DWORD cbRead = 0;
+ TCHAR buffer[1024];
+
+ if (! ReadFile(hStdin, buffer, 1023, &cbRead, NULL))
+ break;
+ if (CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE,
+ _T("quit"), 4, buffer, 4) == CSTR_EQUAL)
+ break;
+
+ WriteFile(hStdout, buffer, cbRead, NULL, NULL);
+ }
+
+ return 0;
+}
+
+void
+Win32Error(HANDLE hFile, LPCTSTR sz, HRESULT hr)
+{
+ LPTSTR lpBuffer = NULL;
+ DWORD dwLen = 0;
+
+ dwLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, (DWORD)hr, LANG_NEUTRAL,
+ (LPTSTR)&lpBuffer, 0, NULL);
+ if (dwLen < 1) {
+ HRESULT ahr[1] = {hr};
+ dwLen = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ _T("code: 0x%1!08X!%n"), 0, LANG_NEUTRAL,
+ (LPTSTR)&lpBuffer, 0, (va_list *)ahr);
+ }
+ WriteConsole(hFile, sz, lstrlen(sz), NULL, NULL);
+ if (dwLen > 0) {
+ WriteConsole(hFile, _T(": "), 3, NULL, NULL);
+ WriteConsole(hFile, lpBuffer, dwLen, NULL, NULL);
+ }
+ LocalFree((HLOCAL)lpBuffer);
+}
--- /dev/null
+/*
+ *
+ */
+#if defined(_UNICODE) && !defined(UNICODE)
+#define UNICODE
+#endif
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <tchar.h>
+#include <stdio.h>
+
+#if _MSC_VER >= 1000
+#pragma comment(lib, "ws2_32")
+#endif
+
+void Win32Error(LPCTSTR sz, HRESULT hr);
+LPSTR W2A(LPCWSTR wstr);
+
+int
+_tmain(int argc, LPCTSTR argv[])
+{
+ WSADATA wsd;
+ struct addrinfo hints = {0};
+ struct addrinfo *res = NULL;
+ const char * hostname;
+ DWORD dwCookie = 0;
+ int r = 0;
+
+ if (argc < 2) {
+ _ftprintf(stderr, _T("usage: resolver <hostname>\n"));
+ return -1;
+ }
+
+ if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
+ Win32Error(_T("WSAStartup failed"), GetLastError());
+ return -1;
+ }
+
+ if (LOBYTE(wsd.wVersion) != 2 ||
+ HIBYTE(wsd.wVersion) != 2) {
+ _ftprintf(stderr,
+ _T("error: failed to load a compatible winsock version\n"));
+ WSACleanup();
+ return -1;
+ }
+
+#ifdef UNICODE
+ hostname = W2A(argv[1]);
+#else
+ hostname = argv[1];
+#endif
+
+ dwCookie = GetCurrentProcessId();
+ ZeroMemory(&hints, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+
+ r = getaddrinfo(hostname, "", &hints, &res);
+
+#ifdef UNICODE
+ HeapFree(GetProcessHeap(), 0, (LPVOID)hostname);
+#endif
+
+ if (r != 0) {
+ Win32Error(_T("getaddrinfo"), WSAGetLastError());
+ } else {
+ struct addrinfo *p = res;
+ printf("%d", dwCookie);
+ while (p != NULL) {
+ char name[NI_MAXHOST];
+ getnameinfo(p->ai_addr, p->ai_addrlen,
+ name, NI_MAXHOST,
+ NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ printf(" %s", name);
+ p = p->ai_next;
+ }
+ printf("\n");
+ freeaddrinfo(res);
+ }
+
+ WSACleanup();
+ return 0;
+}
+
+LPSTR
+W2A(LPCWSTR wstr)
+{
+ LPSTR str = NULL;
+ int slen = 0;
+
+ if (wstr == NULL)
+ return NULL;
+
+ slen = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
+ str = HeapAlloc(GetProcessHeap(), 0, slen * sizeof(CHAR));
+ if (str != NULL)
+ slen = WideCharToMultiByte(CP_ACP, 0, wstr, slen, str, slen, NULL, NULL);
+ return str;
+}
+
+void
+Win32Error(LPCTSTR sz, HRESULT hr)
+{
+ LPTSTR lpBuffer = NULL;
+ TCHAR sBuffer[30];
+
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, (DWORD)hr, LANG_NEUTRAL,
+ (LPTSTR)&lpBuffer, 0, NULL);
+
+ _ftprintf(stderr, _T("%s: "), sz);
+ if (lpBuffer == NULL) {
+ lpBuffer = sBuffer;
+ _ftprintf(stderr, _T("code: %08lX\n"), hr);
+ } else {
+ _ftprintf(stderr, lpBuffer);
+ }
+}
--- /dev/null
+/* tclresolver.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net>
+ *
+ *
+ *
+ * $Id$
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <tchar.h>
+#include <winsock2.h>
+
+#include <tcl.h>
+
+#define BUFSIZE 4096
+#undef TCL_STORAGE_CLASS
+#define TCL_STORAGE_CLASS DLLEXPORT
+
+static const char ASSOC_KEY[] = "tclresolver_key";
+
+EXTERN int Tclresolver_Init(Tcl_Interp *interp);
+EXTERN int Tclresolver_Unload(Tcl_Interp *interp);
+EXTERN int TclGetHostByName(LPSOCKADDR_IN saddrPtr,
+ const char *hostname, int port);
+
+typedef struct {
+ HANDLE hReader;
+ DWORD idReader;
+ HANDLE hReady;
+ HANDLE hDone;
+ HANDLE hIn;
+ HANDLE hOut;
+ LPBYTE pData;
+ BOOL bRunning;
+ Tcl_ThreadId tclid;
+ Tcl_Command command;
+} LOOKUPINFO, *LPLOOKUPINFO;
+
+typedef struct {
+ Tcl_Event header;
+ LPLOOKUPINFO info;
+} RESOLVEREVENT, *LPRESOLVEREVENT;
+
+//static int TclResolverObjCmd(ClientData clientData, Tcl_Interp *interp,
+// int objc, Tcl_Obj *CONST objv[]);
+static Tcl_ObjCmdProc TclresolverObjCmd;
+static Tcl_ExitProc Terminate;
+static int Initialize(Tcl_Interp *interp, LPLOOKUPINFO pLookupInfo);
+static BOOL CreateChildProcess(HANDLE hChildStdoutWr, HANDLE hChildStdinRd);
+static LPCTSTR LookupHostname(LPLOOKUPINFO pLookupInfo, LPCTSTR szHostname);
+static DWORD WINAPI Reader(LPVOID clientData);
+static void QueueEvent(LPLOOKUPINFO pLookupInfo);
+static int ResolverEventProc(Tcl_Event *evPtr, int flags);
+
+
+/* ---------------------------------------------------------------------- */
+
+int
+Tclresolver_Init(Tcl_Interp *interp)
+{
+ int initialized = 0;
+ LPLOOKUPINFO pLookupInfo = NULL;
+
+#ifdef USE_TCL_STUBS
+ if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
+ return TCL_ERROR;
+ }
+#endif
+
+ pLookupInfo = (LPLOOKUPINFO)Tcl_Alloc(sizeof(LOOKUPINFO));
+ if (pLookupInfo == NULL) {
+ Tcl_SetResult(interp, "out of memory", TCL_STATIC);
+ return TCL_ERROR;
+ }
+ ZeroMemory(pLookupInfo, sizeof(LOOKUPINFO));
+
+ if (Initialize(interp, pLookupInfo) != TCL_OK) {
+ Tcl_Free((char*)pLookupInfo);
+ //Tcl_SetResult(interp, "failed to initialize package", TCL_STATIC);
+ return TCL_ERROR;
+ }
+ pLookupInfo->tclid = Tcl_GetCurrentThread();
+ pLookupInfo->command = Tcl_CreateObjCommand(interp, "resolve",
+ TclresolverObjCmd,
+ (ClientData)pLookupInfo,
+ (Tcl_CmdDeleteProc *)NULL);
+ Tcl_CreateExitHandler(Terminate, (ClientData)pLookupInfo);
+ Tcl_SetAssocData(interp, ASSOC_KEY, NULL, (ClientData)pLookupInfo);
+ return Tcl_PkgProvide(interp, "Tclresolver", "1.0.0");
+}
+
+EXTERN int
+Tclresolver_SafeInit(Tcl_Interp *interp)
+{
+ return Tclresolver_Init(interp);
+}
+
+int
+Tclresolver_Unload(Tcl_Interp *interp)
+{
+ LPLOOKUPINFO pLookupInfo = NULL;
+ pLookupInfo = (LPLOOKUPINFO)Tcl_GetAssocData(interp, ASSOC_KEY, NULL);
+ Tcl_DeleteCommandFromToken(interp, pLookupInfo->command);
+ Terminate((ClientData)pLookupInfo);
+ Tcl_SetAssocData(interp, ASSOC_KEY, NULL, (ClientData)NULL);
+ return TCL_OK;
+}
+
+static int
+TclresolverObjCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[])
+{
+ LPLOOKUPINFO pLookupInfo = (LPLOOKUPINFO)clientData;
+ Tcl_Obj *resObj = NULL;
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, objc, objv, "hostname");
+ return TCL_ERROR;
+ }
+ resObj = Tcl_NewStringObj(LookupHostname(pLookupInfo,
+ Tcl_GetString(objv[1])), -1);
+ Tcl_SetObjResult(interp, resObj);
+ return TCL_OK;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int
+Initialize(Tcl_Interp *interp, LPLOOKUPINFO pLookupInfo)
+{
+ SECURITY_ATTRIBUTES saAttr;
+ BOOL fSuccess;
+ HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr;
+ HANDLE hChildStdoutRdDup, hChildStdinWrDup;
+
+ // Set the bInheritHandle flag so pipe handles are inherited.
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ // Create a pipe for the child process's STDOUT.
+ if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
+ Tcl_SetResult(interp, "Stdout pipe creation failed", TCL_STATIC);
+ return TCL_ERROR;
+ }
+
+ // Create noninheritable read handle and close the inheritable read
+ // handle.
+ fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
+ GetCurrentProcess(), &hChildStdoutRdDup , 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS);
+ if( !fSuccess ) {
+ Tcl_SetResult(interp, "DuplicateHandle failed", TCL_STATIC);
+ return TCL_ERROR;
+ }
+ CloseHandle(hChildStdoutRd);
+
+ // Create a pipe for the child process's STDIN.
+ if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
+ Tcl_SetResult(interp, "Stdin pipe creation failed", TCL_STATIC);
+ return TCL_ERROR;
+ }
+
+ // Duplicate the write handle to the pipe so it is not inherited.
+ fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
+ GetCurrentProcess(), &hChildStdinWrDup, 0,
+ FALSE, // not inherited
+ DUPLICATE_SAME_ACCESS);
+ if (! fSuccess) {
+ Tcl_SetResult(interp, "DuplicateHandle failed", TCL_STATIC);
+ return TCL_ERROR;
+ }
+ CloseHandle(hChildStdinWr);
+
+ /* Create synchronization events */
+ pLookupInfo->hReady = CreateEvent(NULL, FALSE, FALSE, NULL);
+ pLookupInfo->hDone = CreateEvent(NULL, FALSE, FALSE, NULL);
+ pLookupInfo->hIn = hChildStdoutRdDup;
+ pLookupInfo->hOut = hChildStdinWrDup;
+ pLookupInfo->bRunning = TRUE;
+ pLookupInfo->pData = (LPBYTE)Tcl_Alloc(BUFSIZE);
+
+ /* Create the reader thread */
+ pLookupInfo->hReader = CreateThread(NULL, 0, Reader, pLookupInfo,
+ 0, &pLookupInfo->idReader);
+
+ /* Now create the child process. */
+ fSuccess = CreateChildProcess(hChildStdoutWr, hChildStdinRd);
+ if (! fSuccess) {
+ Tcl_SetResult(interp, "Create process failed", TCL_STATIC);
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+static void
+Terminate(ClientData clientData)
+{
+ LPLOOKUPINFO pLookupInfo = (LPLOOKUPINFO)clientData;
+ OutputDebugString("Pkgdeleteproc\n");
+ pLookupInfo->bRunning = FALSE;
+ WaitForSingleObject(pLookupInfo->hReader, 400);
+ CloseHandle(pLookupInfo->hReader);
+ CloseHandle(pLookupInfo->hReady);
+ CloseHandle(pLookupInfo->hDone);
+ CloseHandle(pLookupInfo->hIn);
+ CloseHandle(pLookupInfo->hOut);
+ Tcl_Free(pLookupInfo->pData);
+ Tcl_Free((LPBYTE)pLookupInfo);
+}
+
+static LPCTSTR
+LookupHostname(LPLOOKUPINFO pLookupInfo, LPCTSTR szHostname)
+{
+ DWORD dwWrote, dwWait;
+ BOOL br;
+
+ ZeroMemory(pLookupInfo->pData, BUFSIZE);
+ lstrcpy((LPTSTR)pLookupInfo->pData, szHostname);
+ lstrcat((LPTSTR)pLookupInfo->pData, _T("\n"));
+ br = WriteFile(pLookupInfo->hOut, pLookupInfo->pData,
+ lstrlen((LPTSTR)pLookupInfo->pData) * sizeof(TCHAR),
+ &dwWrote, NULL);
+ if (br)
+ br = FlushFileBuffers(pLookupInfo->hOut);
+ if (br) {
+ /* clear out the buffer ready for reading */
+ ZeroMemory(pLookupInfo->pData, BUFSIZE);
+ /* notify the reader thread that it should begin reading */
+ SetEvent(pLookupInfo->hReady);
+#ifdef USE_WIN32_EVENTS
+ /* Doing this will block Tcl event processing */
+ dwWait = WaitForSingleObject(pLookupInfo->hDone, INFINITE);
+#else
+ /* Run a tcl event loop while we wait for the answer */
+ while (1) {
+ Tcl_DoOneEvent(0); /* when tk loaded */
+ //Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT);
+ OutputDebugString("done one event\n");
+ dwWait = WaitForSingleObject(pLookupInfo->hDone, 0);
+ if (dwWait == WAIT_OBJECT_0)
+ break;
+ }
+#endif
+ }
+ if (!br) {
+ return _T("");
+ }
+ return (LPCTSTR)pLookupInfo->pData;
+}
+
+static BOOL
+CreateChildProcess(HANDLE hChildStdoutWr, HANDLE hChildStdinRd)
+{
+ PROCESS_INFORMATION piProcInfo;
+ STARTUPINFO siStartInfo;
+ BOOL bFuncRetn = FALSE;
+
+ /* Set up members of the PROCESS_INFORMATION structure.*/
+ ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
+
+ /* Set up members of the STARTUPINFO structure.*/
+ ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.hStdError = hChildStdoutWr;
+ siStartInfo.hStdOutput = hChildStdoutWr;
+ siStartInfo.hStdInput = hChildStdinRd;
+ siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ /* Create the child process.*/
+ bFuncRetn = CreateProcess(NULL,
+ "resolver", // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ DETACHED_PROCESS, // no console
+ NULL, // use parent's environment
+ NULL, // use parent's current directory
+ &siStartInfo, // STARTUPINFO pointer
+ &piProcInfo); // receives PROCESS_INFORMATION
+
+ if (bFuncRetn == 0)
+ return bFuncRetn; /*ErrorExit("CreateProcess failed");*/
+ else
+ {
+ CloseHandle(piProcInfo.hProcess);
+ CloseHandle(piProcInfo.hThread);
+ return bFuncRetn;
+ }
+}
+
+static DWORD WINAPI
+Reader(LPVOID clientData)
+{
+ LPLOOKUPINFO pLookupInfo = (LPLOOKUPINFO)clientData;
+ DWORD dwErr = 0, dwRead = 0;
+ BOOL br;
+
+ while (pLookupInfo->bRunning) {
+ dwErr = WaitForSingleObject(pLookupInfo->hReady, 500);
+ if (dwErr == WAIT_OBJECT_0) {
+ br = ReadFile(pLookupInfo->hIn, pLookupInfo->pData,
+ BUFSIZE, &dwRead, NULL);
+
+ if (dwRead > 0) {
+ LPTSTR p = (LPTSTR)pLookupInfo->pData;
+ for (p = p + lstrlen(p) - 1;
+ p > pLookupInfo->pData && _istspace(*p);
+ p--)
+ ;
+ *++p = 0;
+ }
+#ifdef USE_WIN32_EVENTS
+ SetEvent(pLookupInfo->hDone);
+#else
+ QueueEvent(pLookupInfo);
+#endif
+ }
+ }
+ return 0;
+}
+
+static void
+QueueEvent(LPLOOKUPINFO pLookupInfo)
+{
+ LPRESOLVEREVENT evPtr;
+ OutputDebugString("queueevent\n");
+ evPtr = (LPRESOLVEREVENT)Tcl_Alloc(sizeof(RESOLVEREVENT));
+ evPtr->header.proc = ResolverEventProc;
+ evPtr->header.nextPtr = NULL;
+ evPtr->info = pLookupInfo;
+ Tcl_ThreadQueueEvent(pLookupInfo->tclid,
+ (Tcl_Event *)evPtr, TCL_QUEUE_TAIL);
+}
+
+static int
+ResolverEventProc(Tcl_Event *evPtr, int flags)
+{
+ LPRESOLVEREVENT revPtr = (LPRESOLVEREVENT)evPtr;
+ OutputDebugString("resolvereventproc\n");
+ SetEvent(revPtr->info->hDone);
+ return 1;
+}
+
+#if 0
+int
+TclGetHostByName(LPSOCKADDR_IN saddrPtr, const char *hostname, int port)
+{
+ struct in_addr addr;
+
+ ZeroMemory(saddrPtr, sizeof(SOCKADDR_IN));
+ saddrPtr->sin_family = AF_INET;
+ saddrPtr->sin_port = htons((unsigned short)port);
+ /* if no hostname then the answer is any host */
+ if (hostname == NULL || *hostname == 0) {
+ addr.s_addr = INADDR_ANY;
+ } else {
+ /* see if we have been given an ip number already */
+ addr.s_addr = inet_addr(hostname);
+ if (addr.s_addr == INADDR_NONE) {
+ /* Perform the address lookup via our subprocess */
+ addr.s_addr = inet_addr(LookupHostname(hostname));
+ if (addr.s_addr == INADDR_NONE) {
+#ifdef EHOSTUNREACH
+ Tcl_SetErrno(EHOSTUNREACH);
+#endif
+ return 0; /* error */
+ }
+ }
+ }
+ saddrPtr->sin_addr.s_addr = addr.s_addr;
+ return 1;
+}
+#endif