From 3b7cf21e6286ead45c7422b1b9a86e528ce39caf Mon Sep 17 00:00:00 2001 From: Pat Thoyts Date: Thu, 26 Aug 2004 09:47:48 +0000 Subject: [PATCH 1/1] Put tclresolver working files into version control. --- Makefile | 49 ++++++ demo/demo.tcl | 80 +++++++++ library/resolver.tcl | 84 ++++++++++ resolvchk.c | 233 +++++++++++++++++++++++++++ tclresolver2.c | 319 ++++++++++++++++++++++++++++++++++++ tclresolver3.c | 336 ++++++++++++++++++++++++++++++++++++++ w32cat.c | 93 +++++++++++ win/resolver.c | 121 ++++++++++++++ win/tclresolver.c | 375 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 1690 insertions(+) create mode 100644 Makefile create mode 100644 demo/demo.tcl create mode 100644 library/resolver.tcl create mode 100644 resolvchk.c create mode 100644 tclresolver2.c create mode 100644 tclresolver3.c create mode 100644 w32cat.c create mode 100644 win/resolver.c create mode 100644 win/tclresolver.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6df5a7d --- /dev/null +++ b/Makefile @@ -0,0 +1,49 @@ + +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 diff --git a/demo/demo.tcl b/demo/demo.tcl new file mode 100644 index 0000000..57b6093 --- /dev/null +++ b/demo/demo.tcl @@ -0,0 +1,80 @@ +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 . {focus .e} + bind . {focus .h} + bind . {focus .x} + bind . {.h invoke} + bind . {.x invoke} + if {[string equal [tk windowingsystem] "win32"]} { + bind . {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 +} diff --git a/library/resolver.tcl b/library/resolver.tcl new file mode 100644 index 0000000..9628903 --- /dev/null +++ b/library/resolver.tcl @@ -0,0 +1,84 @@ +# resolver.tcl - Copyright (C) 2004 Pat Thoyts +# +# +# +# ------------------------------------------------------------------------- +# 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 +} diff --git a/resolvchk.c b/resolvchk.c new file mode 100644 index 0000000..ce8db7e --- /dev/null +++ b/resolvchk.c @@ -0,0 +1,233 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include + +#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); +} diff --git a/tclresolver2.c b/tclresolver2.c new file mode 100644 index 0000000..6684408 --- /dev/null +++ b/tclresolver2.c @@ -0,0 +1,319 @@ +/* tclresolver.c - Copyright (C) 2004 Pat Thoyts + * + * + * + * $Id$ + */ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include + +#include + +#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; +} + + diff --git a/tclresolver3.c b/tclresolver3.c new file mode 100644 index 0000000..d465b5b --- /dev/null +++ b/tclresolver3.c @@ -0,0 +1,336 @@ +/* tclresolver.c - Copyright (C) 2004 Pat Thoyts + * + * Non-blocking name resolution. + * + * $Id$ + */ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#include + +#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; +} + + diff --git a/w32cat.c b/w32cat.c new file mode 100644 index 0000000..560d15a --- /dev/null +++ b/w32cat.c @@ -0,0 +1,93 @@ +/* w32cat.c - Copyright (C) 2004 Pat Thoyts + * + * 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 +#include + +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); +} diff --git a/win/resolver.c b/win/resolver.c new file mode 100644 index 0000000..e4e35b2 --- /dev/null +++ b/win/resolver.c @@ -0,0 +1,121 @@ +/* + * + */ +#if defined(_UNICODE) && !defined(UNICODE) +#define UNICODE +#endif + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include + +#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 \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); + } +} diff --git a/win/tclresolver.c b/win/tclresolver.c new file mode 100644 index 0000000..bcd143f --- /dev/null +++ b/win/tclresolver.c @@ -0,0 +1,375 @@ +/* tclresolver.c - Copyright (C) 2004 Pat Thoyts + * + * + * + * $Id$ + */ + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#include + +#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 -- 2.23.0