This patch is generated from the cygwin branch of HEAD in squid Wed Apr 6 02:18:08 2005 GMT See http://devel.squid-cache.org/ Index: squid/acinclude.m4 diff -u squid/acinclude.m4:1.6 squid/acinclude.m4:1.1.2.6 --- squid/acinclude.m4:1.6 Sun Oct 13 04:43:36 2002 +++ squid/acinclude.m4 Sun Oct 20 06:45:45 2002 @@ -1,3 +1,123 @@ +dnl COMPILER WIN32 support ==================================== +# figure out how to run CC with access to the win32 api if present +# configure that as the CC program, +# WIN32 may be present with WINE, under cygwin, or under mingw, +# or cross compilers targeting those same three targets. +# as it happens, I can only test cygwin, so extra input here will be appreciated +# send bug reports to Robert Collins +# +# logic: is CC already configured? if not, call AC_PROG_CC. +# if so - try it. If that doesn't work ,try -mwin32. If that doesn't work, fail +# +# 2001-03-15 - Changed from yes/no to true/false -suggested by Lars J Aas +# * Change true to : - suggest by Alexandre Oliva +# * changed layout on the basis of autoconf mailing list: +# there are now two interfaces, a language specific one which sets +# or clears WIN32 && WIN32FLAGS as appropriate +# * m4 Syntax fixup: Akim Demaille +# +# All faults& bugs are mine - Robert + +AC_DEFUN([AC_PROG_CC_WIN32], [ +dnl AC_REQUIRE([AC_PROG_CC])dnl +AC_MSG_CHECKING([how to access the Win32 API]) +WIN32FLAGS= +AC_TRY_COMPILE(,[ +#ifndef WIN32 +# ifndef _WIN32 +# error WIN32 or _WIN32 not defined +# endif +#endif], [ +dnl found windows.h with the current config. +AC_MSG_RESULT([present by default]) +], [ +dnl try -mwin32 +ac_compile_save="$ac_compile" +dnl we change CC so config.log looks correct +save_CC="$CC" +ac_compile="$ac_compile -mwin32" +CC="$CC -mwin32" +AC_TRY_COMPILE(,[ +#ifndef WIN32 +# ifndef _WIN32 +# error WIN32 or _WIN32 not defined +# endif +#endif], [ +dnl found windows.h using -mwin32 +AC_MSG_RESULT([found via -mwin32]) +ac_compile="$ac_compile_save" +CC="$save_CC" +WIN32FLAGS="-mwin32" +], [ +ac_compile="$ac_compile_save" +CC="$save_CC" +AC_MSG_RESULT([not found]) +]) +]) + +]) + + +# figure out how to run CXX with access to the win32 api if present +# configure that as the CXX program, +# WIN32 may be present with WINE, under cygwin, or under mingw, +# or cross compilers targeting those same three targets. +# as it happens, I can only test cygwin, so extra input here will be appreciated +# send bug reports to Robert Collins + +AC_DEFUN([AC_PROG_CXX_WIN32], [ +dnl AC_REQUIRE([AC_PROG_CXX])dnl +AC_MSG_CHECKING([how to access the Win32 API]) +WIN32FLAGS= +AC_TRY_COMPILE(,[ +#ifndef WIN32 +# ifndef _WIN32 +# error WIN32 or _WIN32 not defined +# endif +#endif], [ +dnl found windows.h with the current config. +AC_MSG_RESULT([present by default]) +], [ +dnl try -mwin32 +ac_compile_save="$ac_compile" +dnl we change CC so config.log looks correct +save_CXX="$CXX" +ac_compile="$ac_compile -mwin32" +CXX="$CXX -mwin32" +AC_TRY_COMPILE(,[ +#ifndef WIN32 +# ifndef _WIN32 +# error WIN32 or _WIN32 not defined +# endif +#endif], [ +dnl found windows.h using -mwin32 +AC_MSG_RESULT([found via -mwin32]) +ac_compile="$ac_compile_save" +CXX="$save_CXX" +WIN32FLAGS="-mwin32" +], [ +ac_compile="$ac_compile_save" +CXX="$save_CXX" +AC_MSG_RESULT([not found]) +]) +]) + +]) + + +# high level interface for finding out compiler support for win32. +AC_DEFUN([AC_API_WIN32], [ + +AC_LANG_CASE( + [C], AC_PROG_CC_WIN32 [CFLAGS="$WIN32FLAGS $CFLAGS" + CPPFLAGS="$WIN32FLAGS $CPPFLAGS"], + [C++], AC_PROG_CXX_WIN32 [CXXFLAGS="$WIN32FLAGS $CXXFLAGS" + CPPFLAGS="$WIN32FLAGS $CPPFLAGS"], + [AC_FATAL([No macro support for WIN32 with ] _AC_LANG) ]) +]) + +dnl end compiler WIN32 support =========================================== + dnl This encapsulates the nasty mess of headers we need to check when dnl checking types. AC_DEFUN(SQUID_DEFAULT_INCLUDES,[[ Index: squid/configure.in diff -u squid/configure.in:1.80 squid/configure.in:1.8.2.51 --- squid/configure.in:1.80 Sat Feb 8 19:15:56 2003 +++ squid/configure.in Wed Feb 12 01:56:27 2003 @@ -91,6 +91,9 @@ AC_DEFINE_UNQUOTED(CONFIG_HOST_TYPE, "$host",[Host type from configure]) +dnl Can the CC program be asked to use the Win32 API? +AC_API_WIN32 + AC_DEFINE_UNQUOTED(SQUID_CONFIGURE_OPTIONS, "$ac_configure_args", [configure command line used to configure Squid]) dnl Check for GNU cc @@ -2386,6 +2389,7 @@ icons/Makefile \ errors/Makefile \ src/fs/aufs/Makefile \ + src/fs/awin32/Makefile \ src/fs/coss/Makefile \ src/fs/diskd/Makefile \ src/fs/null/Makefile \ @@ -2400,6 +2404,7 @@ helpers/basic_auth/NCSA/Makefile \ helpers/basic_auth/PAM/Makefile \ helpers/basic_auth/SMB/Makefile \ + helpers/basic_auth/win32_locallogon/Makefile \ helpers/basic_auth/YP/Makefile \ helpers/basic_auth/getpwnam/Makefile \ helpers/basic_auth/multi-domain-NTLM/Makefile \ @@ -2412,6 +2417,7 @@ helpers/ntlm_auth/no_check/Makefile \ helpers/ntlm_auth/SMB/Makefile \ helpers/ntlm_auth/SMB/smbval/Makefile \ + helpers/ntlm_auth/NTLMSSP-WIN32/Makefile \ helpers/ntlm_auth/winbind/Makefile \ helpers/external_acl/Makefile \ helpers/external_acl/ip_user/Makefile \ @@ -2419,5 +2425,6 @@ helpers/external_acl/unix_group/Makefile \ helpers/external_acl/wbinfo_group/Makefile \ helpers/external_acl/winbind_group/Makefile \ + helpers/external_acl/win32_group/Makefile \ ]) AC_OUTPUT Index: squid/doc/win32-relnotes.txt diff -u /dev/null squid/doc/win32-relnotes.txt:1.1.2.5 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/doc/win32-relnotes.txt Sun Aug 11 09:45:09 2002 @@ -0,0 +1,38 @@ +WIN32 Squid Relese Notes + + + +PSAPI.DLL (Process Status Helper) Considerations + +The process status helper functions make it easier for you to obtain information about +processes and device drivers running on Microsoft® Windows NT®/Windows® 2000. These +functions are available in PSAPI.DLL, which is distributed in the Microsoft® Platform +Software Development Kit (SDK). The same information is generally available through the +performance data in the registry, but it is more difficult to get to it. PSAPI.DLL is +freely redistributable. + +PSAPI.DLL is available only on Windows NT, 2000 and XP. The implementation in Squid is +aware of this, and try to use it only on the rigth platform. + +On Windows NT PSAPI.DLL can be found as component of many applications, if you need it, +you can find it on Windows NT Resource KIT. If you have problem, you can You can +download it here: +http://download.microsoft.com/download/platformsdk/Redist/4.0.1371.1/NT4/EN-US/psinst.EXE + +On Windows 2000 it is available installing the Windows 2000 Support Tools, located on the +Support\Tools folder of the installation Windows 2000 CD-ROM. + + +Registry DNS lookup + +On Windows platforms, if no value is specified in the dns_nameservers option on +squid.conf or in the /etc/resolv.conf file, the list of DNS name servers are +taken from the Windows registry, both static and dynamic DHCP configurations +are supported. + + +Awin32 fs module + +awin32 is a native WIN32 Async I/O Squid fs module derived from aufs. Configuration +in squid.conf is identical to aufs, but You nedd to specify awin32 instead aufs as +cache type. Index: squid/helpers/basic_auth/Makefile.am diff -u squid/helpers/basic_auth/Makefile.am:1.2 squid/helpers/basic_auth/Makefile.am:1.2.12.2 --- squid/helpers/basic_auth/Makefile.am:1.2 Fri Jun 28 07:59:08 2002 +++ squid/helpers/basic_auth/Makefile.am Tue Jul 16 06:34:35 2002 @@ -3,5 +3,5 @@ # $Id$ # -DIST_SUBDIRS = getpwnam LDAP MSNT multi-domain-NTLM NCSA PAM SMB YP SASL winbind +DIST_SUBDIRS = getpwnam LDAP MSNT multi-domain-NTLM NCSA PAM SMB YP SASL winbind win32_locallogon SUBDIRS = @BASIC_AUTH_HELPERS@ Index: squid/helpers/basic_auth/win32_locallogon/.cvsignore diff -u /dev/null squid/helpers/basic_auth/win32_locallogon/.cvsignore:1.1.6.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/basic_auth/win32_locallogon/.cvsignore Sat Jun 29 14:14:51 2002 @@ -0,0 +1,2 @@ +.cvsignore +Makefile.in Index: squid/helpers/basic_auth/win32_locallogon/Makefile.am diff -u /dev/null squid/helpers/basic_auth/win32_locallogon/Makefile.am:1.1.6.2 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/basic_auth/win32_locallogon/Makefile.am Tue Jul 16 06:34:36 2002 @@ -0,0 +1,17 @@ +# +# Makefile for the Squid Object Cache server +# +# $Id$ +# +# Uncomment and customize the following to suit your needs: +# + + +libexec_PROGRAMS = nt_auth + +nt_auth_SOURCES = NT_auth.c valid.c + +LDADD = -L$(top_builddir)/lib -lnetapi32 -ladvapi32 -lsspwin32 -lmiscutil + +INCLUDES = -I. -I$(top_builddir)/include -I$(top_srcdir)/include \ + -I$(top_srcdir)/src/ Index: squid/helpers/basic_auth/win32_locallogon/NT_auth.c diff -u /dev/null squid/helpers/basic_auth/win32_locallogon/NT_auth.c:1.1.6.4 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/basic_auth/win32_locallogon/NT_auth.c Sun Sep 29 09:31:17 2002 @@ -0,0 +1,172 @@ +/* + NT_auth - Version 2.0 + + Modified to act as a Squid authenticator module. + Removed all Pike stuff. + Returns OK for a successful authentication, or ERR upon error. + + Guido Serassio, Torino - Italy + + Uses code from - + Antonino Iannella 2000 + Andrew Tridgell 1997 + Richard Sharpe 1996 + Bill Welliver 1999 + + * Distributed freely under the terms of the GNU General Public License, + * version 2. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. +*/ + +#include "squid.h" + +/* Check if we try to compile on a Windows Platform */ +#if defined(_SQUID_CYGWIN_) || defined(_SQUID_MSWIN_) + +#include "valid.h" + +static char NTGroup[256]; +char * NTAllowedGroup; +char * NTDisAllowedGroup; +int UseDisallowedGroup = 0; +int UseAllowedGroup = 0; + +/* + * options: + * -a can specify a Windows Local Group name allowed to authenticate. + * -d can specify a Windows Local Group name not allowed to authenticate. + * -D can specify the default Domain against to authenticate. + */ +char *my_program_name = NULL; + +void +usage() +{ + fprintf(stderr, + "%s usage:\n%s [-a UserGroup] [-d UserGroup] [-D DefaultDomain] \n" + "-a can specify a Windows Local Group name allowed to authenticate\n" + "-d can specify a Windows Local Group name not allowed to authenticate\n" + "-D can specify the default Domain against to authenticate\n" + "-h this message\n\n", + my_program_name, my_program_name); +} + +void +process_options(int argc, char *argv[]) +{ + int opt, had_error = 0; + while (-1 != (opt = getopt(argc, argv, "ha:d:D:"))) { + switch (opt) { + case 'a': + safe_free(NTAllowedGroup); + NTAllowedGroup=xstrdup(optarg); + UseAllowedGroup = 1; + break; + case 'd': + safe_free(NTDisAllowedGroup); + NTDisAllowedGroup=xstrdup(optarg); + UseDisallowedGroup = 1; + break; + case 'D': + strcpy(Default_NTDomain, optarg); + break; + case 'h': + usage(argv[0]); + exit(0); + case '?': + opt = optopt; + /* fall thru to default */ + default: + fprintf(stderr, "Unknown option: -%c. Exiting\n", opt); + had_error = 1; + } + } + if (had_error) { + usage(); + exit(1); + } +} + +/* Main program for simple authentication. + Scans and checks for Squid input, and attempts to validate the user. +*/ + +int +main(int argc, char **argv) + +{ + char wstr[256]; + char username[256]; + char password[256]; + char *p; + int err = 0; + + my_program_name = argv[0]; + process_options(argc, argv); + + if (LoadSecurityDll(SSP_BASIC) == NULL) { + fprintf(stderr, "FATAL, can't initialize SSPI, exiting.\n"); + exit(1); + } + atexit(UnloadSecurityDll); + + /* initialize FDescs */ + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + while (1) { + /* Read whole line from standard input. Terminate on break. */ + if (fgets(wstr, 255, stdin) == NULL) + break; + + if (NULL == strchr(wstr, '\n')) { + err = 1; + continue; + } + if (err) { + fprintf(stderr, "Oversized message\n"); + goto error; + } + + if ((p = strchr(wstr, '\n')) != NULL) + *p = '\0'; /* strip \n */ + if ((p = strchr(wstr, '\r')) != NULL) + *p = '\0'; /* strip \r */ + /* Clear any current settings */ + username[0] = '\0'; + password[0] = '\0'; + sscanf(wstr, "%s %s", username, password); /* Extract parameters */ + + /* Check for invalid or blank entries */ + if ((username[0] == '\0') || (password[0] == '\0')) { + fprintf(stderr, "Invalid Request\n"); + puts("ERR"); + fflush(stdout); + continue; + } + rfc1738_unescape(username); + rfc1738_unescape(password); + if (Valid_User(username, password, NTGroup) == NTV_NO_ERROR) + puts("OK"); + else +error: + puts("ERR"); + err = 0; + fflush(stdout); + } + return 0; +} + +#else /* NON Windows Platform !!! */ + +#error NON WINDOWS PLATFORM + +#endif Index: squid/helpers/basic_auth/win32_locallogon/README.txt diff -u /dev/null squid/helpers/basic_auth/win32_locallogon/README.txt:1.1.6.4 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/basic_auth/win32_locallogon/README.txt Sun Sep 29 09:31:17 2002 @@ -0,0 +1,95 @@ +This is a simple authentication module for the Squid proxy server running on Windows NT +to authenticate users on an NT domain in native WIN32 mode. + +Usage is simple. It accepts a username and password on standard input +and will return OK if the username/password is valid for the domain/machine, +or ERR if there was some problem. +It's possible to authenticate against NT trusted domains specifyng the username +in the domain\\username Microsoft notation. + + +============== +Program Syntax +============== + +nt_auth [-a UserGroup] [-d UserGroup] [-D DefaultDomain] + +-a can specify a Windows Local Group name allowed to authenticate. +-d can specify a Windows Local Group name not allowed to authenticate. +-D can specify the default Domain against to authenticate. + +This is released under the GNU General Public License and +is available from http://www.serassio.it. + + +============== +Allowing Users +============== + +Users that are allowed to access the web proxy must have the Windows NT +User Rights "logon from the network" and must be included in the NT LOCAL User Groups +specified in the Authenticator's command line. +This can be accomplished creating a local user group on the NT machine, grant the privilege, +and adding users to it. + +Refer to Squid documentation for the required changes to squid.conf. + + +============ +Installation +============ + +Type 'make', then 'make install', then 'make clean'. + +The default is to install 'nt_auth' into /usr/local/squid/libexec. + +Refer to Squid documentation for the required changes to squid.conf. +You will need to set the following line to enable the authenticator: + +authenticate_program /usr/local/squid/bin/NT_auth + +You will need to set the following lines to enable authentication for +your access list - + + acl proxy_auth REQUIRED + http_access allow + +You will need to specify the absolute path to NT_auth in the +authenticate_program directive, and check the authenticate_children +and authenticate_ttl. + + +================== +Compilation issues +================== + +The Makefile assumes that GCC is in the current PATH. +NT_auth compile ONLY on CygWin Environment or MS VC++. + + +======= +Testing +======= + +I strongly urge that NT_auth is tested prior to being used in a +production environment. It may behave differently on different platforms. +To test it, run it from the command line. Enter username and password +pairs separated by a space. Press ENTER to get an OK or ERR message. +Make sure pressing behaves the same as a carriage return. +Make sure pressing aborts the program. + +Test that entering no details does not result in an OK or ERR message. +Test that entering an invalid username and password results in an ERR message. +Note that if NT guest user access is allowed on the PDC, an OK message +may be returned instead of ERR. +Test that entering an valid username and password results in an OK message. +Test that entering a guest username and password returns the correct +response for the site's access policy. + + +=============== +Contact details +=============== + +To contact the maintainer of this package, e-mail on squidnt@serassio.it. +The latest version may be found on http://www.serassio.it/SquidNT.htm. Index: squid/helpers/basic_auth/win32_locallogon/valid.c diff -u /dev/null squid/helpers/basic_auth/win32_locallogon/valid.c:1.1.6.3 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/basic_auth/win32_locallogon/valid.c Sun Sep 29 09:31:17 2002 @@ -0,0 +1,164 @@ +/* + NT_auth - Version 2.0 + + Modified to act as a Squid authenticator module. + Removed all Pike stuff. + Returns OK for a successful authentication, or ERR upon error. + + Guido Serassio, Torino - Italy + + Uses code from - + Antonino Iannella 2000 + Andrew Tridgell 1997 + Richard Sharpe 1996 + Bill Welliver 1999 + + * Distributed freely under the terms of the GNU General Public License, + * version 2. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. +*/ + +#include "squid.h" + +/* Check if we try to compile on a Windows Platform */ +#if defined(_SQUID_CYGWIN_) || defined(_SQUID_MSWIN_) + +#if defined(_SQUID_CYGWIN_) +#include +#endif +#include +#include +#include "valid.h" + +char Default_NTDomain[256] = NTV_DEFAULT_DOMAIN; + +/* returns 1 on success, 0 on failure */ +int +Valid_Group(char *UserName, char *Group) +{ + int result = FALSE; + WCHAR wszUserName[256]; // Unicode user name + WCHAR wszGroup[256]; // Unicode Group + + LPLOCALGROUP_USERS_INFO_0 pBuf = NULL; + LPLOCALGROUP_USERS_INFO_0 pTmpBuf; + DWORD dwLevel = 0; + DWORD dwFlags = LG_INCLUDE_INDIRECT; + DWORD dwPrefMaxLen = -1; + DWORD dwEntriesRead = 0; + DWORD dwTotalEntries = 0; + NET_API_STATUS nStatus; + DWORD i; + DWORD dwTotalCount = 0; + +/* Convert ANSI User Name and Group to Unicode */ + + MultiByteToWideChar(CP_ACP, 0, UserName, + strlen(UserName) + 1, wszUserName, + sizeof(wszUserName) / sizeof(wszUserName[0])); + MultiByteToWideChar(CP_ACP, 0, Group, + strlen(Group) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0])); + + /* + * Call the NetUserGetLocalGroups function + * specifying information level 0. + * + * The LG_INCLUDE_INDIRECT flag specifies that the + * function should also return the names of the local + * groups in which the user is indirectly a member. + */ + nStatus = NetUserGetLocalGroups(NULL, + wszUserName, + dwLevel, + dwFlags, + (LPBYTE *) & pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries); + /* + * If the call succeeds, + */ + if (nStatus == NERR_Success) { + if ((pTmpBuf = pBuf) != NULL) { + for (i = 0; i < dwEntriesRead; i++) { + if (pTmpBuf == NULL) { + result = FALSE; + break; + } + if (wcscmp(pTmpBuf->lgrui0_name, wszGroup) == 0) { + result = TRUE; + break; + } + pTmpBuf++; + dwTotalCount++; + } + } + } else + result = FALSE; +/* + * Free the allocated memory. + */ + if (pBuf != NULL) + NetApiBufferFree(pBuf); + return result; +} + +/* Valid_User return codes - + 0 - User authenticated successfully. + 1 - Server error. + 2 - Protocol error. + 3 - Logon error; Incorrect password or username given. +*/ + +int +Valid_User(char *UserName, char *Password, char *Group) +{ + int result = NTV_LOGON_ERROR; + char NTDomain[256]; + char *domain_qualify; + char DomainUser[256]; + char User[256]; + + strcpy(NTDomain, UserName); + if ((domain_qualify = strchr(NTDomain, '\\')) == NULL) { + strcpy(User, NTDomain); + strcpy(NTDomain, Default_NTDomain); + } else { + strcpy(User, domain_qualify + 1); + domain_qualify[0] = '\0'; + } + /* Log the client on to the local computer. */ + if (!SSPLogonUser(User, Password, NTDomain)) { + result = NTV_LOGON_ERROR; + } else { + result = NTV_NO_ERROR; + if (strcmp(NTDomain, NTV_DEFAULT_DOMAIN) == 0) + strcpy(DomainUser, User); + else { + strcpy(DomainUser, NTDomain); + strcat(DomainUser, "\\"); + strcat(DomainUser, User); + } + if (UseAllowedGroup) { + if (!Valid_Group(DomainUser, NTAllowedGroup)) { + result = NTV_LOGON_ERROR; + } + } + if (UseDisallowedGroup) { + if (Valid_Group(DomainUser, NTDisAllowedGroup)) { + result = NTV_LOGON_ERROR; + } + } + } + return result; +} +#else /* NON Windows Platform !!! */ + +#error NON WINDOWS PLATFORM + +#endif Index: squid/helpers/basic_auth/win32_locallogon/valid.h diff -u /dev/null squid/helpers/basic_auth/win32_locallogon/valid.h:1.1.6.2 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/basic_auth/win32_locallogon/valid.h Tue Jul 16 06:34:36 2002 @@ -0,0 +1,55 @@ +/* + NT_auth - Version 2.0 + + Modified to act as a Squid authenticator module. + Removed all Pike stuff. + Returns OK for a successful authentication, or ERR upon error. + + Guido Serassio, Torino - Italy + + Uses code from - + Antonino Iannella 2000 + Andrew Tridgell 1997 + Richard Sharpe 1996 + Bill Welliver 1999 + + * Distributed freely under the terms of the GNU General Public License, + * version 2. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. +*/ + +#ifndef _VALID_H_ +#define _VALID_H_ + +#include "sspwin32.h" + +/* SMB User verification function */ + +#define NTV_NO_ERROR 0 +#define NTV_SERVER_ERROR 1 +#define NTV_PROTOCOL_ERROR 2 +#define NTV_LOGON_ERROR 3 + +#ifndef LOGON32_LOGON_NETWORK +#define LOGON32_LOGON_NETWORK 3 +#endif + +#define NTV_DEFAULT_DOMAIN "." + +extern char * NTAllowedGroup; +extern char * NTDisAllowedGroup; +extern int UseDisallowedGroup; +extern int UseAllowedGroup; +extern char Default_NTDomain[256]; + +int Valid_User(char *,char *, char *); + +#endif Index: squid/helpers/basic_auth/winbind/wb_basic_auth.c diff -u squid/helpers/basic_auth/winbind/wb_basic_auth.c:1.6 squid/helpers/basic_auth/winbind/wb_basic_auth.c:1.2.12.5 --- squid/helpers/basic_auth/winbind/wb_basic_auth.c:1.6 Sun Aug 11 18:49:45 2002 +++ squid/helpers/basic_auth/winbind/wb_basic_auth.c Sat Aug 24 00:08:18 2002 @@ -113,8 +113,8 @@ c=memchr(buf,'\n',BUFFER_SIZE); if (c) { - *c = '\0'; - length = c-buf; + *c='\0'; + length=c-buf; } else { err = 1; return; Index: squid/helpers/external_acl/Makefile.am diff -u squid/helpers/external_acl/Makefile.am:1.2 squid/helpers/external_acl/Makefile.am:1.2.12.2 --- squid/helpers/external_acl/Makefile.am:1.2 Sun Jul 7 11:43:53 2002 +++ squid/helpers/external_acl/Makefile.am Tue Jul 16 06:34:36 2002 @@ -3,5 +3,5 @@ # $Id$ # -DIST_SUBDIRS = ip_user ldap_group unix_group wbinfo_group winbind_group +DIST_SUBDIRS = ip_user ldap_group unix_group wbinfo_group winbind_group win32_group SUBDIRS = @EXTERNAL_ACL_HELPERS@ Index: squid/helpers/external_acl/win32_group/.cvsignore diff -u /dev/null squid/helpers/external_acl/win32_group/.cvsignore:1.1.6.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/external_acl/win32_group/.cvsignore Tue Jul 16 06:34:36 2002 @@ -0,0 +1,2 @@ +.cvsignore +Makefile.in Index: squid/helpers/external_acl/win32_group/Makefile.am diff -u /dev/null squid/helpers/external_acl/win32_group/Makefile.am:1.1.2.2 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/external_acl/win32_group/Makefile.am Tue Jul 16 06:34:36 2002 @@ -0,0 +1,17 @@ +# +# Makefile for the Squid Object Cache server +# +# $Id$ +# +# Uncomment and customize the following to suit your needs: +# + + +libexec_PROGRAMS = win32_check_group + +win32_check_group_SOURCES = win32_check_group.c + +INCLUDES = -I. -I$(top_builddir)/include -I$(top_srcdir)/include \ + -I$(top_srcdir)/src + +LDADD = -L$(top_builddir)/lib -lmiscutil -lnetapi32 -ladvapi32 Index: squid/helpers/external_acl/win32_group/readme.txt diff -u /dev/null squid/helpers/external_acl/win32_group/readme.txt:1.1.2.3 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/external_acl/win32_group/readme.txt Sun Sep 29 09:31:17 2002 @@ -0,0 +1,71 @@ + +This is the readme.txt file for win32_check_group, an external +helper fo the External ACL Scheme for Squid. + + +This helper must be used in with an authentication scheme, tipcally basic +or NTLM based on Windows NT/2000 domain users. +It reads from the standard input the domain username and a list of groups +and tries to match it against the groups membership of the specified username. + + +============== +Program Syntax +============== + +win32_check_group [-Gdh] + +-G start helper in Global Group mode +-d enable debug mode +-h this message + + +================ +squid.conf usage +================ + +external_acl_type NT_global_group %LOGIN /usr/local/squid/libexec/win32_check_group -G +external_acl_type NT_local_group %LOGIN /usr/local/squid/libexec/win32_check_group + +acl GProxyUsers external NT_global_group GProxyUsers +acl LProxyUsers external NT_local_group LProxyUsers +acl password proxy_auth REQUIRED + +http_access allow password GProxyUsers +http_access allow password LProxyUsers +http_access deny all + +In the previous example all validated NT users member of GProxyUsers Global +domain group or member of LProxyUsers machine local group are allowed to +use the cache. + +Groups with spaces in name must be quoted, for example "Domain Users" + +NOTES: +- The group name comparation is case sensitive, so group name + must be specified with same case as in the NT/2000 Domain. +- Native WIN32 NTLM and Basic Helpers must be used without the + -a & -d switches. + +Refer to Squid documentation for the more details on squid.conf. + + +======= +Testing +======= + +I strongly urge that win32_check_group is tested prior to being used in a +production environment. It may behave differently on different platforms. +To test it, run it from the command line. Enter username and group +pairs separated by a space (username must entered with domain\\username +syntax). Press ENTER to get an OK or ERR message. +Make sure pressing behaves the same as a carriage return. +Make sure pressing aborts the program. + +Test that entering no details does not result in an OK or ERR message. +Test that entering an invalid username and group results in an ERR message. +Test that entering an valid username and group results in an OK message. + +-- +Serassio Guido +squidnt@serassio.it Index: squid/helpers/external_acl/win32_group/win32_check_group.c diff -u /dev/null squid/helpers/external_acl/win32_group/win32_check_group.c:1.1.2.5 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/external_acl/win32_group/win32_check_group.c Tue Oct 22 12:33:25 2002 @@ -0,0 +1,523 @@ +/* + * $Id$ + * + * This is a helper for the external ACL interface for Squid Cache + * Copyright (C) 2002 Guido Serassio + * Based on previous work of Rodrigo Albani de Campos + * + * It reads from the standard input the domain username and a list of groups + * and tries to match it against the groups membership of the specified username. + * Returns `OK' if the user belongs to a group or `ERR' otherwise, as + * described on http://devel.squid-cache.org/external_acl/config.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#if defined(_SQUID_CYGWIN_) +#include +#endif +#undef assert +#include +#include +#include +#include + +#define BUFSIZE 8192 /* the stdin buffer size */ +int use_global = 0; +char debug_enabled = 0; +char *myname; +pid_t mypid; +char * machinedomain; + +#include "win32_check_group.h" + +char * +strwordtok(char *buf, char **t) +{ + unsigned char *word = NULL; + unsigned char *p = (unsigned char *) buf; + unsigned char *d; + unsigned char ch; + int quoted = 0; + if (!p) + p = (unsigned char *) *t; + if (!p) + goto error; + while (*p && isspace(*p)) + p++; + if (!*p) + goto error; + word = d = p; + while ((ch = *p)) { + switch (ch) { + case '\\': + p++; + *d++ = ch = *p; + if (ch) + p++; + break; + case '"': + quoted = !quoted; + p++; + break; + default: + if (!quoted && isspace(*p)) { + p++; + goto done; + } + *d++ = *p++; + break; + } + } + done: + *d++ = '\0'; + error: + *t = (char *) p; + return (char *) word; +} + + +char * AllocStrFromLSAStr(LSA_UNICODE_STRING LsaStr) +{ + size_t len; + static char * target; + + len = LsaStr.Length/sizeof(WCHAR) + 1; + + /* allocate buffer for str + null termination */ + safe_free(target); + target = (char *)xmalloc(len); + if (target == NULL) + return NULL; + + /* copy unicode buffer */ + WideCharToMultiByte(CP_ACP, 0, LsaStr.Buffer, LsaStr.Length, target, len, NULL, NULL ); + + /* add null termination */ + target[len-1] = '\0'; + return target; +} + + +char * GetDomainName(void) + +{ + LSA_HANDLE PolicyHandle; + LSA_OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS status; + PPOLICY_PRIMARY_DOMAIN_INFO ppdiDomainInfo; + PWKSTA_INFO_100 pwkiWorkstationInfo; + DWORD netret; + char * DomainName = NULL; + + /* + * Always initialize the object attributes to all zeroes. + */ + memset(&ObjectAttributes, '\0', sizeof(ObjectAttributes)); + + /* + * You need the local workstation name. Use NetWkstaGetInfo at level + * 100 to retrieve a WKSTA_INFO_100 structure. + * + * The wki100_computername field contains a pointer to a UNICODE + * string containing the local computer name. + */ + netret = NetWkstaGetInfo(NULL, 100, (LPBYTE *)&pwkiWorkstationInfo); + if (netret == NERR_Success) { + /* + * We have the workstation name in: + * pwkiWorkstationInfo->wki100_computername + * + * Next, open the policy object for the local system using + * the LsaOpenPolicy function. + */ + status = LsaOpenPolicy( + NULL, + &ObjectAttributes, + GENERIC_READ | POLICY_VIEW_LOCAL_INFORMATION, + &PolicyHandle + ); + + /* + * Error checking. + */ + if (status) { + debug("OpenPolicy Error: %ld\n", status); + } else { + + /* + * You have a handle to the policy object. Now, get the + * domain information using LsaQueryInformationPolicy. + */ + status = LsaQueryInformationPolicy(PolicyHandle, + PolicyPrimaryDomainInformation, + (PVOID *)&ppdiDomainInfo); + if (status) { + debug("LsaQueryInformationPolicy Error: %ld\n", status); + } else { + + /* Get name in useable format */ + DomainName = AllocStrFromLSAStr(ppdiDomainInfo->Name); + + /* + * Check the Sid pointer, if it is null, the + * workstation is either a stand-alone computer + * or a member of a workgroup. + */ + if (ppdiDomainInfo->Sid) { + + /* + * Member of a domain. Display it in debug mode. + */ + debug("Member of Domain %s\n",DomainName); + } else { + DomainName = NULL; + } + } + } + + /* + * Clean up all the memory buffers created by the LSA and + * Net* APIs. + */ + NetApiBufferFree(pwkiWorkstationInfo); + LsaFreeMemory((LPVOID)ppdiDomainInfo); + } else + debug("NetWkstaGetInfo Error: %ld\n", netret); + return DomainName; +} + +/* returns 0 on match, -1 if no match */ +static int wcstrcmparray(const wchar_t *str, const char **array) +{ + WCHAR wszGroup[256]; // Unicode Group + + while (*array) { + MultiByteToWideChar(CP_ACP, 0, *array, + strlen(*array) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0])); + debug("Windows group: %S, Squid group: %S\n", str, wszGroup); + if (wcscmp(str, wszGroup) == 0) + return 0; + array++; + } + return -1; +} + +/* returns 1 on success, 0 on failure */ +int +Valid_Local_Groups(char *UserName, const char **Groups) +{ + int result = 0; + WCHAR wszUserName[256]; // Unicode user name + + LPLOCALGROUP_USERS_INFO_0 pBuf = NULL; + LPLOCALGROUP_USERS_INFO_0 pTmpBuf; + DWORD dwLevel = 0; + DWORD dwFlags = LG_INCLUDE_INDIRECT; + DWORD dwPrefMaxLen = -1; + DWORD dwEntriesRead = 0; + DWORD dwTotalEntries = 0; + NET_API_STATUS nStatus; + DWORD i; + DWORD dwTotalCount = 0; + +/* Convert ANSI User Name and Group to Unicode */ + + MultiByteToWideChar(CP_ACP, 0, UserName, + strlen(UserName) + 1, wszUserName, sizeof(wszUserName) / sizeof(wszUserName[0])); + + /* + * Call the NetUserGetLocalGroups function + * specifying information level 0. + * + * The LG_INCLUDE_INDIRECT flag specifies that the + * function should also return the names of the local + * groups in which the user is indirectly a member. + */ + nStatus = NetUserGetLocalGroups( + NULL, + wszUserName, + dwLevel, + dwFlags, + (LPBYTE *) & pBuf, + dwPrefMaxLen, + &dwEntriesRead, + &dwTotalEntries); + /* + * If the call succeeds, + */ + if (nStatus == NERR_Success) { + if ((pTmpBuf = pBuf) != NULL) { + for (i = 0; i < dwEntriesRead; i++) { + assert(pTmpBuf != NULL); + if (pTmpBuf == NULL) { + result = 0; + break; + } + if (wcstrcmparray(pTmpBuf->lgrui0_name, Groups) == 0) { + result = 1; + break; + } + pTmpBuf++; + dwTotalCount++; + } + } + } else + result = 0; +/* + * Free the allocated memory. + */ + if (pBuf != NULL) + NetApiBufferFree(pBuf); + return result; +} + + +/* returns 1 on success, 0 on failure */ +int +Valid_Global_Groups(char *UserName, const char **Groups) +{ + int result = 0; + WCHAR wszUserName[256]; // Unicode user name + WCHAR wszLocalDomain[256]; // Unicode Local Domain + WCHAR wszUserDomain[256]; // Unicode User Domain + + char NTDomain[256]; + char *domain_qualify; + char User[256]; + + LPCWSTR LclDCptr = NULL; + LPCWSTR UsrDCptr = NULL; + LPGROUP_USERS_INFO_0 pBuf = NULL; + LPGROUP_USERS_INFO_0 pTmpBuf; + DWORD dwLevel = 0; + DWORD dwPrefMaxLen = -1; + DWORD dwEntriesRead = 0; + DWORD dwTotalEntries = 0; + NET_API_STATUS nStatus; + DWORD i; + DWORD dwTotalCount = 0; + + strcpy(NTDomain, UserName); + if ((domain_qualify = strchr(NTDomain, '\\')) == NULL) { + return result; + } else { + strcpy(User, domain_qualify + 1); + domain_qualify[0] = '\0'; + _strlwr(NTDomain); + } + +/* Convert ANSI User Name and Group to Unicode */ + + MultiByteToWideChar(CP_ACP, 0, User, + strlen(User) + 1, wszUserName, + sizeof(wszUserName) / sizeof(wszUserName[0])); + MultiByteToWideChar(CP_ACP, 0, machinedomain, + strlen(machinedomain) + 1, wszLocalDomain, sizeof(wszLocalDomain) / sizeof(wszLocalDomain[0])); + + nStatus = NetGetAnyDCName( + NULL, + wszLocalDomain, + (LPBYTE *) & LclDCptr); + + if (nStatus == NERR_Success) { + if (strcmp(NTDomain, machinedomain) != 0) { + MultiByteToWideChar(CP_ACP, 0, NTDomain, + strlen(NTDomain) + 1, wszUserDomain, sizeof(wszUserDomain) / sizeof(wszUserDomain[0])); + nStatus = NetGetAnyDCName( + LclDCptr, + wszUserDomain, + (LPBYTE *) & UsrDCptr); + if (nStatus != NERR_Success) { + fprintf(stderr, "%s Can't find DC for domain %s\n", myname, NTDomain); + if (LclDCptr != NULL) + NetApiBufferFree((LPVOID) LclDCptr); + if (UsrDCptr != NULL) + NetApiBufferFree((LPVOID) UsrDCptr); + return result; + } + } else + UsrDCptr = LclDCptr; + + /* + * Call the NetUserGetLocalGroups function + * specifying information level 0. + * + * The LG_INCLUDE_INDIRECT flag specifies that the + * function should also return the names of the local + * groups in which the user is indirectly a member. + */ + nStatus = NetUserGetGroups(UsrDCptr, + wszUserName, + dwLevel, + (LPBYTE *) & pBuf, + dwPrefMaxLen, + &dwEntriesRead, + &dwTotalEntries); + /* + * If the call succeeds, + */ + if (nStatus == NERR_Success) { + if ((pTmpBuf = pBuf) != NULL) { + for (i = 0; i < dwEntriesRead; i++) { + assert(pTmpBuf != NULL); + if (pTmpBuf == NULL) { + result = 0; + break; + } + if (wcstrcmparray(pTmpBuf->grui0_name, Groups) == 0) { + result = 1; + break; + } + pTmpBuf++; + dwTotalCount++; + } + } + } else + result = 0; + } else { + fprintf(stderr, "%s Can't find DC for domain %s\n", myname, machinedomain); + } + /* + * Free the allocated memory. + */ + if (pBuf != NULL) + NetApiBufferFree(pBuf); + if (LclDCptr != NULL) + NetApiBufferFree((LPVOID) LclDCptr); + if (UsrDCptr != NULL) + NetApiBufferFree((LPVOID) UsrDCptr); + return result; +} + +static void +usage(char *program) +{ + fprintf(stderr,"Usage: %s [-d][-G][-h]\n" + " -d enable debugging\n" + " -G enable Domain Global group mode\n" + " -h this message\n", + program); +} + +void +process_options(int argc, char *argv[]) +{ + int opt; + + opterr = 0; + while (-1 != (opt = getopt(argc, argv, "Gdh"))) { + switch (opt) { + case 'G': + use_global = 1; + break; + case 'd': + debug_enabled = 1; + break; + case 'h': + usage(argv[0]); + exit(0); + case '?': + opt = optopt; + /* fall thru to default */ + default: + fprintf(stderr, "%s Unknown option: -%c. Exiting\n", myname, opt); + usage(argv[0]); + exit(1); + break; /* not reached */ + } + } + return; +} + + +int +main (int argc, char *argv[]) +{ + char *p, *t; + char buf[BUFSIZE]; + char *username; + char *group; + int err = 0; + const char *groups[512]; + int n; + + if (argc > 0) { /* should always be true */ + myname=strrchr(argv[0],'/'); + if (myname==NULL) + myname=argv[0]; + } else { + myname="(unknown)"; + } + mypid=getpid(); + + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + /* Check Command Line */ + process_options(argc, argv); + + if (use_global) { + if ((machinedomain = GetDomainName()) == NULL) { + fprintf(stderr, "%s Can't read machine domain\n", myname); + exit(1); + } + _strlwr(machinedomain); + } + + debug("External ACL win32 group helper build " __DATE__ ", " __TIME__ + " starting up...\n"); + + /* Main Loop */ + while (fgets (buf, BUFSIZE, stdin)) + { + if (NULL == strchr(buf, '\n')) { + err = 1; + continue; + } + if (err) { + fprintf(stderr, "Oversized message\n"); + goto error; + } + + if ((p = strchr(buf, '\n')) != NULL) + *p = '\0'; /* strip \n */ + if ((p = strchr(buf, '\r')) != NULL) + *p = '\0'; /* strip \r */ + + debug("Got '%s' from Squid (length: %d).\n",buf,sizeof(buf)); + + if (buf[0] == '\0') { + fprintf(stderr, "Invalid Request\n"); + goto error; + } + + username = strwordtok(buf, &t); + for (n = 0; (group = strwordtok(NULL, &t)) != NULL; n++) + groups[n] = group; + groups[n] = NULL; + + if ((use_global ? Valid_Global_Groups(username, groups) : Valid_Local_Groups(username, groups))) { + printf ("OK\n"); + } else { +error: + printf ("ERR\n"); + } + err = 0; + } + return 0; +} Index: squid/helpers/external_acl/win32_group/win32_check_group.h diff -u /dev/null squid/helpers/external_acl/win32_group/win32_check_group.h:1.1.8.2 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/external_acl/win32_group/win32_check_group.h Sun Sep 29 09:31:17 2002 @@ -0,0 +1,77 @@ +/* + * (C) 2002 Guido Serassio + * Based on previous work of Francesco Chemolli, Robert Collins and Andrew Doran + * + * Distributed freely under the terms of the GNU General Public License, + * version 2. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#undef debug + +/************* CONFIGURATION ***************/ +/* + * define this if you want debugging + */ +#ifndef DEBUG +#define DEBUG +#endif + +/************* END CONFIGURATION ***************/ + +#include + +/* Debugging stuff */ + +#ifdef __GNUC__ /* this is really a gcc-ism */ +#ifdef DEBUG +#include +#include +static char *__foo; +extern char debug_enabled; +#define debug(X...) if (debug_enabled) { \ + fprintf(stderr,"%s[%d](%s:%d): ", myname, mypid, \ + ((__foo=strrchr(__FILE__,'/'))==NULL?__FILE__:__foo+1),\ + __LINE__);\ + fprintf(stderr,X); } +#else /* DEBUG */ +#define debug(X...) /* */ +#endif /* DEBUG */ +#else /* __GNUC__ */ +extern char debug_enabled; +static void +debug(char *format,...) +{ +#ifdef DEBUG +#ifdef _SQUID_MSWIN_ + if (debug_enabled) { + va_list args; + + va_start(args,format); + fprintf(stderr, "%s[%d]: ", myname, mypid); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + va_end(args); + } +#endif /* _SQUID_MSWIN_ */ +#endif /* DEBUG */ +} +#endif /* __GNUC__ */ + + +/* A couple of harmless helper macros */ +#define SEND(X) debug("sending '%s' to squid\n",X); printf(X "\n"); +#ifdef __GNUC__ +#define SEND2(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y); +#else +/* no gcc, no debugging. varargs macros are a gcc extension */ +#define SEND2(X,Y) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y); +#endif Index: squid/helpers/ntlm_auth/Makefile.am diff -u squid/helpers/ntlm_auth/Makefile.am:1.2 squid/helpers/ntlm_auth/Makefile.am:1.2.12.2 --- squid/helpers/ntlm_auth/Makefile.am:1.2 Fri Jun 28 08:00:33 2002 +++ squid/helpers/ntlm_auth/Makefile.am Tue Jul 16 06:22:02 2002 @@ -3,5 +3,5 @@ # $Id$ # -DIST_SUBDIRS = fakeauth no_check SMB winbind +DIST_SUBDIRS = fakeauth no_check SMB winbind NTLMSSP-WIN32 SUBDIRS = @NTLM_AUTH_HELPERS@ Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/.cvsignore diff -u /dev/null squid/helpers/ntlm_auth/NTLMSSP-WIN32/.cvsignore:1.1.8.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/.cvsignore Tue Jul 16 06:22:02 2002 @@ -0,0 +1,2 @@ +.cvsignore +Makefile.in Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/Makefile.am diff -u /dev/null squid/helpers/ntlm_auth/NTLMSSP-WIN32/Makefile.am:1.1.8.2 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/Makefile.am Tue Jul 16 06:34:36 2002 @@ -0,0 +1,14 @@ +# +# Makefile for the Squid Object Cache server +# +# $Id$ +# + +libexec_PROGRAMS = ntlm_auth + +ntlm_auth_SOURCES = libntlmssp.c ntlm_auth.c ntlm.h + +INCLUDES = -I. -I$(top_srcdir)/include -I$(top_srcdir)/src + +LDADD = -L$(top_builddir)/lib -lntlmauth -lsspwin32 -lnetapi32 \ + -ladvapi32 -lmiscutil $(CRYPTLIB) $(XTRA_LIBS) Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c diff -u /dev/null squid/helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c:1.1.8.2 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/libntlmssp.c Sun Sep 29 09:31:18 2002 @@ -0,0 +1,380 @@ +/* + * (C) 2002 Guido Serassio + * Based on previous work of Francesco Chemolli and Robert Collins + * Distributed freely under the terms of the GNU General Public License, + * version 2. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +typedef unsigned char uchar; + +#include "squid.h" +#include "ntlm.h" +#include +#include + +#ifdef DEBUG +#define debug_dump_ntlmssp_flags dump_ntlmssp_flags +#else /* DEBUG */ +#define debug_dump_ntlmssp_flags(X) /* empty */ +#endif /* DEBUG */ + +#define ENCODED_PASS_LEN 24 +static char challenge[NONCE_LEN]; +static char lmencoded_empty_pass[ENCODED_PASS_LEN], + ntencoded_empty_pass[ENCODED_PASS_LEN]; + +/* returns 1 on success, 0 on failure */ +int +Valid_Group(char *UserName, char *Group) +{ + int result = FALSE; + WCHAR wszUserName[256]; // Unicode user name + WCHAR wszGroup[256]; // Unicode Group + + LPLOCALGROUP_USERS_INFO_0 pBuf = NULL; + LPLOCALGROUP_USERS_INFO_0 pTmpBuf; + DWORD dwLevel = 0; + DWORD dwFlags = LG_INCLUDE_INDIRECT; + DWORD dwPrefMaxLen = -1; + DWORD dwEntriesRead = 0; + DWORD dwTotalEntries = 0; + NET_API_STATUS nStatus; + DWORD i; + DWORD dwTotalCount = 0; + +/* Convert ANSI User Name and Group to Unicode */ + + MultiByteToWideChar(CP_ACP, 0, UserName, + strlen(UserName) + 1, wszUserName, + sizeof(wszUserName) / sizeof(wszUserName[0])); + MultiByteToWideChar(CP_ACP, 0, Group, + strlen(Group) + 1, wszGroup, sizeof(wszGroup) / sizeof(wszGroup[0])); + + /* + * Call the NetUserGetLocalGroups function + * specifying information level 0. + * + * The LG_INCLUDE_INDIRECT flag specifies that the + * function should also return the names of the local + * groups in which the user is indirectly a member. + */ + nStatus = NetUserGetLocalGroups(NULL, + wszUserName, + dwLevel, + dwFlags, + (LPBYTE *) & pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries); + /* + * If the call succeeds, + */ + if (nStatus == NERR_Success) { + if ((pTmpBuf = pBuf) != NULL) { + for (i = 0; i < dwEntriesRead; i++) { + if (pTmpBuf == NULL) { + result = FALSE; + break; + } + if (wcscmp(pTmpBuf->lgrui0_name, wszGroup) == 0) { + result = TRUE; + break; + } + pTmpBuf++; + dwTotalCount++; + } + } + } else + result = FALSE; +/* + * Free the allocated memory. + */ + if (pBuf != NULL) + NetApiBufferFree(pBuf); + return result; +} + +/* returns 0 on success, > 0 on failure */ +static int +init_challenge(void) +{ + static unsigned hash; + int r; + int i; + + r = (int) rand(); + r = (hash ^ r) + r; + for (i = 0; i < NONCE_LEN; i++) { + challenge[i] = r; + r = (r >> 2) ^ r; + } + hash = r; + return 0; +} + +char * AllocStrFromLSAStr(LSA_UNICODE_STRING LsaStr) +{ + size_t len; + static char * target; + + len = LsaStr.Length/sizeof(WCHAR) + 1; + + /* allocate buffer for str + null termination */ + safe_free(target); + target = (char *)xmalloc(len); + if (target == NULL) + return NULL; + + /* copy unicode buffer */ + WideCharToMultiByte(CP_ACP, 0, LsaStr.Buffer, LsaStr.Length, target, len, NULL, NULL ); + + /* add null termination */ + target[len-1] = '\0'; + return target; +} + +char * GetDomainName(void) + +{ + LSA_HANDLE PolicyHandle; + LSA_OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS status; + PPOLICY_PRIMARY_DOMAIN_INFO ppdiDomainInfo; + PWKSTA_INFO_100 pwkiWorkstationInfo; + DWORD netret; + char * DomainName = NULL; + + /* + * Always initialize the object attributes to all zeroes. + */ + memset(&ObjectAttributes, '\0', sizeof(ObjectAttributes)); + + /* + * You need the local workstation name. Use NetWkstaGetInfo at level + * 100 to retrieve a WKSTA_INFO_100 structure. + * + * The wki100_computername field contains a pointer to a UNICODE + * string containing the local computer name. + */ + netret = NetWkstaGetInfo(NULL, 100, (LPBYTE *)&pwkiWorkstationInfo); + if (netret == NERR_Success) { + /* + * We have the workstation name in: + * pwkiWorkstationInfo->wki100_computername + * + * Next, open the policy object for the local system using + * the LsaOpenPolicy function. + */ + status = LsaOpenPolicy( + NULL, + &ObjectAttributes, + GENERIC_READ | POLICY_VIEW_LOCAL_INFORMATION, + &PolicyHandle + ); + + /* + * Error checking. + */ + if (status) { + debug("OpenPolicy Error: %ld\n", status); + } else { + + /* + * You have a handle to the policy object. Now, get the + * domain information using LsaQueryInformationPolicy. + */ + status = LsaQueryInformationPolicy(PolicyHandle, + PolicyPrimaryDomainInformation, + (void **)&ppdiDomainInfo); + if (status) { + debug("LsaQueryInformationPolicy Error: %ld\n", status); + } else { + + /* Get name in useable format */ + DomainName = AllocStrFromLSAStr(ppdiDomainInfo->Name); + + /* + * Check the Sid pointer, if it is null, the + * workstation is either a stand-alone computer + * or a member of a workgroup. + */ + if (ppdiDomainInfo->Sid) { + + /* + * Member of a domain. Display it in debug mode. + */ + debug("Member of Domain %s\n",DomainName); + } else { + DomainName = NULL; + } + } + } + + /* + * Clean up all the memory buffers created by the LSA and + * Net* APIs. + */ + NetApiBufferFree(pwkiWorkstationInfo); + LsaFreeMemory((LPVOID)ppdiDomainInfo); + } else + debug("NetWkstaGetInfo Error: %ld\n", netret); + return DomainName; +} + +const char * +make_challenge(void) +{ + static char ComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + static char * DomainName; + DWORD CNsize = MAX_COMPUTERNAME_LENGTH + 1; + + GetComputerName(ComputerName, &CNsize); + uc(ComputerName); + if ((DomainName=GetDomainName()) == NULL) + return NULL; + uc(DomainName); + debug("ComputerName:%s DomainName: %s",ComputerName,DomainName); + if (init_challenge() > 0) + return NULL; + return ntlm_make_challenge(DomainName, ComputerName, challenge, NONCE_LEN); +} + +int ntlm_errno; +static char credentials[1024]; /* we can afford to waste */ + + +/* Fetches the user's credentials from the challenge. + * Returns NULL if domain or user is not defined + * No identity control is performed. + * WARNING! The result is static storage, shared with ntlm_check_auth + */ +char * +fetch_credentials(ntlm_authenticate * auth, int auth_length) +{ + char *p = credentials; + lstring tmp; + tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->domain); + if (tmp.str == NULL) + return NULL; + memcpy(p, tmp.str, tmp.l); + p += tmp.l; + *p++ = '\\'; + tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->user); + if (tmp.str == NULL) + return NULL; + *(p + tmp.l) = '\0'; + return credentials; +} + +/* returns NULL on failure, or a pointer to + * the user's credentials (domain\\username) + * upon success. WARNING. It's pointing to static storage. + * In case of problem sets as side-effect ntlm_errno to one of the + * codes defined in ntlm.h + */ +char * +ntlm_check_auth(ntlm_authenticate * auth, int auth_length) +{ + int rv; + char pass[25] /*, encrypted_pass[40] */; + char *domain = credentials; + char *user; + lstring tmp; + +/* debug("fetching domain\n"); */ + tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->domain); + if (tmp.str == NULL || tmp.l == 0) { + debug("No domain supplied. Returning no-auth\n"); + ntlm_errno = NTLM_LOGON_ERROR; + return NULL; + } + memcpy(domain, tmp.str, tmp.l); + user = domain + tmp.l; + *user++ = '\0'; + +/* debug("fetching user name\n"); */ + tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->user); + if (tmp.str == NULL || tmp.l == 0) { + debug("No username supplied. Returning no-auth\n"); + ntlm_errno = NTLM_LOGON_ERROR; + return NULL; + } + memcpy(user, tmp.str, tmp.l); + *(user + tmp.l) = '\0'; + + + /* Authenticating against the NT response doesn't seem to work... */ + tmp = ntlm_fetch_string((char *) auth, auth_length, &auth->lmresponse); + if (tmp.str == NULL || tmp.l == 0) { + fprintf(stderr, "No auth at all. Returning no-auth\n"); + ntlm_errno = NTLM_LOGON_ERROR; + return NULL; + } + + memcpy(pass, tmp.str, tmp.l); + pass[25] = '\0'; + +#if 1 + debug ("Empty LM pass detection: user: '%s', ours:'%s', his: '%s'" + "(length: %d)\n", + user,lmencoded_empty_pass,tmp.str,tmp.l); + if (memcmp(tmp.str,lmencoded_empty_pass,ENCODED_PASS_LEN)==0) { + fprintf(stderr,"Empty LM password supplied for user %s\\%s. " + "No-auth\n",domain,user); + ntlm_errno=NTLM_LOGON_ERROR; + return NULL; + } + + tmp = ntlm_fetch_string ((char *) auth, auth_length, &auth->ntresponse); + if (tmp.str != NULL && tmp.l != 0) { + debug ("Empty NT pass detection: user: '%s', ours:'%s', his: '%s'" + "(length: %d)\n", + user,ntencoded_empty_pass,tmp.str,tmp.l); + if (memcmp(tmp.str,lmencoded_empty_pass,ENCODED_PASS_LEN)==0) { + fprintf(stderr,"Empty NT password supplied for user %s\\%s. " + "No-auth\n",domain,user); + ntlm_errno=NTLM_LOGON_ERROR; + return NULL; + } + } +#endif + + /* TODO: check against empty password!!!!! */ + + + + debug("checking domain: '%s', user: '%s', pass='%s'\n", domain, user, pass); + rv = SSPLogonUser(user, pass, domain); + + debug("Login attempt had result %d\n", rv); + + if (!rv) { /* failed */ + ntlm_errno = ss; + return NULL; + } + *(user - 1) = '\\'; /* hack. Performing, but ugly. */ + + if (UseAllowedGroup) { + if (!Valid_Group(credentials, NTAllowedGroup)) { + ntlm_errno = NTLM_BAD_NTGROUP; + debug("User %s not in allowed Group %s\n", credentials, NTAllowedGroup); + return NULL; + } + } + if (UseDisallowedGroup) { + if (Valid_Group(credentials, NTDisAllowedGroup)) { + ntlm_errno = NTLM_BAD_NTGROUP; + debug("User %s is in denied Group %s\n", credentials, NTDisAllowedGroup); + return NULL; + } + } + + debug("credentials: %s\n", credentials); + return credentials; +} Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h diff -u /dev/null squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h:1.1.8.2 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm.h Sun Aug 11 09:45:09 2002 @@ -0,0 +1,108 @@ +/* + * (C) 2002 Guido Serassio + * Based on previous work of Francesco Chemolli, Robert Collins and Andrew Doran + * + * Distributed freely under the terms of the GNU General Public License, + * version 2. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#ifndef _NTLM_H_ +#define _NTLM_H_ + +#include "sspwin32.h" +#include +#include +#include +#include "ntlmauth.h" +#undef debug + +/************* CONFIGURATION ***************/ +/* + * define this if you want debugging + */ +#ifndef DEBUG +#define DEBUG +#endif + +/************* END CONFIGURATION ***************/ + +#include + +/* Debugging stuff */ + +#ifdef __GNUC__ /* this is really a gcc-ism */ +#ifdef DEBUG +#include +#include +static char *__foo; +extern char debug_enabled; +#define debug(X...) if (debug_enabled) { \ + fprintf(stderr,"ntlm-auth[%d](%s:%d): ", getpid(), \ + ((__foo=strrchr(__FILE__,'/'))==NULL?__FILE__:__foo+1),\ + __LINE__);\ + fprintf(stderr,X); } +#else /* DEBUG */ +#define debug(X...) /* */ +#endif /* DEBUG */ +#else /* __GNUC__ */ +extern char debug_enabled; +static void +debug(char *format,...) +{ +#ifdef DEBUG +#ifdef _SQUID_MSWIN_ + if (debug_enabled) { + va_list args; + + va_start(args,format); + fprintf(stderr, "ntlm-auth[%d]: ",getpid()); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + va_end(args); + } +#endif /* _SQUID_MSWIN_ */ +#endif /* DEBUG */ +} +#endif /* __GNUC__ */ + + +/* A couple of harmless helper macros */ +#define SEND(X) debug("sending '%s' to squid\n",X); printf(X "\n"); +#ifdef __GNUC__ +#define SEND2(X,Y...) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y); +#else +/* no gcc, no debugging. varargs macros are a gcc extension */ +#define SEND2(X,Y) debug("sending '" X "' to squid\n",Y); printf(X "\n",Y); +#endif + +extern char * NTAllowedGroup; +extern char * NTDisAllowedGroup; +extern int UseDisallowedGroup; +extern int UseAllowedGroup; +extern int ntlm_errno; + +#define NTLM_NO_ERROR SEC_E_OK +#define NTLM_SERVER_ERROR SEC_E_NOT_SUPPORTED +#define NTLM_PROTOCOL_ERROR SEC_E_INCOMPLETE_MESSAGE +#define NTLM_LOGON_ERROR SEC_E_LOGON_DENIED +#define NTLM_UNTRUSTED_DOMAIN SEC_E_NO_AUTHENTICATING_AUTHORITY +#define NTLM_BAD_PROTOCOL SEC_E_BAD_PKGID +#define NTLM_NOT_CONNECTED SEC_E_SECPKG_NOT_FOUND +#define NTLM_BAD_NTGROUP -1 + +extern void uc(char *); + +const char *make_challenge(void); +extern char *ntlm_check_auth(ntlm_authenticate * auth, int auth_length); +extern char *fetch_credentials(ntlm_authenticate * auth, int auth_length); + +#endif /* _NTLM_H_ */ Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c diff -u /dev/null squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c:1.1.8.4 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/ntlm_auth.c Sun Sep 29 09:31:18 2002 @@ -0,0 +1,320 @@ +/* + * $Id$ + * + * This is a helper for NTLM Authentication for Squid Cache + * Copyright (C) 2002 Guido Serassio + * Based on previous work of Francesco Chemolli and Robert Collins + * + * Distributed freely under the terms of the GNU General Public License, + * version 2. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + + +#include "squid.h" +#include "ntlm.h" + +#define BUFFER_SIZE 10240 + + +#ifdef DEBUG +char error_messages_buffer[BUFFER_SIZE]; +#endif + +char load_balance = 0, protocol_pedantic = 0; +#ifdef NTLM_FAIL_OPEN +char last_ditch_enabled = 0; +#endif + +char debug_enabled=0; + + +char * NTAllowedGroup; +char * NTDisAllowedGroup; +int UseDisallowedGroup = 0; +int UseAllowedGroup = 0; + +/* makes a null-terminated string upper-case. Changes CONTENTS! */ +void +uc(char *string) +{ + char *p = string, c; + while ((c = *p)) { + *p = toupper(c); + p++; + } +} + +/* makes a null-terminated string lower-case. Changes CONTENTS! */ +static void +lc(char *string) +{ + char *p = string, c; + while ((c = *p)) { + *p = tolower(c); + p++; + } +} + +void +helperfail(const char *reason) +{ + SEND2("BH %s", reason); +} + +void +send_bh_or_ld(char *bhmessage, ntlm_authenticate * failedauth, int authlen) +{ +#ifdef NTLM_FAIL_OPEN + char *creds = NULL; + if (last_ditch_enabled) { + creds = fetch_credentials(failedauth, authlen); + if (creds) { + lc(creds); + SEND2("LD %s", creds); + } else { + SEND("NA last-ditch on, but no credentials"); + } + } else { +#endif + SEND2("BH %s", bhmessage); +#ifdef NTLM_FAIL_OPEN + } +#endif +} + +/* + * options: + * -v enables debugging statements if DEBUG was defined at build-time. + * -l if specified, changes behavior on failures to last-ditch. + * -a can specify a Windows Local Group name allowed to authenticate. + * -d can specify a Windows Local Group name not allowed to authenticate. + * domain\controller ... + */ +char *my_program_name = NULL; + +void +usage() +{ + fprintf(stderr, +#ifdef NTLM_FAIL_OPEN + "%s usage:\n%s [-v] [-a UserGroup] [-d UserGroup] [-l] [-h]\n" +#else + "%s usage:\n%s [-v] [-a UserGroup] [-d UserGroup] [-h]\n" +#endif + "-v enables verbose debugging.\n" +#ifdef NTLM_FAIL_OPEN + "-l if specified, changes behavior on failures to last-ditch\n" +#endif + "-a specify a Windows Local Group name allowed to authenticate\n" + "-d specify a Windows Local Group name not allowed to authenticate\n" + "-h this message\n\n", + my_program_name, my_program_name); +} + + +void +process_options(int argc, char *argv[]) +{ + int opt, had_error = 0; + + opterr =0; +#ifdef NTLM_FAIL_OPEN + while (-1 != (opt = getopt(argc, argv, "hvla:d:"))) { +#else + while (-1 != (opt = getopt(argc, argv, "hva:d:"))) { +#endif + switch (opt) { + case 'a': + safe_free(NTAllowedGroup); + NTAllowedGroup=xstrdup(optarg); + UseAllowedGroup = 1; + break; + case 'd': + safe_free(NTDisAllowedGroup); + NTDisAllowedGroup=xstrdup(optarg); + UseDisallowedGroup = 1; + break; +#ifdef NTLM_FAIL_OPEN + case 'l': + last_ditch_enabled = 1; + break; +#endif + case 'v': + debug_enabled=1; + break; + case 'h': + usage(); + exit(0); + case '?': + opt = optopt; + /* fall thru to default */ + default: + fprintf(stderr, "unknown option: -%c. Exiting\n", opt); + usage(); + had_error = 1; + } + } + if (had_error) + exit(1); +} + +/* tries connecting to the domain controllers in the "controllers" ring, + * with failover if the adequate option is specified. + */ +const char * +obtain_challenge() +{ + const char *ch = NULL; + + debug("attempting challenge retrieval\n"); + ch = make_challenge(); + debug("make_challenge returned %p\n", ch); + if (ch) { + debug("Got it\n"); + return ch; /* All went OK, returning */ + } + return NULL; +} + + +void +manage_request() +{ + ntlmhdr *fast_header; + char buf[BUFFER_SIZE]; + char *c, *decoded, *cred; + int plen; + int oversized = 0; + +try_again: + if (fgets(buf, BUFFER_SIZE, stdin) == NULL) { + debug("fgets() failed! dying..... errno=%d (%s)\n", errno, + strerror(errno)); + exit(1); /* BIIG buffer */ + } + c = memchr(buf, '\n', BUFFER_SIZE); /* safer against overrun than strchr */ + if (c) { + if (oversized) { + helperfail("illegal request received"); + fprintf(stderr, "Illegal request received: '%s'\n", buf); + return; + } + *c = '\0'; + } else { + fprintf(stderr, "No newline in '%s'\n", buf); + oversized = 1; + goto try_again; + } + + debug("Got '%s' from Squid\n", buf); + + if (memcmp(buf, "YR", 2) == 0) { /* refresh-request */ + c = (char *) obtain_challenge(); + SEND2("TT %s", c); + return; + } + if (memcmp(buf, "KK ", 3) == 0) { /* authenticate-request */ + /* figure out what we got */ + decoded = base64_decode(buf + 3); + /* Note: we don't need to manage memory at this point, since + * base64_decode returns a pointer to static storage. + */ + + if (!decoded) { /* decoding failure, return error */ + SEND("NA Packet format error, couldn't base64-decode"); + return; + } + /* fast-track-decode request type. */ + fast_header = (struct _ntlmhdr *) decoded; + + /* sanity-check: it IS a NTLMSSP packet, isn't it? */ + if (memcmp(fast_header->signature, "NTLMSSP", 8) != 0) { + SEND("NA Broken authentication packet"); + return; + } + switch (fast_header->type) { + case NTLM_NEGOTIATE: + SEND("NA Invalid negotiation request received"); + return; + /* notreached */ + case NTLM_CHALLENGE: + SEND + ("NA Got a challenge. We refuse to have our authority disputed"); + return; + /* notreached */ + case NTLM_AUTHENTICATE: + /* check against SSPI */ + plen = strlen(buf) * 3 / 4; /* we only need it here. Optimization */ + cred = ntlm_check_auth((ntlm_authenticate *) decoded, plen); + if (cred == NULL) { + switch (ntlm_errno) { + case NTLM_LOGON_ERROR: + SEND("NA Logon Failure"); + return; + case NTLM_BAD_NTGROUP: + SEND("NA Incorrect Group Membership"); + return; + case NTLM_UNTRUSTED_DOMAIN: + SEND("NA No authority could be contacted for authentication"); + return; + default: + SEND("NA SSPI Error"); + return; + } + } + lc(cred); /* let's lowercase them for our convenience */ + SEND2("AF %s", cred); + return; + default: + helperfail("unknown authentication packet type"); + return; + } + return; + } else { /* not an auth-request */ + helperfail("illegal request received"); + fprintf(stderr, "Illegal request received: '%s'\n", buf); + return; + } + helperfail("detected protocol error"); + return; +/********* END ********/ +} + +int +main(int argc, char *argv[]) +{ + + debug("ntlm_auth build " __DATE__ ", " __TIME__ " starting up...\n"); + + my_program_name = argv[0]; + process_options(argc, argv); + debug("options processed OK\n"); + srand( (unsigned)time( NULL ) ); + + if (LoadSecurityDll(SSP_NTLM) == NULL) { + fprintf(stderr, "FATAL, can't initialize SSPI, exiting.\n"); + exit(1); + } + debug("SSPI initialized OK\n"); + + atexit(UnloadSecurityDll); + + /* initialize FDescs */ + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + while (1) { + manage_request(); + } + return 0; +} Index: squid/helpers/ntlm_auth/NTLMSSP-WIN32/readme.txt diff -u /dev/null squid/helpers/ntlm_auth/NTLMSSP-WIN32/readme.txt:1.1.8.2 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/helpers/ntlm_auth/NTLMSSP-WIN32/readme.txt Sun Sep 29 09:31:18 2002 @@ -0,0 +1,36 @@ +ntlm_auth.exe + +Native Windows NTLM authenticator for Squid 2.5 + +===== +Usage +===== + +ntlm_auth [-v] [-a UserGroup] [-d UserGroup] [-l] + +-v enables debugging statements. +-l if specified, changes behavior on failures to last-ditch. +-a specify a Windows Local Group name allowed to authenticate. +-d specify a Windows Local Group name not allowed to authenticate. + +This is released under the GNU General Public License + +============== +Allowing Users +============== + +Users that are allowed to access the web proxy must have the Windows NT +User Rights "logon from the network" and must be included in the NT LOCAL User Groups +specified in the Authenticator's command line. +This can be accomplished creating a local user group on the NT machine, grant the privilege, +and adding users to it. + +Refer to Squid documentation for the required changes to squid.conf. + + +=============== +Contact details +=============== + +To contact the maintainer of this package, e-mail on squidnt@serassio.it. +The latest version may be found on http://www.serassio.it/SquidNT.htm. Index: squid/include/sspwin32.h diff -u /dev/null squid/include/sspwin32.h:1.1.6.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/include/sspwin32.h Tue Jul 16 06:23:57 2002 @@ -0,0 +1,45 @@ +/* + * (C) 2002 Guido Serassio + * Based on previous work of Francesco Chemolli, Robert Collins and Andrew Doran + * + * Distributed freely under the terms of the GNU General Public License, + * version 2. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#ifndef _LIBSSPWIN32_H_ +#define _LIBSSPWIN32_H_ +#define SECURITY_WIN32 +#define NTLM_PACKAGE_NAME "NTLM" + +#ifdef _SQUID_CYGWIN_ +#include +#define _T(x) TEXT(x) +#else +#include +#endif +#include +#include +#include + +#define WINNT_SECURITY_DLL "security.dll" +#define WIN9X_SECURITY_DLL "secur32.dll" + +#define SSP_BASIC 1 +#define SSP_NTLM 2 + +HMODULE LoadSecurityDll(int); +void UnloadSecurityDll(void); +BOOL WINAPI SSPLogonUser(PTSTR, PTSTR, PTSTR); + +extern SECURITY_STATUS ss; + +#endif /* LIBSSPWIN32_H_ */ Index: squid/include/util.h diff -u squid/include/util.h:1.11 squid/include/util.h:1.4.20.5 --- squid/include/util.h:1.11 Sat Apr 6 03:34:49 2002 +++ squid/include/util.h Tue Oct 22 12:52:32 2002 @@ -68,7 +68,6 @@ extern char *xstrdup(const char *); extern char *xstrndup(const char *, size_t); extern const char *xstrerror(void); -extern const char *xbstrerror(int); extern int tvSubMsec(struct timeval, struct timeval); extern int tvSubUsec(struct timeval, struct timeval); extern double tvSubDsec(struct timeval, struct timeval); Index: squid/lib/Makefile.am diff -u squid/lib/Makefile.am:1.6 squid/lib/Makefile.am:1.2.6.6 --- squid/lib/Makefile.am:1.6 Wed Oct 2 04:10:43 2002 +++ squid/lib/Makefile.am Wed Oct 2 11:57:19 2002 @@ -27,6 +27,7 @@ @LIBDLMALLOC@ \ libmiscutil.a \ libntlmauth.a \ + libsspwin32.a \ @LIBREGEX@ EXTRA_libmiscutil_a_SOURCES = \ md5.c \ @@ -66,5 +67,7 @@ ntlmauth.c libntlmauth_a_LIBADD = \ @LIBOBJS@ +libsspwin32_a_SOURCES = \ + sspwin32.c -INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include +INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/src -I$(top_builddir)/src Index: squid/lib/sspwin32.c diff -u /dev/null squid/lib/sspwin32.c:1.1.6.2 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/lib/sspwin32.c Sun Sep 29 09:31:18 2002 @@ -0,0 +1,409 @@ +/* + * (C) 2002 Guido Serassio + * Based on previous work of Francesco Chemolli, Robert Collins + * + * Distributed freely under the terms of the GNU General Public License, + * version 2. See the file COPYING for licensing details + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#include "squid.h" +#include "sspwin32.h" + +typedef struct _AUTH_SEQ { + BOOL fInitialized; + BOOL fHaveCredHandle; + BOOL fHaveCtxtHandle; + CredHandle hcred; + struct _SecHandle hctxt; +} AUTH_SEQ, *PAUTH_SEQ; + +static HMODULE hModule; +static int NTLM_mode = SSP_BASIC; +SECURITY_STATUS ss = SEC_E_OK; + +/* Function pointers */ +ACCEPT_SECURITY_CONTEXT_FN _AcceptSecurityContext = NULL; +ACQUIRE_CREDENTIALS_HANDLE_FN _AcquireCredentialsHandle = NULL; +COMPLETE_AUTH_TOKEN_FN _CompleteAuthToken = NULL; +DELETE_SECURITY_CONTEXT_FN _DeleteSecurityContext = NULL; +FREE_CONTEXT_BUFFER_FN _FreeContextBuffer = NULL; +FREE_CREDENTIALS_HANDLE_FN _FreeCredentialsHandle = NULL; +INITIALIZE_SECURITY_CONTEXT_FN _InitializeSecurityContext = NULL; +QUERY_SECURITY_PACKAGE_INFO_FN _QuerySecurityPackageInfo = NULL; + + +void UnloadSecurityDll(void) +{ + if (hModule) + FreeLibrary(hModule); + _AcceptSecurityContext = NULL; + _AcquireCredentialsHandle = NULL; + _CompleteAuthToken = NULL; + _DeleteSecurityContext = NULL; + _FreeContextBuffer = NULL; + _FreeCredentialsHandle = NULL; + _InitializeSecurityContext = NULL; + _QuerySecurityPackageInfo = NULL; + hModule = NULL; +} + + +HMODULE LoadSecurityDll(int mode) +{ + TCHAR lpszDLL[MAX_PATH]; + OSVERSIONINFO VerInfo; +/* + * Find out which security DLL to use, depending on + * whether we are on NT or Win95 or 2000 or XP or .NET Server + * We have to use security.dll on Windows NT 4.0. + * All other operating systems, we have to use Secur32.dll + */ + hModule = NULL; + if ((mode != SSP_BASIC) && (mode != SSP_NTLM)) + return hModule; + NTLM_mode = mode; + VerInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + if (!GetVersionEx (&VerInfo)) { /* If this fails, something has gone wrong */ + return hModule; + } + if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && + VerInfo.dwMajorVersion == 4 && + VerInfo.dwMinorVersion == 0) + { + lstrcpy (lpszDLL, _T(WINNT_SECURITY_DLL)); + } else { + lstrcpy (lpszDLL, _T(WIN9X_SECURITY_DLL)); + } + hModule = LoadLibrary(lpszDLL); + if (!hModule) + return hModule; + _AcceptSecurityContext = (ACCEPT_SECURITY_CONTEXT_FN) + GetProcAddress(hModule, "AcceptSecurityContext"); + if (!_AcceptSecurityContext) { + UnloadSecurityDll(); + hModule = NULL; + return hModule; + } +#ifdef UNICODE + _AcquireCredentialsHandle = (ACQUIRE_CREDENTIALS_HANDLE_FN) + GetProcAddress(hModule, "AcquireCredentialsHandleW"); +#else + _AcquireCredentialsHandle = (ACQUIRE_CREDENTIALS_HANDLE_FN) + GetProcAddress(hModule, "AcquireCredentialsHandleA"); +#endif + if (!_AcquireCredentialsHandle) { + UnloadSecurityDll(); + hModule = NULL; + return hModule; + } +/* CompleteAuthToken is not present on Windows 9x Secur32.dll + * Do not check for the availablity of the function if it is NULL + */ + _CompleteAuthToken = (COMPLETE_AUTH_TOKEN_FN) + GetProcAddress(hModule, "CompleteAuthToken"); + _DeleteSecurityContext = (DELETE_SECURITY_CONTEXT_FN) + GetProcAddress(hModule, "DeleteSecurityContext"); + if (!_DeleteSecurityContext) { + UnloadSecurityDll(); + hModule = NULL; + return hModule; + } + _FreeContextBuffer = (FREE_CONTEXT_BUFFER_FN) + GetProcAddress(hModule, "FreeContextBuffer"); + if (!_FreeContextBuffer) { + UnloadSecurityDll(); + hModule = NULL; + return hModule; + } + _FreeCredentialsHandle = (FREE_CREDENTIALS_HANDLE_FN) + GetProcAddress(hModule, "FreeCredentialsHandle"); + if (!_FreeCredentialsHandle) { + UnloadSecurityDll(); + hModule = NULL; + return hModule; + } +#ifdef UNICODE + _InitializeSecurityContext = (INITIALIZE_SECURITY_CONTEXT_FN) + GetProcAddress(hModule, "InitializeSecurityContextW"); +#else + _InitializeSecurityContext = (INITIALIZE_SECURITY_CONTEXT_FN) + GetProcAddress(hModule, "InitializeSecurityContextA"); +#endif + if (!_InitializeSecurityContext) { + UnloadSecurityDll(); + hModule = NULL; + return hModule; + } +#ifdef UNICODE + _QuerySecurityPackageInfo = (QUERY_SECURITY_PACKAGE_INFO_FN) + GetProcAddress(hModule, "QuerySecurityPackageInfoW"); +#else + _QuerySecurityPackageInfo = (QUERY_SECURITY_PACKAGE_INFO_FN) + GetProcAddress(hModule, "QuerySecurityPackageInfoA"); +#endif + if (!_QuerySecurityPackageInfo) { + UnloadSecurityDll(); + hModule = NULL; + } + return hModule; +} + + +BOOL GenClientContext(PAUTH_SEQ pAS, PSEC_WINNT_AUTH_IDENTITY pAuthIdentity, + PVOID pIn, DWORD cbIn, PVOID pOut, PDWORD pcbOut, PBOOL pfDone) +{ +/* + * Routine Description: + * + * Optionally takes an input buffer coming from the server and returns + * a buffer of information to send back to the server. Also returns + * an indication of whether or not the context is complete. + * + * Return Value: + * Returns TRUE if successful; otherwise FALSE. + */ + TimeStamp tsExpiry; + SecBufferDesc sbdOut; + SecBuffer sbOut; + SecBufferDesc sbdIn; + SecBuffer sbIn; + ULONG fContextAttr; + + if (!pAS->fInitialized) { + ss = _AcquireCredentialsHandle(NULL, _T(NTLM_PACKAGE_NAME), + SECPKG_CRED_OUTBOUND, NULL, (NTLM_mode == SSP_NTLM) ? NULL : pAuthIdentity, NULL, NULL, + &pAS->hcred, &tsExpiry); + if (ss < 0) { + fprintf(stderr, "AcquireCredentialsHandle failed with %08lX\n", ss); + return FALSE; + } + pAS->fHaveCredHandle = TRUE; + } + + /* Prepare output buffer */ + sbdOut.ulVersion = 0; + sbdOut.cBuffers = 1; + sbdOut.pBuffers = &sbOut; + sbOut.cbBuffer = *pcbOut; + sbOut.BufferType = SECBUFFER_TOKEN; + sbOut.pvBuffer = pOut; + + /* Prepare input buffer */ + if (pAS->fInitialized) { + sbdIn.ulVersion = 0; + sbdIn.cBuffers = 1; + sbdIn.pBuffers = &sbIn; + sbIn.cbBuffer = cbIn; + sbIn.BufferType = SECBUFFER_TOKEN; + sbIn.pvBuffer = pIn; + } + ss = _InitializeSecurityContext(&pAS->hcred, + pAS->fInitialized ? &pAS->hctxt : NULL, NULL, 0, 0, + SECURITY_NATIVE_DREP, pAS->fInitialized ? &sbdIn : NULL, + 0, &pAS->hctxt, &sbdOut, &fContextAttr, &tsExpiry); + if (ss < 0) { + // + fprintf(stderr, "InitializeSecurityContext failed with %08lX\n", ss); + return FALSE; + } + pAS->fHaveCtxtHandle = TRUE; + + /* If necessary, complete token */ + if (ss == SEC_I_COMPLETE_NEEDED || ss == SEC_I_COMPLETE_AND_CONTINUE) { + if (_CompleteAuthToken) { + ss = _CompleteAuthToken(&pAS->hctxt, &sbdOut); + if (ss < 0) { + fprintf(stderr, "CompleteAuthToken failed with %08lX\n", ss); + return FALSE; + } + } else { + fprintf (stderr, "CompleteAuthToken not supported.\n"); + return FALSE; + } + } + *pcbOut = sbOut.cbBuffer; + if (!pAS->fInitialized) + pAS->fInitialized = TRUE; + *pfDone = !(ss == SEC_I_CONTINUE_NEEDED + || ss == SEC_I_COMPLETE_AND_CONTINUE ); + return TRUE; +} + + +BOOL GenServerContext(PAUTH_SEQ pAS, PVOID pIn, DWORD cbIn, PVOID pOut, + PDWORD pcbOut, PBOOL pfDone) +{ +/* + * Routine Description: + * + * Takes an input buffer coming from the client and returns a buffer + * to be sent to the client. Also returns an indication of whether or + * not the context is complete. + * + * Return Value: + * + * Returns TRUE if successful; otherwise FALSE. + */ + + TimeStamp tsExpiry; + SecBufferDesc sbdOut; + SecBuffer sbOut; + SecBufferDesc sbdIn; + SecBuffer sbIn; + ULONG fContextAttr; + + if (!pAS->fInitialized) { + ss = _AcquireCredentialsHandle(NULL, _T("NTLM"), + SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &pAS->hcred, + &tsExpiry); + if (ss < 0) { + fprintf(stderr, "AcquireCredentialsHandle failed with %08lX\n", ss); + return FALSE; + } + pAS->fHaveCredHandle = TRUE; + } + + /* Prepare output buffer */ + sbdOut.ulVersion = 0; + sbdOut.cBuffers = 1; + sbdOut.pBuffers = &sbOut; + sbOut.cbBuffer = *pcbOut; + sbOut.BufferType = SECBUFFER_TOKEN; + sbOut.pvBuffer = pOut; + + /* Prepare input buffer */ + sbdIn.ulVersion = 0; + sbdIn.cBuffers = 1; + sbdIn.pBuffers = &sbIn; + sbIn.cbBuffer = cbIn; + sbIn.BufferType = SECBUFFER_TOKEN; + sbIn.pvBuffer = pIn; + ss = _AcceptSecurityContext(&pAS->hcred, + pAS->fInitialized ? &pAS->hctxt : NULL, &sbdIn, (NTLM_mode == SSP_NTLM) ? ASC_REQ_DELEGATE : 0, + SECURITY_NATIVE_DREP, &pAS->hctxt, &sbdOut, &fContextAttr, + &tsExpiry); + if (ss < 0) { + fprintf(stderr, "AcceptSecurityContext failed with %08lX\n", ss); + return FALSE; + } + pAS->fHaveCtxtHandle = TRUE; + + /* If necessary, complete token */ + if (ss == SEC_I_COMPLETE_NEEDED || ss == SEC_I_COMPLETE_AND_CONTINUE) { + if (_CompleteAuthToken) { + ss = _CompleteAuthToken(&pAS->hctxt, &sbdOut); + if (ss < 0) { + fprintf(stderr, "CompleteAuthToken failed with %08lX\n", ss); + return FALSE; + } + } else { + fprintf (stderr, "CompleteAuthToken not supported.\n"); + return FALSE; + } + } + *pcbOut = sbOut.cbBuffer; + if (!pAS->fInitialized) + pAS->fInitialized = TRUE; + *pfDone = !(ss = SEC_I_CONTINUE_NEEDED + || ss == SEC_I_COMPLETE_AND_CONTINUE); + return TRUE; +} + + +BOOL WINAPI SSPLogonUser(PTSTR szUser, PTSTR szPassword, PTSTR szDomain) +{ + AUTH_SEQ asServer = {0}; + AUTH_SEQ asClient = {0}; + BOOL fDone = FALSE; + BOOL fResult = FALSE; + DWORD cbOut = 0; + DWORD cbIn = 0; + DWORD cbMaxToken = 0; + PVOID pClientBuf = NULL; + PVOID pServerBuf = NULL; + PSecPkgInfo pSPI = NULL; + + SEC_WINNT_AUTH_IDENTITY ai; + + do { + if (!hModule) + break; + /* Get max token size */ + _QuerySecurityPackageInfo(_T("NTLM"), &pSPI); + cbMaxToken = pSPI->cbMaxToken; + _FreeContextBuffer(pSPI); + + /* Allocate buffers for client and server messages */ + pClientBuf = xcalloc(cbMaxToken, sizeof(char)); + pServerBuf = xcalloc(cbMaxToken, sizeof(char)); + + /* Initialize auth identity structure */ + ZeroMemory(&ai, sizeof(ai)); + ai.Domain = szDomain; + ai.DomainLength = lstrlen(szDomain); + ai.User = szUser; + ai.UserLength = lstrlen(szUser); + ai.Password = szPassword; + ai.PasswordLength = lstrlen(szPassword); +#if defined(UNICODE) || defined(_UNICODE) + ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; +#else + ai.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; +#endif + + /* Prepare client message (negotiate) */ + cbOut = cbMaxToken; + if (!GenClientContext(&asClient, &ai, NULL, 0, pClientBuf, &cbOut, &fDone)) + break; + + /* Prepare server message (challenge) */ + cbIn = cbOut; + cbOut = cbMaxToken; + if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut, + &fDone)) + break; +/* Most likely failure: AcceptServerContext fails with SEC_E_LOGON_DENIED + * in the case of bad szUser or szPassword. + * Unexpected Result: Logon will succeed if you pass in a bad szUser and + * the guest account is enabled in the specified domain. + */ + + /* Prepare client message (authenticate) */ + cbIn = cbOut; + cbOut = cbMaxToken; + if (!GenClientContext(&asClient, &ai, pServerBuf, cbIn, pClientBuf, &cbOut, + &fDone)) + break; + + /* Prepare server message (authentication) */ + cbIn = cbOut; + cbOut = cbMaxToken; + if (!GenServerContext(&asServer, pClientBuf, cbIn, pServerBuf, &cbOut, + &fDone)) + break; + fResult = TRUE; + } while(0); + + /* Clean up resources */ + if (asClient.fHaveCtxtHandle) + _DeleteSecurityContext(&asClient.hctxt); + if (asClient.fHaveCredHandle) + _FreeCredentialsHandle(&asClient.hcred); + if (asServer.fHaveCtxtHandle) + _DeleteSecurityContext(&asServer.hctxt); + if (asServer.fHaveCredHandle) + _FreeCredentialsHandle(&asServer.hcred); + xfree(pClientBuf); + xfree(pServerBuf); + + return fResult; +} Index: squid/src/Makefile.am diff -u squid/src/Makefile.am:1.31 squid/src/Makefile.am:1.2.6.23 --- squid/src/Makefile.am:1.31 Sun Nov 10 14:41:02 2002 +++ squid/src/Makefile.am Mon Nov 11 13:21:25 2002 @@ -68,7 +68,7 @@ WIN32SOURCE = endif -AM_CFLAGS = -Werror -Wall +AM_CFLAGS = -Wall SUBDIRS = fs repl auth Index: squid/src/cf.data.pre diff -u squid/src/cf.data.pre:1.91 squid/src/cf.data.pre:1.10.2.46 --- squid/src/cf.data.pre:1.91 Wed Feb 5 19:18:14 2003 +++ squid/src/cf.data.pre Wed Feb 12 01:56:28 2003 @@ -822,6 +822,16 @@ see argument descriptions under ufs above + The awin32 store type: + + "awin32" uses the same storage format as "ufs", utilizing + WIN32-threads to avoid blocking the main Squid process on + disk-I/O. This was formerly known in Squid as async-io. + + cache_dir awin32 Directory-Name Mbytes L1 L2 [options] + + see argument descriptions under ufs above + The diskd store type: "diskd" uses the same storage format as "ufs", utilizing a @@ -1910,7 +1920,7 @@ DEFAULT: 10 seconds DOC_START Maximum time to wait for IDENT lookups to complete. - + If this is too high, and you enabled IDENT lookups from untrusted users, then you might be susceptible to denial-of-service by having many ident requests going at once. Index: squid/src/client_side_request.c diff -u squid/src/client_side_request.c:1.6 squid/src/client_side_request.c:1.3.2.3 --- squid/src/client_side_request.c:1.6 Thu Oct 3 05:55:28 2002 +++ squid/src/client_side_request.c Sun Oct 20 06:46:10 2002 @@ -1,10 +1,10 @@ /* * $Id$ - * - * DEBUG: section 85 Client-side Request Routines AUTHOR: Robert Collins - * (Originally Duane Wessels in client_side.c) - * + * + * DEBUG: section 85 Client-side Request Routines + * AUTHOR: Robert Collins (Originally Duane Wessels in client_side.c) + * * SQUID Web Proxy Cache http://www.squid-cache.org/ * ---------------------------------------------------------- * Index: squid/src/defines.h diff -u squid/src/defines.h:1.25 squid/src/defines.h:1.3.22.26 --- squid/src/defines.h:1.25 Sun Sep 15 04:06:31 2002 +++ squid/src/defines.h Mon Sep 16 00:25:32 2002 @@ -313,4 +313,5 @@ #endif #define HTTP_REQBUF_SZ 4096 + #endif /* SQUID_DEFINES_H */ Index: squid/src/pinger.c diff -u squid/src/pinger.c:1.5 squid/src/pinger.c:1.3.28.3 --- squid/src/pinger.c:1.5 Sat Oct 5 19:33:41 2002 +++ squid/src/pinger.c Sun Oct 20 06:46:13 2002 @@ -37,19 +37,92 @@ #if USE_ICMP +/* Native Windows port doesn't have netinet support, so we emulate it. + At this time, Cygwin lacks icmp support in its include files, so we need + to use the native Windows port definitions. + */ + +#if !defined(_SQUID_MSWIN_) && !defined(_SQUID_CYGWIN_) + #include #include #include #include +#define PINGER_TIMEOUT 10 + +static int socket_from_squid = 0; +static int socket_to_squid = 1; + +#else /* _SQUID_MSWIN_ or _SQUID_CYGWIN_ */ + +#ifdef _SQUID_MSWIN_ + +#include +#include + +#define PINGER_TIMEOUT 5 + +static SOCKET socket_to_squid = -1; +#define socket_from_squid socket_to_squid + +#else /* _SQUID_CYGWIN_ */ + +#include +#include +#include +#include + +#define PINGER_TIMEOUT 10 + +static int socket_from_squid = 0; +static int socket_to_squid = 1; + +#endif + +#define ICMP_ECHO 8 +#define ICMP_ECHOREPLY 0 + +typedef struct iphdr +{ + u_int8_t ip_vhl:4; /* Length of the header in dwords */ + u_int8_t version:4; /* Version of IP */ + u_int8_t tos; /* Type of service */ + u_int16_t total_len; /* Length of the packet in dwords */ + u_int16_t ident; /* unique identifier */ + u_int16_t flags; /* Flags */ + u_int8_t ip_ttl; /* Time to live */ + u_int8_t proto; /* Protocol number (TCP, UDP etc) */ + u_int16_t checksum; /* IP checksum */ + u_int32_t source_ip; + u_int32_t dest_ip; +} +iphdr; + +/* ICMP header */ +typedef struct icmphdr +{ + u_int8_t icmp_type; /* ICMP packet type */ + u_int8_t icmp_code; /* Type sub code */ + u_int16_t icmp_cksum; + u_int16_t icmp_id; + u_int16_t icmp_seq; + u_int32_t timestamp; /* not part of ICMP, but we need it */ +} +icmphdr; + +#endif /* _SQUID_MSWIN_ */ + #ifndef _SQUID_LINUX_ #ifndef _SQUID_CYGWIN_ +#ifndef _SQUID_MSWIN_ #define icmphdr icmp #define iphdr ip #endif #endif +#endif -#if defined (_SQUID_LINUX_) || defined (_SQUID_CYGWIN_) +#if defined (_SQUID_LINUX_) #ifdef icmp_id #undef icmp_id #endif @@ -118,11 +191,45 @@ static void pingerLog(struct icmphdr *, struct in_addr, int, int); static int ipHops(int ttl); static void pingerSendtoSquid(pingerReplyData * preply); +static void pingerOpen(void); +static void pingerClose(void); void pingerOpen(void) { struct protoent *proto = NULL; +#ifdef _SQUID_MSWIN_ + WSADATA wsaData; + WSAPROTOCOL_INFO wpi; + char buf[sizeof(wpi)]; + int x; + struct sockaddr_in PS; + + WSAStartup(2, &wsaData); + + getCurrentTime(); + _db_init(NULL, "ALL,1"); + setmode(0, O_BINARY); + setmode(1, O_BINARY); + x = read(0, buf, sizeof(wpi)); + if (x < sizeof(wpi)) { + getCurrentTime(); + debug(42, 0) ("pingerOpen: read: FD 0: %s\n", xstrerror()); + write(1, "ERR\n", 4); + exit(1); + } + xmemcpy(&wpi, buf, sizeof(wpi)); + + write(1, "OK\n", 3); + x = read(0, buf, sizeof(PS)); + if (x < sizeof(PS)) { + getCurrentTime(); + debug(42, 0) ("pingerOpen: read: FD 0: %s\n", xstrerror()); + write(1, "ERR\n", 4); + exit(1); + } + xmemcpy(&PS, buf, sizeof(PS)); +#endif if ((proto = getprotobyname("icmp")) == 0) { debug(42, 0) ("pingerOpen: unknown protocol: icmp\n"); exit(1); @@ -134,12 +241,50 @@ } icmp_ident = getpid() & 0xffff; debug(42, 0) ("pinger: ICMP socket opened\n"); +#ifdef _SQUID_MSWIN_ + socket_to_squid = + WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, + &wpi, 0, 0); + if (socket_to_squid == INVALID_SOCKET) { + getCurrentTime(); + debug(42, 0) ("pingerOpen: WSASocket: %s\n", xstrerror()); + write(1, "ERR\n", 4); + exit(1); + } + x = connect(socket_to_squid, (struct sockaddr *) &PS, sizeof(PS)); + if (SOCKET_ERROR == x) { + getCurrentTime(); + debug(42, 0) ("pingerOpen: connect: %s\n", xstrerror()); + write(1, "ERR\n", 4); + exit(1); + } + write(1, "OK\n", 3); + memset(buf, 0, sizeof(buf)); + x = recv(socket_to_squid, buf, sizeof(buf), 0); + if (x < 3) { + debug(42, 0) ("icmpOpen: recv: %s\n", xstrerror()); + exit(1); + } + x = send(socket_to_squid, buf, strlen(buf), 0); + if (x < 3 || strncmp("OK\n", buf, 3)) { + debug(42, 0) ("icmpOpen: recv: %s\n", xstrerror()); + exit(1); + } + + getCurrentTime(); + debug(42, 0) ("pinger: Squid socket opened\n"); +#endif } void pingerClose(void) { close(icmp_sock); +#ifdef _SQUID_MSWIN_ + shutdown(socket_to_squid, SD_BOTH); + close(socket_to_squid); + socket_to_squid = -1; +#endif icmp_sock = -1; icmp_ident = 0; } @@ -267,7 +412,7 @@ } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); - answer = ~sum; + answer = (unsigned short) ~sum; return (answer); } @@ -307,7 +452,7 @@ int n; int guess_size; memset(&pecho, '\0', sizeof(pecho)); - n = recv(0, (char *) &pecho, sizeof(pecho), 0); + n = recv(socket_from_squid, (char *) &pecho, sizeof(pecho), 0); if (n < 0) return n; if (0 == n) { @@ -334,8 +479,9 @@ pingerSendtoSquid(pingerReplyData * preply) { int len = sizeof(pingerReplyData) - MAX_PKT_SZ + preply->psize; - if (send(1, (char *) preply, len, 0) < 0) { + if (send(socket_to_squid, (char *) preply, len, 0) < 0) { debug(50, 0) ("pinger: send: %s\n", xstrerror()); + pingerClose(); exit(1); } } @@ -376,25 +522,30 @@ _db_init(NULL, debug_args); for (;;) { - tv.tv_sec = 10; + tv.tv_sec = PINGER_TIMEOUT; tv.tv_usec = 0; FD_ZERO(&R); - FD_SET(0, &R); + FD_SET(socket_from_squid, &R); FD_SET(icmp_sock, &R); x = select(icmp_sock + 1, &R, NULL, NULL, &tv); getCurrentTime(); - if (x < 0) + if (x < 0) { + pingerClose(); exit(1); - if (FD_ISSET(0, &R)) + } + if (FD_ISSET(socket_from_squid, &R)) if (pingerReadRequest() < 0) { debug(42, 0) ("Pinger exiting.\n"); + pingerClose(); exit(1); } if (FD_ISSET(icmp_sock, &R)) pingerRecv(); - if (10 + last_check_time < squid_curtime) { - if (send(1, (char *) &tv, 0, 0) < 0) + if (PINGER_TIMEOUT + last_check_time < squid_curtime) { + if (send(socket_to_squid, (char *) &tv, 0, 0) < 0) { + pingerClose(); exit(1); + } last_check_time = squid_curtime; } } Index: squid/src/ufscommon.c diff -u squid/src/ufscommon.c:1.3 squid/src/ufscommon.c:1.2.12.3 --- squid/src/ufscommon.c:1.3 Thu Jan 9 03:34:05 2003 +++ squid/src/ufscommon.c Thu Jan 9 12:55:37 2003 @@ -1281,6 +1281,8 @@ int commonUfsDirIs(SwapDir * sd) { + if (strncmp(sd->type, "awin32", 6) == 0) + return 1; if (strncmp(sd->type, "aufs", 4) == 0) return 1; if (strncmp(sd->type, "diskd", 5) == 0) Index: squid/src/win32.c diff -u squid/src/win32.c:1.6 squid/src/win32.c:1.1.50.19 --- squid/src/win32.c:1.6 Wed Jun 26 10:28:32 2002 +++ squid/src/win32.c Tue Jul 16 03:04:20 2002 @@ -22,6 +22,9 @@ * */ +#ifndef WIN32_C +#define WIN32_C + #include "squid.h" /* This code compiles only CygWin & Windows NT Port */ @@ -34,6 +37,7 @@ /* LOCAL FUNCTIONS */ /* ====================================================================== */ + static unsigned int GetOSVersion() { @@ -108,3 +112,6 @@ return 0; } #endif + +#endif /* WIN32_C */ + Index: squid/src/fs/Makefile.am diff -u squid/src/fs/Makefile.am:1.3 squid/src/fs/Makefile.am:1.2.6.3 --- squid/src/fs/Makefile.am:1.3 Mon May 20 23:58:31 2002 +++ squid/src/fs/Makefile.am Tue May 21 02:26:38 2002 @@ -5,14 +5,16 @@ AUTOMAKE_OPTIONS = subdir-objects -DIST_SUBDIRS = aufs coss diskd null ufs +DIST_SUBDIRS = aufs awin32 coss diskd null ufs SUBDIRS = @STORE_MODULE_SUBDIRS@ -EXTRA_LIBRARIES = libaufs.a libcoss.a libdiskd.a libnull.a libufs.a +EXTRA_LIBRARIES = libaufs.a libawin32.a libcoss.a libdiskd.a libnull.a libufs.a noinst_LIBRARIES = @STORE_LIBS@ libaufs_a_SOURCES = aufs/aiops.c aufs/async_io.c aufs/store_asyncufs.h \ aufs/store_dir_aufs.c aufs/store_io_aufs.c +libawin32_a_SOURCES = awin32/aiops.c awin32/async_io.c awin32/store_asyncufs.h \ + awin32/store_dir_aufs.c awin32/store_io_aufs.c libcoss_a_SOURCES = coss/store_coss.h coss/store_io_coss.c coss/store_dir_coss.c \ coss/async_io.c coss/async_io.h libdiskd_a_SOURCES = diskd/diskd.c diskd/store_dir_diskd.c diskd/store_diskd.h \ @@ -29,6 +31,8 @@ ## targets below to emulate distributed makefiles aufs/all: libaufs.a aufs/clean: clean +awin32/all: libawin32.a +awin32/clean: clean coss/all: libcoss.a coss/clean: clean null/all: libnull.a Index: squid/src/fs/awin32/.cvsignore diff -u /dev/null squid/src/fs/awin32/.cvsignore:1.1.2.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/fs/awin32/.cvsignore Fri Oct 19 12:24:13 2001 @@ -0,0 +1,2 @@ +.cvsignore +Makefile.in Index: squid/src/fs/awin32/Makefile.am diff -u /dev/null squid/src/fs/awin32/Makefile.am:1.1.2.1 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/fs/awin32/Makefile.am Fri Oct 19 12:24:13 2001 @@ -0,0 +1,2 @@ +all clean: + @cd .. && $(MAKE) $(MFLAGS) awin32/$@ Index: squid/src/fs/awin32/aiops.c diff -u /dev/null squid/src/fs/awin32/aiops.c:1.1.2.7 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/fs/awin32/aiops.c Tue Oct 22 12:52:34 2002 @@ -0,0 +1,1012 @@ +/* + * $Id$ + * + * DEBUG: section 43 AIOPS + * AUTHOR: Stewart Forster + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include +#include "store_asyncufs.h" + +#include +#include +#include +#include +#include +#include +#include + +#define RIDICULOUS_LENGTH 4096 + +enum _squidaio_thread_status { + _THREAD_STARTING = 0, + _THREAD_WAITING, + _THREAD_BUSY, + _THREAD_FAILED, + _THREAD_DONE +}; +typedef enum _squidaio_thread_status squidaio_thread_status; + +typedef struct squidaio_request_t { + struct squidaio_request_t *next; + squidaio_request_type request_type; + int cancelled; + char *path; + int oflag; + mode_t mode; + int fd; + char *bufferp; + char *tmpbufp; + int buflen; + off_t offset; + int whence; + int ret; + int err; + struct stat *tmpstatp; + struct stat *statp; + squidaio_result_t *resultp; +} squidaio_request_t; + +typedef struct squidaio_request_queue_t { + HANDLE mutex; + HANDLE cond; /* See Event objects */ + squidaio_request_t *volatile head; + squidaio_request_t *volatile *volatile tailp; + unsigned long requests; + unsigned long blocked; /* main failed to lock the queue */ +} squidaio_request_queue_t; + +typedef struct squidaio_thread_t squidaio_thread_t; +struct squidaio_thread_t { + squidaio_thread_t *next; + HANDLE thread; + DWORD dwThreadId; /* thread ID */ + squidaio_thread_status status; + struct squidaio_request_t *current_req; + unsigned long requests; + int volatile exit; +}; + +static void squidaio_init(void); +static void squidaio_queue_request(squidaio_request_t *); +static void squidaio_cleanup_request(squidaio_request_t *); +static DWORD WINAPI squidaio_thread_loop( LPVOID lpParam ); +static void squidaio_do_open(squidaio_request_t *); +static void squidaio_do_read(squidaio_request_t *); +static void squidaio_do_write(squidaio_request_t *); +static void squidaio_do_close(squidaio_request_t *); +static void squidaio_do_stat(squidaio_request_t *); +static void squidaio_do_unlink(squidaio_request_t *); +static void squidaio_do_truncate(squidaio_request_t *); +#if AIO_OPENDIR +static void *squidaio_do_opendir(squidaio_request_t *); +#endif +static void squidaio_debug(squidaio_request_t *); +static void squidaio_poll_queues(void); + +static squidaio_thread_t *threads = NULL; +static int squidaio_initialised = 0; + + +#define AIO_LARGE_BUFS 16384 +#define AIO_MEDIUM_BUFS AIO_LARGE_BUFS >> 1 +#define AIO_SMALL_BUFS AIO_LARGE_BUFS >> 2 +#define AIO_TINY_BUFS AIO_LARGE_BUFS >> 3 +#define AIO_MICRO_BUFS 128 + +static MemPool *squidaio_large_bufs = NULL; /* 16K */ +static MemPool *squidaio_medium_bufs = NULL; /* 8K */ +static MemPool *squidaio_small_bufs = NULL; /* 4K */ +static MemPool *squidaio_tiny_bufs = NULL; /* 2K */ +static MemPool *squidaio_micro_bufs = NULL; /* 128K */ + +static int request_queue_len = 0; +static MemPool *squidaio_request_pool = NULL; +static MemPool *squidaio_thread_pool = NULL; +static squidaio_request_queue_t request_queue; +static struct { + squidaio_request_t *head, **tailp; +} request_queue2 = { + + NULL, &request_queue2.head +}; +static squidaio_request_queue_t done_queue; +static struct { + squidaio_request_t *head, **tailp; +} done_requests = { + + NULL, &done_requests.head +}; + +static HANDLE main_thread; + +static MemPool * +squidaio_get_pool(int size) +{ + MemPool *p; + if (size <= AIO_LARGE_BUFS) { + if (size <= AIO_MICRO_BUFS) + p = squidaio_micro_bufs; + else if (size <= AIO_TINY_BUFS) + p = squidaio_tiny_bufs; + else if (size <= AIO_SMALL_BUFS) + p = squidaio_small_bufs; + else if (size <= AIO_MEDIUM_BUFS) + p = squidaio_medium_bufs; + else + p = squidaio_large_bufs; + } else + p = NULL; + return p; +} + +static void * +squidaio_xmalloc(int size) +{ + void *p; + MemPool *pool; + + if ((pool = squidaio_get_pool(size)) != NULL) { + p = memPoolAlloc(pool); + } else + p = xmalloc(size); + + return p; +} + +static char * +squidaio_xstrdup(const char *str) +{ + char *p; + int len = strlen(str) + 1; + + p = squidaio_xmalloc(len); + strncpy(p, str, len); + + return p; +} + +static void +squidaio_xfree(void *p, int size) +{ + MemPool *pool; + + if ((pool = squidaio_get_pool(size)) != NULL) { + memPoolFree(pool, p); + } else + xfree(p); +} + +static void +squidaio_xstrfree(char *str) +{ + MemPool *pool; + int len = strlen(str) + 1; + + if ((pool = squidaio_get_pool(len)) != NULL) { + memPoolFree(pool, str); + } else + xfree(str); +} + +static void +squidaio_init(void) +{ + int i; + squidaio_thread_t *threadp; + + if (squidaio_initialised) + return; + + if (!DuplicateHandle(GetCurrentProcess(), /* pseudo handle, don't close */ + GetCurrentThread(), /* pseudo handle to copy */ + GetCurrentProcess(),/* pseudo handle, don't close */ + &main_thread, + 0, /* required access */ + FALSE, /* child process's don't inherit the handle */ + DUPLICATE_SAME_ACCESS)) { + /* spit errors */ + fatal("couldn't get current thread handle\n"); + } + /* Initialize request queue */ + if ((request_queue.mutex = CreateMutex(NULL, /* no inheritance */ + FALSE, /* start unowned (as per mutex_init) */ + NULL) /* no name */ + ) == NULL) { + fatal("failed to create mutex\n"); + } + if ((request_queue.cond = CreateEvent(NULL, /* no inheritance */ + FALSE, /* auto signal reset - which I think is pthreads like ? */ + FALSE, /* start non signaled */ + NULL) /* no name */ + ) == NULL) { + fatal("failed to create condition event variable.\n"); + } + request_queue.head = NULL; + request_queue.tailp = &request_queue.head; + request_queue.requests = 0; + request_queue.blocked = 0; + + /* Initialize done queue */ + if ((done_queue.mutex = CreateMutex(NULL, /* no inheritance */ + FALSE, /* start unowned (as per mutex_init) */ + NULL) /* no name */ + ) == NULL) { + fatal("failed to create mutex\n"); + } + if ((done_queue.cond = CreateEvent(NULL, /* no inheritance */ + TRUE, /* manually signaled - which I think is pthreads like ? */ + FALSE, /* start non signaled */ + NULL) /* no name */ + ) == NULL) { + fatal("failed to create condition event variable.\n"); + } + done_queue.head = NULL; + done_queue.tailp = &done_queue.head; + done_queue.requests = 0; + done_queue.blocked = 0; + + /* Create threads and get them to sit in their wait loop */ + squidaio_thread_pool = memPoolCreate("aio_thread", sizeof(squidaio_thread_t)); + assert(NUMTHREADS); + for (i = 0; i < NUMTHREADS; i++) { + threadp = memPoolAlloc(squidaio_thread_pool); + threadp->status = _THREAD_STARTING; + threadp->current_req = NULL; + threadp->requests = 0; + threadp->next = threads; + threads = threadp; + if ((threadp->thread = CreateThread(NULL, /* no security attributes */ + 0, /* use default stack size */ + squidaio_thread_loop, /* thread function */ + threadp, /* argument to thread function */ + 0, /* use default creation flags */ + &(threadp->dwThreadId)) /* returns the thread identifier */ + ) == NULL) { + fprintf(stderr, "Thread creation failed\n"); + threadp->status = _THREAD_FAILED; + continue; + } + /* Set the new thread priority above parent process */ + SetThreadPriority(threadp->thread,THREAD_PRIORITY_ABOVE_NORMAL); + } + + /* Create request pool */ + squidaio_request_pool = memPoolCreate("aio_request", sizeof(squidaio_request_t)); + squidaio_large_bufs = memPoolCreate("squidaio_large_bufs", AIO_LARGE_BUFS); + squidaio_medium_bufs = memPoolCreate("squidaio_medium_bufs", AIO_MEDIUM_BUFS); + squidaio_small_bufs = memPoolCreate("squidaio_small_bufs", AIO_SMALL_BUFS); + squidaio_tiny_bufs = memPoolCreate("squidaio_tiny_bufs", AIO_TINY_BUFS); + squidaio_micro_bufs = memPoolCreate("squidaio_micro_bufs", AIO_MICRO_BUFS); + + squidaio_initialised = 1; +} + +void +squidaio_shutdown(void) +{ + squidaio_thread_t *threadp; + int i; + HANDLE * hthreads; + + if (!squidaio_initialised) + return; + + hthreads = (HANDLE *) xcalloc (NUMTHREADS, sizeof (HANDLE)); + threadp = threads; + for (i = 0; i < NUMTHREADS; i++) { + threadp->exit = 1; + hthreads[i] = threadp->thread; + threadp = threadp->next; + } + ReleaseMutex(request_queue.mutex); + ResetEvent(request_queue.cond); + ReleaseMutex(done_queue.mutex); + ResetEvent(done_queue.cond); + Sleep(0); + + WaitForMultipleObjects(NUMTHREADS, hthreads, TRUE, 2000); + for (i = 0; i < NUMTHREADS; i++) { + CloseHandle(hthreads[i]); + } + CloseHandle(main_thread); + squidaio_initialised = 0; + xfree(hthreads); +} + +static DWORD WINAPI +squidaio_thread_loop(LPVOID lpParam) +{ + squidaio_thread_t *threadp = lpParam; + squidaio_request_t *request; + HANDLE cond; /* local copy of the event queue because win32 event handles + * don't atomically release the mutex as cond variables do. */ + + /* lock the thread info */ + if (WAIT_FAILED == WaitForSingleObject(request_queue.mutex, INFINITE)) { + fatal("Can't get ownership of mutex\n"); + } + /* duplicate the handle */ + if (!DuplicateHandle(GetCurrentProcess(), /* pseudo handle, don't close */ + request_queue.cond, /* handle to copy */ + GetCurrentProcess(), /* pseudo handle, don't close */ + &cond, + 0, /* required access */ + FALSE, /* child process's don't inherit the handle */ + DUPLICATE_SAME_ACCESS)) + fatal("Can't duplicate mutex handle\n"); + if (!ReleaseMutex(request_queue.mutex)) { + CloseHandle(cond); + fatal("Can't release mutex\n"); + } + Sleep(0); + + while (1) { + DWORD rv; + threadp->current_req = request = NULL; + request = NULL; + /* Get a request to process */ + threadp->status = _THREAD_WAITING; + if (threadp->exit) { + CloseHandle(request_queue.mutex); + CloseHandle(cond); + return 0; + } + rv = WaitForSingleObject(request_queue.mutex, INFINITE); + if (rv == WAIT_FAILED) { + CloseHandle(cond); + return 1; + } + + while (!request_queue.head) { + if (!ReleaseMutex(request_queue.mutex)) { + CloseHandle(cond); + threadp->status = _THREAD_FAILED; + return 1; + } + Sleep(0); + rv = WaitForSingleObject(cond, INFINITE); + if (rv == WAIT_FAILED) { + CloseHandle(cond); + return 1; + } + rv = WaitForSingleObject(request_queue.mutex, INFINITE); + if (rv == WAIT_FAILED) { + CloseHandle(cond); + return 1; + } + } + request = request_queue.head; + if (request) + request_queue.head = request->next; + if (!request_queue.head) + request_queue.tailp = &request_queue.head; + if (!ReleaseMutex(request_queue.mutex)) { + CloseHandle(cond); + return 1; + } + Sleep(0); + /* process the request */ + threadp->status = _THREAD_BUSY; + request->next = NULL; + threadp->current_req = request; + errno = 0; + if (!request->cancelled) { + switch (request->request_type) { + case _AIO_OP_OPEN: + squidaio_do_open(request); + break; + case _AIO_OP_READ: + squidaio_do_read(request); + break; + case _AIO_OP_WRITE: + squidaio_do_write(request); + break; + case _AIO_OP_CLOSE: + squidaio_do_close(request); + break; + case _AIO_OP_UNLINK: + squidaio_do_unlink(request); + break; + case _AIO_OP_TRUNCATE: + squidaio_do_truncate(request); + break; +#if AIO_OPENDIR /* Opendir not implemented yet */ + case _AIO_OP_OPENDIR: + squidaio_do_opendir(request); + break; +#endif + case _AIO_OP_STAT: + squidaio_do_stat(request); + break; + default: + request->ret = -1; + request->err = EINVAL; + break; + } + } else { /* cancelled */ + request->ret = -1; + request->err = EINTR; + } + threadp->status = _THREAD_DONE; + /* put the request in the done queue */ + rv = WaitForSingleObject(done_queue.mutex, INFINITE); + if (rv == WAIT_FAILED) { + CloseHandle(cond); + return 1; + } + *done_queue.tailp = request; + done_queue.tailp = &request->next; + if (!ReleaseMutex(done_queue.mutex)) { + CloseHandle(cond); + return 1; + } + Sleep(0); + threadp->requests++; + } /* while forever */ + CloseHandle(cond); + return 0; +} /* squidaio_thread_loop */ + +static void +squidaio_queue_request(squidaio_request_t * request) +{ + static int high_start = 0; + debug(43, 9) ("squidaio_queue_request: %p type=%d result=%p\n", + request, request->request_type, request->resultp); + /* Mark it as not executed (failing result, no error) */ + request->ret = -1; + request->err = 0; + /* Internal housekeeping */ + request_queue_len += 1; + request->resultp->_data = request; + /* Play some tricks with the request_queue2 queue */ + request->next = NULL; + if (WaitForSingleObject(request_queue.mutex, 0) == WAIT_OBJECT_0) { + if (request_queue2.head) { + /* Grab blocked requests */ + *request_queue.tailp = request_queue2.head; + request_queue.tailp = request_queue2.tailp; + } + /* Enqueue request */ + *request_queue.tailp = request; + request_queue.tailp = &request->next; + if (!SetEvent(request_queue.cond)) + fatal("couldn't push queue\n"); + if (!ReleaseMutex(request_queue.mutex)) { + /* unexpected error */ + fatal("couldn't push queue\n"); + } + Sleep(0); + if (request_queue2.head) { + /* Clear queue of blocked requests */ + request_queue2.head = NULL; + request_queue2.tailp = &request_queue2.head; + } + } else { + /* Oops, the request queue is blocked, use request_queue2 */ + *request_queue2.tailp = request; + request_queue2.tailp = &request->next; + } + if (request_queue2.head) { + static int filter = 0; + static int filter_limit = 8; + if (++filter >= filter_limit) { + filter_limit += filter; + filter = 0; + debug(43, 1) ("squidaio_queue_request: WARNING - Queue congestion\n"); + } + } + /* Warn if out of threads */ + if (request_queue_len > MAGIC1) { + static int last_warn = 0; + static int queue_high, queue_low; + if (high_start == 0) { + high_start = squid_curtime; + queue_high = request_queue_len; + queue_low = request_queue_len; + } + if (request_queue_len > queue_high) + queue_high = request_queue_len; + if (request_queue_len < queue_low) + queue_low = request_queue_len; + if (squid_curtime >= (last_warn + 15) && + squid_curtime >= (high_start + 5)) { + debug(43, 1) ("squidaio_queue_request: WARNING - Disk I/O overloading\n"); + if (squid_curtime >= (high_start + 15)) + debug(43, 1) ("squidaio_queue_request: Queue Length: current=%d, high=%d, low=%d, duration=%ld\n", + request_queue_len, queue_high, queue_low, (long int) (squid_curtime - high_start)); + last_warn = squid_curtime; + } + } else { + high_start = 0; + } + /* Warn if seriously overloaded */ + if (request_queue_len > RIDICULOUS_LENGTH) { + debug(43, 0) ("squidaio_queue_request: Async request queue growing uncontrollably!\n"); + debug(43, 0) ("squidaio_queue_request: Syncing pending I/O operations.. (blocking)\n"); + squidaio_sync(); + debug(43, 0) ("squidaio_queue_request: Synced\n"); + } +} /* squidaio_queue_request */ + +static void +squidaio_cleanup_request(squidaio_request_t * requestp) +{ + squidaio_result_t *resultp = requestp->resultp; + int cancelled = requestp->cancelled; + + /* Free allocated structures and copy data back to user space if the */ + /* request hasn't been cancelled */ + switch (requestp->request_type) { + case _AIO_OP_STAT: + if (!cancelled && requestp->ret == 0) + xmemcpy(requestp->statp, requestp->tmpstatp, sizeof(struct stat)); + squidaio_xfree(requestp->tmpstatp, sizeof(struct stat)); + squidaio_xstrfree(requestp->path); + break; + case _AIO_OP_OPEN: + if (cancelled && requestp->ret >= 0) + /* The open() was cancelled but completed */ + close(requestp->ret); + squidaio_xstrfree(requestp->path); + break; + case _AIO_OP_CLOSE: + if (cancelled && requestp->ret < 0) + /* The close() was cancelled and never got executed */ + close(requestp->fd); + break; + case _AIO_OP_UNLINK: + case _AIO_OP_TRUNCATE: + case _AIO_OP_OPENDIR: + squidaio_xstrfree(requestp->path); + break; + case _AIO_OP_READ: + if (!cancelled && requestp->ret > 0) + xmemcpy(requestp->bufferp, requestp->tmpbufp, requestp->ret); + squidaio_xfree(requestp->tmpbufp, requestp->buflen); + break; + case _AIO_OP_WRITE: + squidaio_xfree(requestp->tmpbufp, requestp->buflen); + break; + default: + break; + } + if (resultp != NULL && !cancelled) { + resultp->aio_return = requestp->ret; + resultp->aio_errno = requestp->err; + } + memPoolFree(squidaio_request_pool, requestp); +} /* squidaio_cleanup_request */ + + +int +squidaio_cancel(squidaio_result_t * resultp) +{ + squidaio_request_t *request = resultp->_data; + + if (request && request->resultp == resultp) { + debug(43, 9) ("squidaio_cancel: %p type=%d result=%p\n", + request, request->request_type, request->resultp); + request->cancelled = 1; + request->resultp = NULL; + resultp->_data = NULL; + resultp->result_type = _AIO_OP_NONE; + return 0; + } + return 1; +} /* squidaio_cancel */ + + +int +squidaio_open(const char *path, int oflag, mode_t mode, squidaio_result_t * resultp) +{ + squidaio_request_t *requestp; + + if (!squidaio_initialised) + squidaio_init(); + requestp = memPoolAlloc(squidaio_request_pool); + requestp->path = (char *) squidaio_xstrdup(path); + requestp->oflag = oflag; + requestp->mode = mode; + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_OPEN; + requestp->cancelled = 0; + resultp->result_type = _AIO_OP_OPEN; + + squidaio_queue_request(requestp); + return 0; +} + + +static void +squidaio_do_open(squidaio_request_t * requestp) +{ + requestp->ret = open(requestp->path, requestp->oflag, requestp->mode); + requestp->err = errno; +} + + +int +squidaio_read(int fd, char *bufp, int bufs, off_t offset, int whence, squidaio_result_t * resultp) +{ + squidaio_request_t *requestp; + + if (!squidaio_initialised) + squidaio_init(); + requestp = memPoolAlloc(squidaio_request_pool); + requestp->fd = fd; + requestp->bufferp = bufp; + requestp->tmpbufp = (char *) squidaio_xmalloc(bufs); + requestp->buflen = bufs; + requestp->offset = offset; + requestp->whence = whence; + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_READ; + requestp->cancelled = 0; + resultp->result_type = _AIO_OP_READ; + + squidaio_queue_request(requestp); + return 0; +} + + +static void +squidaio_do_read(squidaio_request_t * requestp) +{ + lseek(requestp->fd, requestp->offset, requestp->whence); +#ifdef _SQUID_MSWIN_ + if (!ReadFile((HANDLE)_get_osfhandle(requestp->fd), requestp->tmpbufp, + requestp->buflen, (LPDWORD)&requestp->ret, NULL)) { + WIN32_maperror(GetLastError()); + requestp->ret = -1; + } +#else + requestp->ret = read(requestp->fd, requestp->tmpbufp, requestp->buflen); +#endif + requestp->err = errno; +} + + +int +squidaio_write(int fd, char *bufp, int bufs, off_t offset, int whence, squidaio_result_t * resultp) +{ + squidaio_request_t *requestp; + + if (!squidaio_initialised) + squidaio_init(); + requestp = memPoolAlloc(squidaio_request_pool); + requestp->fd = fd; + requestp->tmpbufp = (char *) squidaio_xmalloc(bufs); + xmemcpy(requestp->tmpbufp, bufp, bufs); + requestp->buflen = bufs; + requestp->offset = offset; + requestp->whence = whence; + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_WRITE; + requestp->cancelled = 0; + resultp->result_type = _AIO_OP_WRITE; + + squidaio_queue_request(requestp); + return 0; +} + + +static void +squidaio_do_write(squidaio_request_t * requestp) +{ +#ifdef _SQUID_MSWIN_ + if (!WriteFile((HANDLE)_get_osfhandle(requestp->fd), requestp->tmpbufp, + requestp->buflen, (LPDWORD)&requestp->ret, NULL)) { + WIN32_maperror(GetLastError()); + requestp->ret = -1; + } +#else + requestp->ret = write(requestp->fd, requestp->tmpbufp, requestp->buflen); +#endif + requestp->err = errno; +} + + +int +squidaio_close(int fd, squidaio_result_t * resultp) +{ + squidaio_request_t *requestp; + + if (!squidaio_initialised) + squidaio_init(); + requestp = memPoolAlloc(squidaio_request_pool); + requestp->fd = fd; + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_CLOSE; + requestp->cancelled = 0; + resultp->result_type = _AIO_OP_CLOSE; + + squidaio_queue_request(requestp); + return 0; +} + + +static void +squidaio_do_close(squidaio_request_t * requestp) +{ + if((requestp->ret = close(requestp->fd)) < 0) { + debug(43, 0) ("squidaio_do_close: FD %d, errno %d\n", requestp->fd, errno); + close(requestp->fd); + } + requestp->err = errno; +} + + +int +squidaio_stat(const char *path, struct stat *sb, squidaio_result_t * resultp) +{ + squidaio_request_t *requestp; + + if (!squidaio_initialised) + squidaio_init(); + requestp = memPoolAlloc(squidaio_request_pool); + requestp->path = (char *) squidaio_xstrdup(path); + requestp->statp = sb; + requestp->tmpstatp = (struct stat *) squidaio_xmalloc(sizeof(struct stat)); + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_STAT; + requestp->cancelled = 0; + resultp->result_type = _AIO_OP_STAT; + + squidaio_queue_request(requestp); + return 0; +} + + +static void +squidaio_do_stat(squidaio_request_t * requestp) +{ + requestp->ret = stat(requestp->path, requestp->tmpstatp); + requestp->err = errno; +} + + +int +squidaio_unlink(const char *path, squidaio_result_t * resultp) +{ + squidaio_request_t *requestp; + + if (!squidaio_initialised) + squidaio_init(); + requestp = memPoolAlloc(squidaio_request_pool); + requestp->path = squidaio_xstrdup(path); + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_UNLINK; + requestp->cancelled = 0; + resultp->result_type = _AIO_OP_UNLINK; + + squidaio_queue_request(requestp); + return 0; +} + + +static void +squidaio_do_unlink(squidaio_request_t * requestp) +{ + requestp->ret = unlink(requestp->path); + requestp->err = errno; +} + +int +squidaio_truncate(const char *path, off_t length, squidaio_result_t * resultp) +{ + squidaio_request_t *requestp; + + if (!squidaio_initialised) + squidaio_init(); + requestp = memPoolAlloc(squidaio_request_pool); + requestp->path = (char *) squidaio_xstrdup(path); + requestp->offset = length; + requestp->resultp = resultp; + requestp->request_type = _AIO_OP_TRUNCATE; + requestp->cancelled = 0; + resultp->result_type = _AIO_OP_TRUNCATE; + + squidaio_queue_request(requestp); + return 0; +} + + +static void +squidaio_do_truncate(squidaio_request_t * requestp) +{ + requestp->ret = truncate(requestp->path, requestp->offset); + requestp->err = errno; +} + + +#if AIO_OPENDIR +/* XXX squidaio_opendir NOT implemented yet.. */ + +int +squidaio_opendir(const char *path, squidaio_result_t * resultp) +{ + squidaio_request_t *requestp; + int len; + + if (!squidaio_initialised) + squidaio_init(); + requestp = memPoolAlloc(squidaio_request_pool); + resultp->result_type = _AIO_OP_OPENDIR; + return -1; +} + +static void +squidaio_do_opendir(squidaio_request_t * requestp) +{ + /* NOT IMPLEMENTED */ +} + +#endif + +static void +squidaio_poll_queues(void) +{ + /* kick "overflow" request queue */ + if (request_queue2.head && + (WaitForSingleObject(request_queue.mutex, 0)==WAIT_OBJECT_0)) { + *request_queue.tailp = request_queue2.head; + request_queue.tailp = request_queue2.tailp; + if (!SetEvent(request_queue.cond)) + fatal("couldn't push queue\n"); + if (!ReleaseMutex(request_queue.mutex)) { + /* unexpected error */ + } + Sleep(0); + request_queue2.head = NULL; + request_queue2.tailp = &request_queue2.head; + } + /* poll done queue */ + if (done_queue.head && + (WaitForSingleObject(done_queue.mutex, 0)==WAIT_OBJECT_0)) { + struct squidaio_request_t *requests = done_queue.head; + done_queue.head = NULL; + done_queue.tailp = &done_queue.head; + if (!ReleaseMutex(done_queue.mutex)) { + /* unexpected error */ + } + Sleep(0); + *done_requests.tailp = requests; + request_queue_len -= 1; + while (requests->next) { + requests = requests->next; + request_queue_len -= 1; + } + done_requests.tailp = &requests->next; + } +} + +squidaio_result_t * +squidaio_poll_done(void) +{ + squidaio_request_t *request; + squidaio_result_t *resultp; + int cancelled; + int polled = 0; + + AIO_REPOLL: + request = done_requests.head; + if (request == NULL && !polled) { + squidaio_poll_queues(); + polled = 1; + request = done_requests.head; + } + if (!request) { + return NULL; + } + debug(43, 9) ("squidaio_poll_done: %p type=%d result=%p\n", + request, request->request_type, request->resultp); + done_requests.head = request->next; + if (!done_requests.head) + done_requests.tailp = &done_requests.head; + resultp = request->resultp; + cancelled = request->cancelled; + squidaio_debug(request); + debug(43, 5) ("DONE: %d -> %d\n", request->ret, request->err); + squidaio_cleanup_request(request); + if (cancelled) + goto AIO_REPOLL; + return resultp; +} /* squidaio_poll_done */ + +int +squidaio_operations_pending(void) +{ + return request_queue_len + (done_requests.head ? 1 : 0); +} + +int +squidaio_sync(void) +{ + /* XXX This might take a while if the queue is large.. */ + do { + squidaio_poll_queues(); + } while (request_queue_len > 0); + return squidaio_operations_pending(); +} + +int +squidaio_get_queue_len(void) +{ + return request_queue_len; +} + +static void +squidaio_debug(squidaio_request_t * request) +{ + switch (request->request_type) { + case _AIO_OP_OPEN: + debug(43, 5) ("OPEN of %s to FD %d\n", request->path, request->ret); + break; + case _AIO_OP_READ: + debug(43, 5) ("READ on fd: %d\n", request->fd); + break; + case _AIO_OP_WRITE: + debug(43, 5) ("WRITE on fd: %d\n", request->fd); + break; + case _AIO_OP_CLOSE: + debug(43, 5) ("CLOSE of fd: %d\n", request->fd); + break; + case _AIO_OP_UNLINK: + debug(43, 5) ("UNLINK of %s\n", request->path); + break; + case _AIO_OP_TRUNCATE: + debug(43, 5) ("UNLINK of %s\n", request->path); + break; + default: + break; + } +} + +void +squidaio_stats(StoreEntry * sentry) +{ + squidaio_thread_t *threadp; + int i; + + if (!squidaio_initialised) + return; + + storeAppendPrintf(sentry, "\n\nThreads Status:\n"); + storeAppendPrintf(sentry, "#\tID\t# Requests\n"); + + threadp = threads; + for (i = 0; i < NUMTHREADS; i++) { + storeAppendPrintf(sentry, "%i\t0x%lx\t%ld\n", i + 1, threadp->dwThreadId, threadp->requests); + threadp = threadp->next; + } +} Index: squid/src/fs/awin32/async_io.c diff -u /dev/null squid/src/fs/awin32/async_io.c:1.1.2.4 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/fs/awin32/async_io.c Sun Oct 20 06:46:20 2002 @@ -0,0 +1,383 @@ + +/* + * $Id$ + * + * DEBUG: section 32 Asynchronous Disk I/O + * AUTHOR: Pete Bentley + * AUTHOR: Stewart Forster + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "store_asyncufs.h" + +#define _AIO_OPEN 0 +#define _AIO_READ 1 +#define _AIO_WRITE 2 +#define _AIO_CLOSE 3 +#define _AIO_UNLINK 4 +#define _AIO_TRUNCATE 4 +#define _AIO_OPENDIR 5 +#define _AIO_STAT 6 + +typedef struct squidaio_ctrl_t { + struct squidaio_ctrl_t *next; + int fd; + int operation; + AIOCB *done_handler; + void *done_handler_data; + squidaio_result_t result; + char *bufp; + FREE *free_func; + dlink_node node; +} squidaio_ctrl_t; + +static struct { + int open_start; + int open_finish; + int close_start; + int close_finish; + int cancel; + int write_start; + int write_finish; + int read_start; + int read_finish; + int stat_start; + int stat_finish; + int unlink_start; + int unlink_finish; + int check_callback; +} squidaio_counts; + +typedef struct squidaio_unlinkq_t { + char *path; + struct squidaio_unlinkq_t *next; +} squidaio_unlinkq_t; + +static dlink_list used_list; +static int initialised = 0; +static OBJH aioStats; +static MemPool *squidaio_ctrl_pool; +static void aioFDWasClosed(int fd); + +static void +aioFDWasClosed(int fd) +{ + if (fd_table[fd].flags.closing) + fd_close(fd); +} + +void +aioInit(void) +{ + if (initialised) + return; + squidaio_ctrl_pool = memPoolCreate("aio_ctrl", sizeof(squidaio_ctrl_t)); + cachemgrRegister("squidaio_counts", "Async IO Function Counters", + aioStats, 0, 1); + initialised = 1; + comm_quick_poll_required(); +} + +void +aioDone(void) +{ + if (!initialised) + return; + squidaio_shutdown(); + memPoolDestroy(&squidaio_ctrl_pool); + initialised = 0; +} + +void +aioOpen(const char *path, int oflag, mode_t mode, AIOCB * callback, void *callback_data) +{ + squidaio_ctrl_t *ctrlp; + + assert(initialised); + squidaio_counts.open_start++; + ctrlp = memPoolAlloc(squidaio_ctrl_pool); + ctrlp->fd = -2; + ctrlp->done_handler = callback; + ctrlp->done_handler_data = cbdataReference(callback_data); + ctrlp->operation = _AIO_OPEN; + ctrlp->result.data = ctrlp; + squidaio_open(path, oflag, mode, &ctrlp->result); + dlinkAdd(ctrlp, &ctrlp->node, &used_list); + return; +} + +void +aioClose(int fd) +{ + squidaio_ctrl_t *ctrlp; + + assert(initialised); + squidaio_counts.close_start++; + aioCancel(fd); + ctrlp = memPoolAlloc(squidaio_ctrl_pool); + ctrlp->fd = fd; + ctrlp->done_handler = NULL; + ctrlp->done_handler_data = NULL; + ctrlp->operation = _AIO_CLOSE; + ctrlp->result.data = ctrlp; + squidaio_close(fd, &ctrlp->result); + dlinkAdd(ctrlp, &ctrlp->node, &used_list); + return; +} + +void +aioCancel(int fd) +{ + squidaio_ctrl_t *curr; + dlink_node *m, *next; + + assert(initialised); + squidaio_counts.cancel++; + for (m = used_list.head; m; m = next) { + next = m->next; + curr = m->data; + if (curr->fd != fd) + continue; + + squidaio_cancel(&curr->result); + + if (curr->done_handler) { + AIOCB *callback = curr->done_handler; + void *cbdata; + curr->done_handler = NULL; + debug(32, 2) ("this be aioCancel\n"); + if (cbdataReferenceValidDone(curr->done_handler_data, &cbdata)) + callback(fd, cbdata, -2, -2); + } + dlinkDelete(m, &used_list); + memPoolFree(squidaio_ctrl_pool, curr); + } +} + + +void +aioWrite(int fd, int offset, char *bufp, int len, AIOCB * callback, void *callback_data, FREE * free_func) +{ + squidaio_ctrl_t *ctrlp; + int seekmode; + + assert(initialised); + squidaio_counts.write_start++; + ctrlp = memPoolAlloc(squidaio_ctrl_pool); + ctrlp->fd = fd; + ctrlp->done_handler = callback; + ctrlp->done_handler_data = cbdataReference(callback_data); + ctrlp->operation = _AIO_WRITE; + ctrlp->bufp = bufp; + ctrlp->free_func = free_func; + if (offset >= 0) + seekmode = SEEK_SET; + else { + seekmode = SEEK_END; + offset = 0; + } + ctrlp->result.data = ctrlp; + squidaio_write(fd, bufp, len, offset, seekmode, &ctrlp->result); + dlinkAdd(ctrlp, &ctrlp->node, &used_list); +} /* aioWrite */ + + +void +aioRead(int fd, int offset, char *bufp, int len, AIOCB * callback, void *callback_data) +{ + squidaio_ctrl_t *ctrlp; + int seekmode; + + assert(initialised); + squidaio_counts.read_start++; + ctrlp = memPoolAlloc(squidaio_ctrl_pool); + ctrlp->fd = fd; + ctrlp->done_handler = callback; + ctrlp->done_handler_data = cbdataReference(callback_data); + ctrlp->operation = _AIO_READ; + if (offset >= 0) + seekmode = SEEK_SET; + else { + seekmode = SEEK_CUR; + offset = 0; + } + ctrlp->result.data = ctrlp; + squidaio_read(fd, bufp, len, offset, seekmode, &ctrlp->result); + dlinkAdd(ctrlp, &ctrlp->node, &used_list); + return; +} /* aioRead */ + +void +aioStat(char *path, struct stat *sb, AIOCB * callback, void *callback_data) +{ + squidaio_ctrl_t *ctrlp; + + assert(initialised); + squidaio_counts.stat_start++; + ctrlp = memPoolAlloc(squidaio_ctrl_pool); + ctrlp->fd = -2; + ctrlp->done_handler = callback; + ctrlp->done_handler_data = cbdataReference(callback_data); + ctrlp->operation = _AIO_STAT; + ctrlp->result.data = ctrlp; + squidaio_stat(path, sb, &ctrlp->result); + dlinkAdd(ctrlp, &ctrlp->node, &used_list); + return; +} /* aioStat */ + +void +aioUnlink(const char *path, AIOCB * callback, void *callback_data) +{ + squidaio_ctrl_t *ctrlp; + assert(initialised); + squidaio_counts.unlink_start++; + ctrlp = memPoolAlloc(squidaio_ctrl_pool); + ctrlp->fd = -2; + ctrlp->done_handler = callback; + ctrlp->done_handler_data = cbdataReference(callback_data); + ctrlp->operation = _AIO_UNLINK; + ctrlp->result.data = ctrlp; + squidaio_unlink(path, &ctrlp->result); + dlinkAdd(ctrlp, &ctrlp->node, &used_list); +} /* aioUnlink */ + +void +aioTruncate(const char *path, off_t length, AIOCB * callback, void *callback_data) +{ + squidaio_ctrl_t *ctrlp; + assert(initialised); + squidaio_counts.unlink_start++; + ctrlp = memPoolAlloc(squidaio_ctrl_pool); + ctrlp->fd = -2; + ctrlp->done_handler = callback; + ctrlp->done_handler_data = cbdataReference(callback_data); + ctrlp->operation = _AIO_TRUNCATE; + ctrlp->result.data = ctrlp; + squidaio_truncate(path, length, &ctrlp->result); + dlinkAdd(ctrlp, &ctrlp->node, &used_list); +} /* aioTruncate */ + + +int +aioCheckCallbacks(SwapDir * SD) +{ + squidaio_result_t *resultp; + squidaio_ctrl_t *ctrlp; + int retval = 0; + + assert(initialised); + squidaio_counts.check_callback++; + for (;;) { + if ((resultp = squidaio_poll_done()) == NULL) + break; + ctrlp = (squidaio_ctrl_t *) resultp->data; + switch (resultp->result_type){ + case _AIO_OP_NONE: + case _AIO_OP_TRUNCATE: + case _AIO_OP_OPENDIR: + break; + case _AIO_OP_OPEN: + ++squidaio_counts.open_finish; + break; + case _AIO_OP_READ: + ++squidaio_counts.read_finish; + break; + case _AIO_OP_WRITE: + ++squidaio_counts.write_finish; + break; + case _AIO_OP_CLOSE: + ++squidaio_counts.close_finish; + break; + case _AIO_OP_UNLINK: + ++squidaio_counts.unlink_finish; + break; + case _AIO_OP_STAT: + ++squidaio_counts.stat_finish; + break; + } + if (ctrlp == NULL) + continue; /* XXX Should not happen */ + dlinkDelete(&ctrlp->node, &used_list); + if (ctrlp->done_handler) { + AIOCB *callback = ctrlp->done_handler; + void *cbdata; + ctrlp->done_handler = NULL; + if (cbdataReferenceValidDone(ctrlp->done_handler_data, &cbdata)) { + retval = 1; /* Return that we've actually done some work */ + callback(ctrlp->fd, cbdata, + ctrlp->result.aio_return, ctrlp->result.aio_errno); + } + } + /* free data if requested to aioWrite() */ + if (ctrlp->free_func) + ctrlp->free_func(ctrlp->bufp); + if (ctrlp->operation == _AIO_CLOSE) + aioFDWasClosed(ctrlp->fd); + memPoolFree(squidaio_ctrl_pool, ctrlp); + } + return retval; +} + +void +aioStats(StoreEntry * sentry) +{ + storeAppendPrintf(sentry, "ASYNC IO Counters:\n"); + storeAppendPrintf(sentry, "Operation\t# Requests\tNumber serviced\n"); + storeAppendPrintf(sentry, "open\t%d\t%d\n", squidaio_counts.open_start,squidaio_counts.open_finish); + storeAppendPrintf(sentry, "close\t%d\t%d\n", squidaio_counts.close_start,squidaio_counts.close_finish); + storeAppendPrintf(sentry, "cancel\t%d\t-\n", squidaio_counts.cancel); + storeAppendPrintf(sentry, "write\t%d\t%d\n", squidaio_counts.write_start,squidaio_counts.write_finish); + storeAppendPrintf(sentry, "read\t%d\t%d\n", squidaio_counts.read_start,squidaio_counts.read_finish); + storeAppendPrintf(sentry, "stat\t%d\t%d\n", squidaio_counts.stat_start,squidaio_counts.stat_finish); + storeAppendPrintf(sentry, "unlink\t%d\t%d\n", squidaio_counts.unlink_start,squidaio_counts.unlink_finish); + storeAppendPrintf(sentry, "check_callback\t%d\t-\n", squidaio_counts.check_callback); + storeAppendPrintf(sentry, "queue\t%d\t-\n", squidaio_get_queue_len()); + squidaio_stats(sentry); +} + +/* Flush all pending I/O */ +void +aioSync(SwapDir * SD) +{ + if (!initialised) + return; /* nothing to do then */ + /* Flush all pending operations */ + debug(32, 1) ("aioSync: flushing pending I/O operations\n"); + do { + aioCheckCallbacks(SD); + } while (squidaio_sync()); + debug(32, 1) ("aioSync: done\n"); +} + +int +aioQueueSize(void) +{ + return memPoolInUseCount(squidaio_ctrl_pool); +} Index: squid/src/fs/awin32/store_asyncufs.h diff -u /dev/null squid/src/fs/awin32/store_asyncufs.h:1.1.2.4 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/fs/awin32/store_asyncufs.h Sun Oct 20 06:46:20 2002 @@ -0,0 +1,131 @@ +/* + * store_aufs.h + * + * Internal declarations for the aufs routines + */ + +#ifndef __STORE_ASYNCUFS_H__ +#define __STORE_ASYNCUFS_H__ + +#ifdef AUFS_IO_THREADS +#define NUMTHREADS AUFS_IO_THREADS +#else +#define NUMTHREADS (Config.cacheSwap.n_configured*16) +#endif + +/* Queue limit where swapouts are deferred (load calculation) */ +#define MAGIC1 (NUMTHREADS*Config.cacheSwap.n_configured*5) +/* Queue limit where swapins are deferred (open/create fails) */ +#define MAGIC2 (NUMTHREADS*Config.cacheSwap.n_configured*20) + +/* Which operations to run async */ +#define ASYNC_OPEN 1 +#define ASYNC_CLOSE 0 +#define ASYNC_CREATE 1 +#define ASYNC_WRITE 0 +#define ASYNC_READ 1 + +enum _squidaio_request_type { + _AIO_OP_NONE = 0, + _AIO_OP_OPEN, + _AIO_OP_READ, + _AIO_OP_WRITE, + _AIO_OP_CLOSE, + _AIO_OP_UNLINK, + _AIO_OP_TRUNCATE, + _AIO_OP_OPENDIR, + _AIO_OP_STAT +}; +typedef enum _squidaio_request_type squidaio_request_type; + +struct _squidaio_result_t { + int aio_return; + int aio_errno; + enum _squidaio_request_type result_type; + void *_data; /* Internal housekeeping */ + void *data; /* Available to the caller */ +}; + +typedef struct _squidaio_result_t squidaio_result_t; + +typedef void AIOCB(int fd, void *, int aio_return, int aio_errno); + +int squidaio_cancel(squidaio_result_t *); +int squidaio_open(const char *, int, mode_t, squidaio_result_t *); +int squidaio_read(int, char *, int, off_t, int, squidaio_result_t *); +int squidaio_write(int, char *, int, off_t, int, squidaio_result_t *); +int squidaio_close(int, squidaio_result_t *); +int squidaio_stat(const char *, struct stat *, squidaio_result_t *); +int squidaio_unlink(const char *, squidaio_result_t *); +int squidaio_truncate(const char *, off_t length, squidaio_result_t *); +int squidaio_opendir(const char *, squidaio_result_t *); +squidaio_result_t *squidaio_poll_done(void); +int squidaio_operations_pending(void); +int squidaio_sync(void); +int squidaio_get_queue_len(void); +void squidaio_shutdown(void); +void squidaio_stats(StoreEntry *); + +void aioInit(void); +void aioDone(void); +void aioCancel(int); +void aioOpen(const char *, int, mode_t, AIOCB *, void *); +void aioClose(int); +void aioWrite(int, int offset, char *, int size, AIOCB *, void *, FREE *); +void aioRead(int, int offset, char *, int size, AIOCB *, void *); +void aioStat(char *, struct stat *, AIOCB *, void *); +void aioUnlink(const char *, AIOCB *, void *); +void aioTruncate(const char *, off_t length, AIOCB *, void *); +int aioCheckCallbacks(SwapDir *); +void aioSync(SwapDir *); +int aioQueueSize(void); + +struct _squidaiostate_t { + int fd; + struct { + unsigned int close_request:1; + unsigned int reading:1; + unsigned int writing:1; + unsigned int opening:1; + unsigned int write_kicking:1; + unsigned int read_kicking:1; + unsigned int inreaddone:1; + } flags; + const char *read_buf; + link_list *pending_writes; + link_list *pending_reads; +}; + +struct _queued_write { + char *buf; + size_t size; + off_t offset; + FREE *free_func; +}; + +struct _queued_read { + char *buf; + size_t size; + off_t offset; + STRCB *callback; + void *callback_data; +}; + +typedef struct _squidaiostate_t squidaiostate_t; + +/* The squidaio_state memory pools */ +extern MemPool *squidaio_state_pool; +extern MemPool *aufs_qread_pool; +extern MemPool *aufs_qwrite_pool; + +/* + * Store IO stuff + */ +extern STOBJCREATE storeAufsCreate; +extern STOBJOPEN storeAufsOpen; +extern STOBJCLOSE storeAufsClose; +extern STOBJREAD storeAufsRead; +extern STOBJWRITE storeAufsWrite; +extern STOBJUNLINK storeAufsUnlink; + +#endif Index: squid/src/fs/awin32/store_dir_aufs.c diff -u /dev/null squid/src/fs/awin32/store_dir_aufs.c:1.1.2.6 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/fs/awin32/store_dir_aufs.c Sun Oct 20 06:46:20 2002 @@ -0,0 +1,250 @@ + +/* + * $Id$ + * + * DEBUG: section 47 Store Directory Routines + * AUTHOR: Duane Wessels + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" + +#include "store_asyncufs.h" +#include "ufscommon.h" + +MemPool *squidaio_state_pool = NULL; +MemPool *aufs_qread_pool = NULL; +MemPool *aufs_qwrite_pool = NULL; +static int asyncufs_initialised = 0; + +static STDUMP storeAufsDirDump; +static STCHECKOBJ storeAufsDirCheckObj; +static void storeAufsDirIOUnlinkFile(char *path); + + +/* The MAIN externally visible function */ +STSETUP storeFsSetup_awin32; + +/* + * storeAufsDirCheckObj + * + * This routine is called by storeDirSelectSwapDir to see if the given + * object is able to be stored on this filesystem. AUFS filesystems will + * happily store anything as long as the LRU time isn't too small. + */ +int +storeAufsDirCheckObj(SwapDir * SD, const StoreEntry * e) +{ + int loadav; + int ql; + +#if OLD_UNUSED_CODE + if (storeAufsDirExpiredReferenceAge(SD) < 300) { + debug(47, 3) ("storeAufsDirCheckObj: NO: LRU Age = %d\n", + storeAufsDirExpiredReferenceAge(SD)); + /* store_check_cachable_hist.no.lru_age_too_low++; */ + return -1; + } +#endif + ql = aioQueueSize(); + if (ql == 0) + loadav = 0; + loadav = ql * 1000 / MAGIC1; + debug(47, 9) ("storeAufsDirCheckObj: load=%d\n", loadav); + return loadav; +} + +void +storeAufsDirIOUnlinkFile(char *path) +{ +#if USE_TRUNCATE_NOT_UNLINK + aioTruncate(path, NULL, NULL); +#else + aioUnlink(path, NULL, NULL); +#endif +} + +/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */ + +static struct cache_dir_option options[] = +{ +#if NOT_YET_DONE + {"L1", storeAufsDirParseL1, storeAufsDirDumpL1}, + {"L2", storeAufsDirParseL2, storeAufsDirDumpL2}, +#endif + {NULL, NULL} +}; + +/* + * storeAufsDirReconfigure + * + * This routine is called when the given swapdir needs reconfiguring + */ +static void +storeAufsDirReconfigure(SwapDir * sd, int index, char *path) +{ + int i; + int size; + int l1; + int l2; + + i = GetInteger(); + size = i << 10; /* Mbytes to kbytes */ + if (size <= 0) + fatal("storeAufsDirReconfigure: invalid size value"); + i = GetInteger(); + l1 = i; + if (l1 <= 0) + fatal("storeAufsDirReconfigure: invalid level 1 directories value"); + i = GetInteger(); + l2 = i; + if (l2 <= 0) + fatal("storeAufsDirReconfigure: invalid level 2 directories value"); + + /* just reconfigure it */ + if (size == sd->max_size) + debug(3, 1) ("Cache dir '%s' size remains unchanged at %d KB\n", + path, size); + else + debug(3, 1) ("Cache dir '%s' size changed to %d KB\n", + path, size); + sd->max_size = size; + + parse_cachedir_options(sd, options, 0); + + return; +} + +void +storeAufsDirDump(StoreEntry * entry, SwapDir * s) +{ + commonUfsDirDump (entry, s); + dump_cachedir_options(entry, options, s); +} + +/* + * storeAufsDirParse * + * Called when a *new* fs is being setup. + */ +static void +storeAufsDirParse(SwapDir * sd, int index, char *path) +{ + int i; + int size; + int l1; + int l2; + squidufsinfo_t *aioinfo; + + i = GetInteger(); + size = i << 10; /* Mbytes to kbytes */ + if (size <= 0) + fatal("storeAufsDirParse: invalid size value"); + i = GetInteger(); + l1 = i; + if (l1 <= 0) + fatal("storeAufsDirParse: invalid level 1 directories value"); + i = GetInteger(); + l2 = i; + if (l2 <= 0) + fatal("storeAufsDirParse: invalid level 2 directories value"); + + aioinfo = xmalloc(sizeof(squidufsinfo_t)); + if (aioinfo == NULL) + fatal("storeAufsDirParse: couldn't xmalloc() squidufsinfo_t!\n"); + + sd->index = index; + sd->path = xstrdup(path); + sd->max_size = size; + sd->fsdata = aioinfo; + aioinfo->l1 = l1; + aioinfo->l2 = l2; + aioinfo->swaplog_fd = -1; + aioinfo->map = NULL; /* Debugging purposes */ + aioinfo->suggest = 0; + aioinfo->io.storeDirUnlinkFile = storeAufsDirIOUnlinkFile; + sd->init = commonUfsDirInit; + sd->newfs = commonUfsDirNewfs; + sd->dump = storeAufsDirDump; + sd->freefs = commonUfsDirFree; + sd->dblcheck = commonUfsCleanupDoubleCheck; + sd->statfs = commonUfsDirStats; + sd->maintainfs = commonUfsDirMaintain; + sd->checkobj = storeAufsDirCheckObj; + sd->refobj = commonUfsDirRefObj; + sd->unrefobj = commonUfsDirUnrefObj; + sd->callback = aioCheckCallbacks; + sd->sync = aioSync; + sd->obj.create = storeAufsCreate; + sd->obj.open = storeAufsOpen; + sd->obj.close = storeAufsClose; + sd->obj.read = storeAufsRead; + sd->obj.write = storeAufsWrite; + sd->obj.unlink = storeAufsUnlink; + sd->log.open = commonUfsDirOpenSwapLog; + sd->log.close = commonUfsDirCloseSwapLog; + sd->log.write = commonUfsDirSwapLog; + sd->log.clean.start = commonUfsDirWriteCleanStart; + sd->log.clean.nextentry = commonUfsDirCleanLogNextEntry; + sd->log.clean.done = commonUfsDirWriteCleanDone; + + parse_cachedir_options(sd, options, 0); + + /* Initialise replacement policy stuff */ + sd->repl = createRemovalPolicy(Config.replPolicy); +} + +/* + * Initial setup / end destruction + */ +static void +storeAufsDirDone(void) +{ + aioDone(); + memPoolDestroy(&squidaio_state_pool); + memPoolDestroy(&aufs_qread_pool); + memPoolDestroy(&aufs_qwrite_pool); + asyncufs_initialised = 0; +} + +void +storeFsSetup_awin32(storefs_entry_t * storefs) +{ + assert(!asyncufs_initialised); + storefs->parsefunc = storeAufsDirParse; + storefs->reconfigurefunc = storeAufsDirReconfigure; + storefs->donefunc = storeAufsDirDone; + squidaio_state_pool = memPoolCreate("AWIN32 IO State data", sizeof(squidaiostate_t)); + aufs_qread_pool = memPoolCreate("AWIN32 Queued read data", + sizeof(queued_read)); + aufs_qwrite_pool = memPoolCreate("AWIN32 Queued write data", + sizeof(queued_write)); + + asyncufs_initialised = 1; + aioInit(); +} Index: squid/src/fs/awin32/store_io_aufs.c diff -u /dev/null squid/src/fs/awin32/store_io_aufs.c:1.1.2.5 --- /dev/null Thu Jan 1 01:00:00 1970 +++ squid/src/fs/awin32/store_io_aufs.c Sun Oct 20 06:46:20 2002 @@ -0,0 +1,473 @@ + +/* + * $Id$ + * + * DEBUG: section 79 Storage Manager awin32 Interface + * AUTHOR: Robert Collins + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "store_asyncufs.h" +#include "ufscommon.h" + +#if ASYNC_READ +static AIOCB storeAufsReadDone; +#else +static DRCB storeAufsReadDone; +#endif +#if ASYNC_WRITE +static AIOCB storeAufsWriteDone; +#else +static DWCB storeAufsWriteDone; +#endif +static void storeAufsIOCallback(storeIOState * sio, int errflag); +static AIOCB storeAufsOpenDone; +static int storeAufsSomethingPending(storeIOState *); +static int storeAufsKickWriteQueue(storeIOState * sio); +static CBDUNL storeAufsIOFreeEntry; + +CBDATA_TYPE(storeIOState); + +/* === PUBLIC =========================================================== */ + +/* open for reading */ +storeIOState * +storeAufsOpen(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, + STIOCB * callback, void *callback_data) +{ + sfileno f = e->swap_filen; + char *path = commonUfsDirFullPath(SD, f, NULL); + storeIOState *sio; +#if !ASYNC_OPEN + int fd; +#endif + debug(79, 3) ("storeAufsOpen: fileno %08X\n", f); + /* + * we should detect some 'too many files open' condition and return + * NULL here. + */ +#ifdef MAGIC2 + if (aioQueueSize() > MAGIC2) + return NULL; +#endif +#if !ASYNC_OPEN + fd = file_open(path, O_RDONLY | O_BINARY); + if (fd < 0) { + debug(79, 3) ("storeAufsOpen: got failure (%d)\n", errno); + return NULL; + } +#endif + CBDATA_INIT_TYPE_FREECB(storeIOState, storeAufsIOFreeEntry); + sio = cbdataAlloc(storeIOState); + sio->fsstate = memPoolAlloc(squidaio_state_pool); + ((squidaiostate_t *) (sio->fsstate))->fd = -1; + ((squidaiostate_t *) (sio->fsstate))->flags.opening = 1; + sio->swap_filen = f; + sio->swap_dirn = SD->index; + sio->mode = O_RDONLY | O_BINARY; + sio->callback = callback; + sio->callback_data = cbdataReference(callback_data); + sio->e = e; + Opening_FD++; +#if ASYNC_OPEN + aioOpen(path, O_RDONLY | O_BINARY, 0644, storeAufsOpenDone, sio); +#else + storeAufsOpenDone(fd, sio, fd, 0); +#endif + return sio; +} + +/* open for creating */ +storeIOState * +storeAufsCreate(SwapDir * SD, StoreEntry * e, STFNCB * file_callback, STIOCB * callback, void *callback_data) +{ + char *path; + storeIOState *sio; + sfileno filn; + sdirno dirn; +#if !ASYNC_CREATE + int fd; +#endif + + /* Allocate a number */ + dirn = SD->index; + filn = commonUfsDirMapBitAllocate(SD); + path = commonUfsDirFullPath(SD, filn, NULL); + + debug(79, 3) ("storeAufsCreate: fileno %08X\n", filn); + /* + * we should detect some 'too many files open' condition and return + * NULL here. + */ +#ifdef MAGIC2 + if (aioQueueSize() > MAGIC2) + return NULL; +#endif +#if !ASYNC_CREATE + fd = file_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY); + if (fd < 0) { + debug(79, 3) ("storeAufsCreate: got failure (%d)\n", errno); + return NULL; + } +#endif + CBDATA_INIT_TYPE_FREECB(storeIOState, storeAufsIOFreeEntry); + sio = cbdataAlloc(storeIOState); + sio->fsstate = memPoolAlloc(squidaio_state_pool); + ((squidaiostate_t *) (sio->fsstate))->fd = -1; + ((squidaiostate_t *) (sio->fsstate))->flags.opening = 1; + sio->swap_filen = filn; + sio->swap_dirn = dirn; + sio->mode = O_WRONLY | O_BINARY; + sio->callback = callback; + sio->callback_data = cbdataReference(callback_data); + sio->e = (StoreEntry *) e; + Opening_FD++; +#if ASYNC_CREATE + aioOpen(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644, storeAufsOpenDone, sio); +#else + storeAufsOpenDone(fd, sio, fd, 0); +#endif + + /* now insert into the replacement policy */ + commonUfsDirReplAdd(SD, e); + return sio; + +} + + + +/* Close */ +void +storeAufsClose(SwapDir * SD, storeIOState * sio) +{ + squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate; + debug(79, 3) ("storeAufsClose: dirno %d, fileno %08X, FD %d\n", + sio->swap_dirn, sio->swap_filen, aiostate->fd); + if (storeAufsSomethingPending(sio)) { + aiostate->flags.close_request = 1; + return; + } + storeAufsIOCallback(sio, DISK_OK); +} + + +/* Read */ +void +storeAufsRead(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data) +{ + squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate; + assert(sio->read.callback == NULL); + assert(sio->read.callback_data == NULL); + assert(!aiostate->flags.reading); + if (aiostate->fd < 0) { + struct _queued_read *q; + debug(79, 3) ("storeAufsRead: queueing read because FD < 0\n"); + assert(aiostate->flags.opening); + assert(aiostate->pending_reads == NULL); + q = memPoolAlloc(aufs_qread_pool); + q->buf = buf; + q->size = size; + q->offset = offset; + q->callback = callback; + q->callback_data = callback_data; + linklistPush(&(aiostate->pending_reads), q); + return; + } + sio->read.callback = callback; + sio->read.callback_data = cbdataReference(callback_data); + aiostate->read_buf = buf; + debug(79, 3) ("storeAufsRead: dirno %d, fileno %08X, FD %d\n", + sio->swap_dirn, sio->swap_filen, aiostate->fd); + sio->offset = offset; + aiostate->flags.reading = 1; +#if ASYNC_READ + aioRead(aiostate->fd, offset, buf, size, storeAufsReadDone, sio); +#else + file_read(aiostate->fd, buf, size, offset, storeAufsReadDone, sio); +#endif +} + + +/* Write */ +void +storeAufsWrite(SwapDir * SD, storeIOState * sio, char *buf, size_t size, off_t offset, FREE * free_func) +{ + squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate; + debug(79, 3) ("storeAufsWrite: dirno %d, fileno %08X, FD %d\n", + sio->swap_dirn, sio->swap_filen, aiostate->fd); + if (aiostate->fd < 0) { + /* disk file not opened yet */ + struct _queued_write *q; + assert(aiostate->flags.opening); + q = memPoolAlloc(aufs_qwrite_pool); + q->buf = buf; + q->size = size; + q->offset = offset; + q->free_func = free_func; + linklistPush(&(aiostate->pending_writes), q); + return; + } +#if ASYNC_WRITE + if (aiostate->flags.writing) { + struct _queued_write *q; + debug(79, 3) ("storeAufsWrite: queuing write\n"); + q = memPoolAlloc(aufs_qwrite_pool); + q->buf = buf; + q->size = size; + q->offset = offset; + q->free_func = free_func; + linklistPush(&(aiostate->pending_writes), q); + return; + } + aiostate->flags.writing = 1; + aioWrite(aiostate->fd, offset, buf, size, storeAufsWriteDone, sio, + free_func); +#else + file_write(aiostate->fd, offset, buf, size, storeAufsWriteDone, sio, + free_func); +#endif +} + +/* Unlink */ +void +storeAufsUnlink(SwapDir * SD, StoreEntry * e) +{ + debug(79, 3) ("storeAufsUnlink: dirno %d, fileno %08X\n", SD->index, e->swap_filen); + commonUfsDirReplRemove(e); + commonUfsDirMapBitReset(SD, e->swap_filen); + commonUfsDirUnlinkFile(SD, e->swap_filen); +} + +/* === STATIC =========================================================== */ + +static int +storeAufsKickWriteQueue(storeIOState * sio) +{ + squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate; + struct _queued_write *q = linklistShift(&aiostate->pending_writes); + if (NULL == q) + return 0; + debug(79, 3) ("storeAufsKickWriteQueue: writing queued chunk of %ld bytes\n", + (long int) q->size); + storeAufsWrite(INDEXSD(sio->swap_dirn), sio, q->buf, q->size, q->offset, q->free_func); + memPoolFree(aufs_qwrite_pool, q); + return 1; +} + +static int +storeAufsKickReadQueue(storeIOState * sio) +{ + squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate; + struct _queued_read *q = linklistShift(&(aiostate->pending_reads)); + if (NULL == q) + return 0; + debug(79, 3) ("storeAufsKickReadQueue: reading queued request of %ld bytes\n", + (long int) q->size); + storeAufsRead(INDEXSD(sio->swap_dirn), sio, q->buf, q->size, q->offset, q->callback, q->callback_data); + memPoolFree(aufs_qread_pool, q); + return 1; +} + +static void +storeAufsOpenDone(int unused, void *my_data, int fd, int errflag) +{ + storeIOState *sio = my_data; + squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate; + debug(79, 3) ("storeAufsOpenDone: FD %d, errflag %d\n", fd, errflag); + Opening_FD--; + aiostate->flags.opening = 0; + if (errflag || fd < 0) { + errno = errflag; + debug(79, 0) ("storeAufsOpenDone: %s\n", xstrerror()); + debug(79, 1) ("\t%s\n", commonUfsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL)); + storeAufsIOCallback(sio, DISK_ERROR); + return; + } + store_open_disk_fd++; + aiostate->fd = fd; + commSetCloseOnExec(fd); + fd_open(fd, FD_FILE, commonUfsDirFullPath(INDEXSD(sio->swap_dirn), sio->swap_filen, NULL)); + if (FILE_MODE(sio->mode) == O_WRONLY) { + if (storeAufsKickWriteQueue(sio)) + return; + } else if (FILE_MODE(sio->mode) == O_RDONLY) { + if (storeAufsKickReadQueue(sio)) + return; + } + if (aiostate->flags.close_request) + storeAufsIOCallback(sio, errflag); + debug(79, 3) ("storeAufsOpenDone: exiting\n"); +} + +#if ASYNC_READ +static void +storeAufsReadDone(int fd, void *my_data, int len, int errflag) +#else +static void +storeAufsReadDone(int fd, const char *buf, int len, int errflag, void *my_data) +#endif +{ + storeIOState *sio = my_data; + squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate; + STRCB *callback = sio->read.callback; + void *cbdata; + ssize_t rlen; + debug(79, 3) ("storeAufsReadDone: dirno %d, fileno %08X, FD %d, len %d\n", + sio->swap_dirn, sio->swap_filen, fd, len); + aiostate->flags.inreaddone = 1; + aiostate->flags.reading = 0; + if (errflag) { + debug(79, 3) ("storeAufsReadDone: got failure (%d)\n", errflag); + rlen = -1; + } else { + rlen = (ssize_t) len; + sio->offset += len; + } +#if ASYNC_READ + /* translate errflag from errno to Squid disk error */ + errno = errflag; + if (errflag) + errflag = DISK_ERROR; + else + errflag = DISK_OK; +#else + if (errflag == DISK_EOF) + errflag = DISK_OK; /* EOF is signalled by len == 0, not errors... */ +#endif + assert(callback); + sio->read.callback = NULL; + if (cbdataReferenceValidDone(sio->read.callback_data, &cbdata)) + callback(cbdata, aiostate->read_buf, rlen); + aiostate->flags.inreaddone = 0; + if (aiostate->flags.close_request) + storeAufsIOCallback(sio, errflag); +} + +#if ASYNC_WRITE +static void +storeAufsWriteDone(int fd, void *my_data, int len, int errflag) +#else +static void +storeAufsWriteDone(int fd, int errflag, size_t len, void *my_data) +#endif +{ + static int loop_detect = 0; + storeIOState *sio = my_data; + squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate; + debug(79, 3) ("storeAufsWriteDone: dirno %d, fileno %08X, FD %d, len %ld, err=%d\n", + sio->swap_dirn, sio->swap_filen, fd, (long int) len, errflag); +#if ASYNC_WRITE + /* Translate from errno to Squid disk error */ + errno = errflag; + if (errflag) + errflag = errno == ENOSPC ? DISK_NO_SPACE_LEFT : DISK_ERROR; + else + errflag = DISK_OK; +#endif + assert(++loop_detect < 10); + aiostate->flags.writing = 0; + if (errflag) { + debug(79, 0) ("storeAufsWriteDone: got failure (%d)\n", errflag); + storeAufsIOCallback(sio, errflag); + loop_detect--; + return; + } + sio->offset += len; +#if ASYNC_WRITE + if (!storeAufsKickWriteQueue(sio)) + 0; + else if (aiostate->flags.close_request) + storeAufsIOCallback(sio, errflag); +#else + if (!aiostate->flags.write_kicking) { + aiostate->flags.write_kicking = 1; + while (storeAufsKickWriteQueue(sio)) + (void) 0; + aiostate->flags.write_kicking = 0; + if (aiostate->flags.close_request) + storeAufsIOCallback(sio, errflag); + } +#endif + loop_detect--; +} + +static void +storeAufsIOCallback(storeIOState * sio, int errflag) +{ + STIOCB *callback = sio->callback; + squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate; + int fd = aiostate->fd; + debug(79, 3) ("storeAufsIOCallback: errflag=%d\n", errflag); + debug(79, 3) ("%s:%d\n", __FILE__, __LINE__); + if (callback) { + void *cbdata; + sio->callback = NULL; + if (cbdataReferenceValidDone(sio->callback_data, &cbdata)) + callback(cbdata, errflag, sio); + } + debug(79, 3) ("%s:%d\n", __FILE__, __LINE__); + aiostate->fd = -1; + cbdataFree(sio); + if (fd < 0) + return; + debug(79, 3) ("%s:%d\n", __FILE__, __LINE__); + aioClose(fd); + fd_close(fd); + store_open_disk_fd--; + debug(79, 3) ("%s:%d\n", __FILE__, __LINE__); +} + + +static int +storeAufsSomethingPending(storeIOState * sio) +{ + squidaiostate_t *aiostate = (squidaiostate_t *) sio->fsstate; + if (aiostate->flags.reading) + return 1; + if (aiostate->flags.writing) + return 1; + if (aiostate->flags.opening) + return 1; + if (aiostate->flags.inreaddone) + return 1; + return 0; +} + + +/* + * Clean up references from the SIO before it gets released. + * The actuall SIO is managed by cbdata so we do not need + * to bother with that. + */ +static void +storeAufsIOFreeEntry(void *sio) +{ + memPoolFree(squidaio_state_pool, ((storeIOState *) sio)->fsstate); +} Index: squid/src/fs/ufs/store_io_ufs.c diff -u squid/src/fs/ufs/store_io_ufs.c:1.10 squid/src/fs/ufs/store_io_ufs.c:1.2.24.8 --- squid/src/fs/ufs/store_io_ufs.c:1.10 Sat Oct 12 02:58:27 2002 +++ squid/src/fs/ufs/store_io_ufs.c Sun Oct 20 06:46:23 2002 @@ -78,7 +78,7 @@ ((ufsstate_t *) (sio->fsstate))->flags.reading = 0; ((ufsstate_t *) (sio->fsstate))->flags.close_request = 0; if (fstat(fd, &sb) == 0) - sio->st_size = sb.st_size; + sio->st_size = (size_t) sb.st_size; store_open_disk_fd++; /* We should update the heap/dlink position here ! */