Download Latest Version sqdtrust.7z (209.7 kB)
Email in envelope

Get an email when there's a new version of SquidTrust

Home / Initial
Name Modified Size InfoDownloads / Week
Parent folder
squidtrustREADME.txt 2011-04-20 15.3 kB
sqdtrust.7z 2011-04-20 209.7 kB
Totals: 2 Items   225.0 kB 0

Packing List
squidtrustREADME.txt -- this file
SourceSquidTrust.AHK -- source code in AutoHotkey  http://www.autohotkey.com/
SquidTrust.exe -- compiled exe
SquidTrust.pl -- Perl authentication helper for Squid












I started with this http://www.autohotkey.com/forum/topic42967.html 
And made a few small changes to make the script spit out the logged in Novell user any time a connection is made and a “1” sent to the server. Sending a “2” will give the computer name (%A_COMPUTERNAME%) and lastly a “3” will send the windows user name (%A_USERNAME%).

Here's the script:


***************************************************************************
#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
#SingleInstance force


Menu, Tray, NoStandard
Menu, tray, add, ComputerName
Menu, tray, add, IPAddress
Menu, tray, add, Exit

SetBatchLines -1
; --------------------------
; ---------run on the workstation----------
; ---------most code stolen directly from http://www.autohotkey.com/forum/topic42967.html-------------
; -------------------
; START CONFIGURATION
; -------------------
; Specify Your own Network's address and port.
Network_Address = 0.0.0.0
Network_Port = 6399
; --------------------
; END OF CONFIGURATION
; --------------------
Gosub Connection_Init
return


Connection_Init:
OnExit, ExitSub  ; For connection cleanup purposes.
socket := PrepareForIncomingConnection(Network_Address, Network_Port)
if socket = -1  ; Connection failed (it already displayed the reason).
    ExitApp
; Find this script's main window:
Process, Exist  ; This sets ErrorLevel to this script's PID (it's done this way to support compiled scripts).
DetectHiddenWindows On
ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " . ErrorLevel)
DetectHiddenWindows Off
; When the OS notifies the script that there is incoming data waiting to be received,
; the following causes a function to be launched to read the data:
NotificationMsg = 0x5555  ; An arbitrary message number, but should be greater than 0x1000.
OnMessage(NotificationMsg, "ReceiveData")
; Set up the connection to notify this script via message whenever new data has arrived.
; This avoids the need to poll the connection and thus cuts down on resource usage.
FD_READ = 1      ; Received when data is available to be read.
FD_CLOSE = 32    ; Received when connection has been closed.
FD_CONNECT = 20  ; Received when connection has been made.
FD_ACCEPT = 8    ; Received when connection has been accepted.
if DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", ScriptMainWindowId, "UInt", NotificationMsg, "Int", FD_ACCEPT|FD_READ|FD_CLOSE|FD_CONNECT)
{
    MsgBox % "WSAAsyncSelect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
    ExitApp
}
return


PrepareForIncomingConnection(IPAddress, Port)
; This can connect to most types of TCP servers, not just Network.
; Returns -1 (INVALID_SOCKET) upon failure or the socket ID upon success.
{
    VarSetCapacity(wsaData, 32)  ; The struct is only about 14 in size, so 32 is conservative.
    result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0002, "UInt", &wsaData) ; Request Winsock 2.0 (0x0002)
    ; Since WSAStartup() will likely be the first Winsock function called by this script,
    ; check ErrorLevel to see if the OS has Winsock 2.0 available:
    if ErrorLevel
    {
        MsgBox WSAStartup() could not be called due to error %ErrorLevel%. Winsock 2.0 or higher is required.
        return -1
    }
    if result  ; Non-zero, which means it failed (most Winsock functions return 0 upon success).
    {
        MsgBox % "WSAStartup() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
        return -1
    }
    AF_INET = 2
    SOCK_STREAM = 1
    IPPROTO_TCP = 6
    socket := DllCall("Ws2_32\socket", "Int", AF_INET, "Int", SOCK_STREAM, "Int", IPPROTO_TCP)
    if socket = -1
    {
        MsgBox % "socket() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
        return -1
    }
    ; Prepare for connection:
    SizeOfSocketAddress = 16
    VarSetCapacity(SocketAddress, SizeOfSocketAddress)
    InsertInteger(2, SocketAddress, 0, AF_INET)   ; sin_family
    InsertInteger(DllCall("Ws2_32\htons", "UShort", Port), SocketAddress, 2, 2)   ; sin_port
    InsertInteger(DllCall("Ws2_32\inet_addr", "Str", IPAddress), SocketAddress, 4, 4)   ; sin_addr.s_addr
    ; Bind to socket:
    if DllCall("Ws2_32\bind", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress)
    {
        MsgBox % "bind() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . "?"
        return -1
    }
    if DllCall("Ws2_32\listen", "UInt", socket, "UInt", "SOMAXCONN")
    {
        MsgBox % "listen() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . "?"
        return -1
    }
    return socket  ; Indicate success by returning a valid socket ID rather than -1.
}


ReceiveData(wParam, lParam)
; By means of OnMessage(), this function has been set up to be called automatically whenever new data
; arrives on the connection.
{
    critical   ; So OnMessage doesn't interrupt (may cause conn_refused if there's lag in the rest of this function)
    global ShowReceived
    socket := wParam
    ; accept requests that are in the pipeline of the socket
    ConnCheck := DllCall("Ws2_32\accept", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress)
    ; Ws2_22/accept returns the new Connection-Socket if a connection request was in the pipeline
    ; on failure it returns an negative value
    if ConnCheck > 1   ; new connection
    {
        ;TrayTip, , Connection Established, 5   ; Show connection accepted
    }
    else
    {
        ReceivedDataSize = 8192  ; Large in case a lot of data gets buffered due to delay in processing previous data.
        Loop   ; This loop solves the issue of the notification message being discarded due to thread-already-running.
        {
            VarSetCapacity(ReceivedData, ReceivedDataSize, 0)   ; 0 for last param terminates string for use with recv().
            ReceivedDataLength := DllCall("Ws2_32\recv", "UInt", socket, "Str", ReceivedData, "Int", ReceivedDataSize, "Int", 0)
            if ReceivedDataLength = 0   ; The connection was gracefully closed,
            {
                ShowReceived =
                TrayTip, , Connection Closed, 5   ; Show connection closed by client
                CloseSocket(socket)
                return 1
            }
            if ReceivedDataLength = -1
            {
                ;WinsockError := DllCall("Ws2_32\WSAGetLastError")
                ;if WinsockError = 10035  ; WSAEWOULDBLOCK, which means "no more data to be read".
                    ;return 1
                ;if WinsockError = 10054 ; WSAECONNRESET, which happens when Network closes via system shutdown/logoff.
                    ;CloseSocket(socket)
                ;else
                    ;MsgBox % "recv() indicated Winsock error " . WinsockError
                CloseSocket(socket)
                return 1
            }
            ; Otherwise, process the data received.
            Loop, parse, ReceivedData, `a
            {
               ShowReceived = %ShowReceived%%A_LoopField%
               ;Tooltip % ShowReceived
            }
            if ReceivedDataLength > 0
                break
        }
        ;TrayTip, Data Received, %ShowReceived%, 5, 1   ; Show info about Received Data
	
	;;;;;;;;;;;;;;; 1 sends netware username ;;;;;;;;;;;;;;;;;;;;;;;;;;;
	if ShowReceived = 1
	{
		; Open NetWare utility DLL
		hModule := DllCall("LoadLibrary", "str", "calwin32.Dll") 
		if !hModule 
		{ 
   		MsgBox Error: Can't load calwin32 DLL. are you SURE the Novell client is on this machine? 
   		Exit 
		}

		; initializes NWCallsInit
		nwrc :=DllCall("calwin32\NWCallsInit", UInt, 0, UInt, 0 ) 
		If nwrc <> 0
		{
		MsgBox Error: NWCallsInit was not successful. That's all I know.
		Exit
		}

		; Get primary connection ID
		DllCall("calwin32\NWGetPrimaryConnectionID", "*UShort", ConnID, "Int") 

		; Get Connection Number
		DllCall("calwin32.dll\NWGetConnectionNumber", "UShort", ConnID, "*UShort", ConnNum, "Int") 

		; Get Connection Information
		VarSetCapacity(UserName, 48)
		DllCall("calwin32.dll\NWGetConnectionInformation", "UShort", ConnID, "UShort", ConnNum, "Str", UserName) 


        SendText = %UserName%`n
        SentOK := SendData(socket, SendText)
        CloseSocket(socket)
        ;Clear ShowReceived
        ShowReceived =
	}
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 send computer name ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	else if ShowReceived = 2
	{	
        SendText = %A_COMPUTERNAME%`n
        SentOK := SendData(socket, SendText)
        CloseSocket(socket)
        ;Clear ShowReceived
        ShowReceived =
	}
	;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 3 sends windows username ;;;;;;;;;;;;;;;;;;;;;;;;;
	else if ShowReceived = 3
	{	
        SendText = %A_USERNAME%`n
        SentOK := SendData(socket, SendText)
        CloseSocket(socket)
        ;Clear ShowReceived
        ShowReceived =
	}

    
    else if ShowReceived > 3
    {
    SentOK := SendData(socket, SendText)
    CloseSocket(socket)
    ;Clear ShowReceived
    ShowReceived =
    }
    return 1   ; Tell the program that no further processing of this message is needed.
    }
}


CloseSocket(wParam)
{
    socket := wParam
    ;ShutStatus := DllCall("Ws2_32\shutdown", "UInt", socket, "Int", 0) ; Commented to prevent CONN_RESET errors
    ; The shutdown() last parameter is either
    ; SD_SEND: 0: Shutdown send operations.
    ; SD_RECEIVE: 1: Shutdown receive operations.
    ; SD_BOTH: 2: Shutdown both send and receive operations.
    CloseStatus := DllCall("Ws2_32\closesocket", "UInt", socket)
}


SendData(wParam, SendData)
{
    socket := wParam
    sendret := DllCall("Ws2_32\send", "UInt", socket, "Str", SendData, "Int", StrLen(SendData), "Int", 0)
    return sendret
}


InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity.  To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
    Loop %pSize%  ; Copy each byte in the integer into the structure as raw binary data.
        DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}


ExitSub:  ; This subroutine is called automatically when the script exits for any reason.
; MSDN: "Any sockets open when WSACleanup is called are reset and automatically
; deallocated as if closesocket was called."
DllCall("Ws2_32\WSACleanup")
ExitApp

Exit:
ExitApp


ComputerName:
EnvGet, COMPUTERNAME, COMPUTERNAME
msgbox, "%COMPUTERNAME%"
return

IPAddress:
msgbox, "%A_IPAddress1% --- %A_IPAddress2% --- %A_IPAddress3% --- %A_IPAddress4%"
return
************************************************************************







To test:
Run the script.
From a cmd prompt, telnet to your IP on port 6399, press a "2" to get the %A_COMPUTERNAME%. The script should immidately disconnect 
and drop you back to a prompt. Telnet again to port 6399 and press a "3" to get the %A_USERNAME%.
To get a Novell username you will need the Novell client and to be logged into a server.


Part two of the authentication to a squid proxy is a little Perl.
This script requires IO::Socket, which is pretty common in most Perl installs, but is also available via CPAN.
I think I've documented this fairly well...if not....google is your friend.
This script is refered to as an "Authentication helper" and needs to be saved somewhere on the Squid Proxy, but, you 
should be able to test it on any box that has Perl installed.



***********************SquidTrust.pl********************************
##SquidTrust.pl

#!/usr/bin/perl

use IO::Socket;

$|=1;

## Main loop (START), sets the loop to wait for an input <STDIN> and sets the input to $host (clients IP address)  
START: while ($host = <STDIN>) {

## Sets up the socket connection to the client computer
$port = 6399;
$sock = new IO::Socket::INET(PeerAddr => $host,
                             PeerPort => $port,
                             Proto    => 'tcp',
                             Timeout  => '1',);

## if - else section to either print OK or ERR based on weather a successful connection was made 

if ($sock =~ /IO::Socket/) {
	#send a command to the workstation
	$cmd = "1";
	print $sock $cmd;
	# print workstation response
	$nwusername = <$sock>;
		if ( length $nwusername > 0 )
		{
    		print "OK user=$nwusername\n";
		close $sock;
		next START;
		}
		
		else
		{
		print "ERR\n";
		close $sock;
		next START;
		}

	
} else {
	print "ERR\n";
	close $sock;
	}

next START;
}



******************************************************

run it as "perl SquidTrust.pl" and it should just sit there and wait for an IP address to be entered.
once an IP address is entered , the scipt will try to connect to that IP on port 6399 (the AHK script) and send a "1".
If it gets any return, it prints "OK user=$nwusername" if it doesn't get a return, it prints "ERR".
These are the standard returns expected by Squid. The script then just restarts and waits for the next IP address.
To get the windows username, change the line 
$cmd = "1"; 
to 
$cmd = "3";





in my squid.conf the helper is added like this:

*****************snip of squid.conf********************************

external_acl_type IPUser ttl=60 children=10 %SRC /usr/local/squid/SquidTrust.pl
acl AuthNDS external IPUser
http_access allow AuthNDS

# These hosts do not have any restrictions
http_access allow unrestricted_hosts
# Always allow access to whitelist domains
http_access allow whitelist
auth_param basic program /usr/local/libexec/squid/squid_ldap_auth -v 2 -b T=MYTREE -f "(&(|(groupMembership=cn=LIMITEDaccess,o=INTERNET)(groupMembership=cn=FULLaccess,o=INTERNET))(objectclass=User)(cn=%s))" -u uid -P 10.1.1.7
auth_param basic children 70

**********************************************************



Notice how the "external_acl_type" (my perl helper script) is loaded before the default "squid_ldap_auth".
If a user isn't authenticated with the "helper" it will drop back to the standard LDAP auth and they will be prompted for a login username and password.



For those that like numbers and stats.....here they are.....

CacheMgr.

External ACL Statistics: IPUser
Cache size: 1133
program: /usr/local/squid/SquidTrust.pl
number running: 10 of 10
requests sent: 155129
replies received: 155129
queue length: 0
avg service time: 15.18 msec


 
I've been running a slightly modified version of this script on 300-400 machines for almost 6 months and haven't had a single
issue. Machines are mostly XP, but the script seems to run fine on Windows 7.

This is just kind of the begining..there is a lot of fun stuff to do with this script....if there is any interest I'll post more later.



Source: squidtrustREADME.txt, updated 2011-04-20