www.pudn.com > tdi_fw.zip > tdi_fw_svc.c


// $Id: tdi_fw_svc.c,v 1.12 2002/12/05 13:03:27 dev Exp $ 
 
#include  
#include  
#include  
#include  
#include  
 
#include "flt_rule.h" 
#include "ipc.h" 
#include "tdi_fw_svc.h" 
#include "thread.h" 
 
#include "msg.h" 
 
#define DISP_BUF_SIZE	0x10000 
 
HANDLE g_device = INVALID_HANDLE_VALUE; 
 
static HANDLE g_event = NULL; 
static HANDLE g_exit_event = NULL; 
 
static HANDLE g_pipe = INVALID_HANDLE_VALUE; 
 
static HANDLE g_dispatcher = NULL; 
static char *g_disp_buf = NULL; 
 
static DWORD WINAPI dispatcher(LPVOID param); 
static DWORD WINAPI restart_thread(LPVOID param); 
 
static void dispatch_request(struct flt_request *request); 
 
static FILE *g_logfile = NULL; 
 
static BOOL		load_config(const char *config); 
static BOOL		read_config(const char *config); 
static BOOL		add_rules_name(const char *main_name, const char *config, int chain); 
static void		add_rules(char *buf, const char *name, int chain); 
static BOOL		get_pname_by_pid(u_long pid, char *buf, int buf_size); 
static void		prepare_addr(char *buf, int size, u_long addr); 
 
static void		my_GetLongPathName(LPCSTR lpszShortPath, LPSTR lpszLongPath, 
								   DWORD cchBuffer); 
 
// some config switches 
static BOOL g_eventlog_allow = FALSE; 
static BOOL g_eventlog_deny = FALSE; 
static BOOL g_eventlog_error = FALSE; 
 
// used in flt_rule.c 
BOOL g_rules_resolve_addr = FALSE; 
 
static BOOL g_logs_resolve_addr = FALSE; 
 
static void log_msg(const char *msg, int type); 
 
enum { 
	MSGTYPE_ALLOW, 
	MSGTYPE_DENY, 
	MSGTYPE_ERROR 
}; 
 
int 
start(const char *config) 
{ 
	static char device_name[] = "\\\\.\\tdi_fw"; 
 
	int result = FALSE; 
	WSADATA wsd; 
	DWORD thread_id; 
 
	// startup sockets for names resolution 
	WSAStartup(MAKEWORD(1, 1), &wsd); 
 
	/* connect with driver */ 
	 
	g_device = CreateFile(device_name, GENERIC_READ | GENERIC_WRITE,  
		FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 
	if (g_device == INVALID_HANDLE_VALUE) { 
		winerr(device_name); 
		goto done; 
	} 
 
	/* open event for driver communication */ 
 
	g_event = OpenEvent(SYNCHRONIZE, FALSE, "tdi_fw_request"); 
	if (g_event == NULL) { 
		winerr("start: OpenEvent"); 
		goto done; 
	} 
 
	/* load config & rules */ 
 
	if (!load_config(config)) 
		goto done; 
 
	/* start dispatcher thread */ 
 
	g_exit_event = CreateEvent(NULL, TRUE, FALSE, NULL); 
	if (g_exit_event == NULL) { 
		winerr("start: CreateEvent"); 
		goto done; 
	} 
 
	g_disp_buf = (char *)malloc(DISP_BUF_SIZE); 
	if (g_disp_buf == NULL) { 
		liberr("start: malloc"); 
		goto done; 
	} 
 
	g_dispatcher = lib_CreateThread(NULL, 0, dispatcher, (LPVOID)config, 0, &thread_id); 
	if (g_dispatcher == NULL) { 
		winerr("start: lib_CreateThread"); 
		goto done; 
	} 
	 
	result = TRUE; 
 
done: 
	if (!result) 
		stop(); 
 
	return result; 
} 
 
void 
stop(void) 
{ 
	// disconnect from driver 
	if (g_device != INVALID_HANDLE_VALUE) { 
		HANDLE old_h = g_device; 
		g_device = INVALID_HANDLE_VALUE; 
		CancelIo(old_h); 
		CloseHandle(old_h); 
	} 
 
	// stop dispatcher thread 
 
	if (g_exit_event != NULL) 
		SetEvent(g_exit_event); 
	 
	if (g_dispatcher != NULL) { 
		WaitForSingleObject(g_dispatcher, INFINITE); 
		g_dispatcher = NULL; 
	} 
 
	// close logfile 
	if (g_logfile != NULL) { 
		fprintf(g_logfile, "--- end ---\n"); 
		fclose(g_logfile); 
		g_logfile = NULL; 
	} 
 
	if (g_exit_event != NULL) 
		CloseHandle(g_exit_event); 
	if (g_event != NULL) 
		CloseHandle(g_event); 
 
	if (g_disp_buf != NULL) { 
		free(g_disp_buf); 
		g_disp_buf = NULL; 
	} 
} 
 
void 
wait(void) 
{ 
	if (g_exit_event != NULL) 
		WaitForSingleObject(g_exit_event, INFINITE); 
} 
 
/* output functions */ 
 
void 
error(const char *fmt, ...) 
{ 
	va_list ap; 
	char message[1024]; 
 
	// prepare message 
 
	va_start(ap, fmt); 
 
	if (_vsnprintf(message, sizeof(message), fmt, ap) == -1) 
		message[sizeof(message) - 1] = '\0'; 
 
	va_end(ap); 
 
	// got message 
	log_msg(message, MSGTYPE_ERROR); 
} 
 
void 
winerr(const char *fn) 
{ 
	char *win_msg = NULL; 
	DWORD code = GetLastError(); 
 
	if (code == 0) 
		error("WINERR\t%s:", fn); 
	else { 
		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |  
			FORMAT_MESSAGE_FROM_SYSTEM,NULL,code, 
			MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR)&win_msg,0,NULL); 
 
		if (win_msg != NULL) { 
			// kill \r\n 
			int len = strlen(win_msg); 
			 
			if (len >= 1) 
				win_msg[len-1]=0; 
			if (len >= 2) 
				win_msg[len-2]=0; 
			 
			error("WINERR\t%s: %s", fn, win_msg); 
 
			LocalFree(win_msg); 
		} else 
			error("WINERR\t%s: %d (0x%x)", fn, code, code); 
	} 
 
	// save error code 
	SetLastError(code); 
} 
 
void 
liberr(const char *fn) 
{ 
	int code = errno; 
	if (code != 0) 
		error("LIBERR\t%s: %s", fn, strerror(code)); 
	else 
		error("LIBERR\t%s:", fn); 
} 
 
/* dispatcher thread */ 
DWORD WINAPI 
dispatcher(LPVOID param) 
{ 
	char *config = (char *)param; 
	HANDLE handles[2]; 
	DWORD i, n; 
 
	handles[0] = g_event; 
	handles[1] = g_exit_event; 
 
	for (;;) { 
		if (!DeviceIoControl(g_device, IOCTL_CMD_GETREQUEST, NULL, 0, 
			g_disp_buf, DISP_BUF_SIZE, &n, NULL)) { 
			winerr("dispatcher: DeviceIoControl"); 
			break; 
		} 
 
		if (n == 0) { 
			// wait for data 
			DWORD wait = WaitForMultipleObjects(2, handles, FALSE, INFINITE); 
			 
			if (wait == WAIT_OBJECT_0 + 1) 
				break; 
			else if (wait != WAIT_OBJECT_0) { 
				winerr("dispatcher: WaitForSingleObject"); 
				break; 
			} 
			continue; 
		} 
 
		for (i = 0; i < n;) { 
			struct flt_request *request; 
			 
			if (n - i < sizeof(*request)) 
				break; 
 
			request = (struct flt_request *)(g_disp_buf + i); 
 
			dispatch_request(request); 
			 
			i += request->struct_size; 
		} 
	} 
 
	if (g_device != INVALID_HANDLE_VALUE) { 
		DWORD thread_id; 
 
		error("dispatcher: unexpected exit!"); 
		 
		// restart tdi_fw_svc 
		lib_CreateThread(NULL, 0, restart_thread, config, 0, &thread_id); 
	} 
 
	return 0; 
} 
 
DWORD 
WINAPI restart_thread(LPVOID param) 
{ 
	char *config = (char *)param; 
 
	error("restarting..."); 
	 
	stop(); 
	start(config); 
	 
	return 0; 
} 
 
/* log filter request from filter driver */ 
void 
dispatch_request(struct flt_request *request) 
{ 
	u_long from_ip, to_ip; 
	u_short from_port, to_port; 
	char msg[1024], pname_buf[MAX_PATH], addr_from[100], addr_to[100], *pname; 
 
	if (request->pname == NULL) { 
		// try to resolve pid to pname 
		if (get_pname_by_pid(request->pid, pname_buf + sizeof(DWORD), sizeof(pname_buf) - sizeof(DWORD))) { 
			// send message to driver to send process name next time 
			DWORD n; 
 
			pname = pname_buf + sizeof(DWORD); 
			*(DWORD *)pname_buf = request->pid; 
 
			if (!DeviceIoControl(g_device, IOCTL_CMD_SETPNAME, pname_buf, 
				sizeof(DWORD) + strlen(pname) + 1, 
				NULL, 0, &n, NULL)) 
				winerr("DeviceIoControl"); 
 
		} else { 
			error("PROCESS\tCan't resolve pid %u!\n", request->pid); 
			sprintf(pname_buf, "pid:%u", request->pid); 
			pname = pname_buf; 
		} 
	 
	} else 
		pname = (char *)&request[1]; 
	 
	// check is it request type "TYPE_RESOLVE_PID" 
	if (request->type == TYPE_RESOLVE_PID) { 
 
		if (_snprintf(msg, sizeof(msg), "PROCESS\t%u\t%s", request->pid, pname) == -1) 
			msg[sizeof(msg) - 1] = '\0'; 
 
		log_msg(msg, request->result == FILTER_ALLOW ? MSGTYPE_ALLOW : MSGTYPE_DENY);	 
		 
	} else { 
	 
		// prepare message 
		 
		from_ip = ((struct sockaddr_in *)&request->addr.from)->sin_addr.s_addr; 
		from_port = ntohs(((struct sockaddr_in *)&request->addr.from)->sin_port); 
		to_ip = ((struct sockaddr_in *)&request->addr.to)->sin_addr.s_addr; 
		to_port = ntohs(((struct sockaddr_in *)&request->addr.to)->sin_port); 
 
		// prepare address "from" & "to" 
		prepare_addr(addr_from, sizeof(addr_from), from_ip); 
		prepare_addr(addr_to, sizeof(addr_to), to_ip); 
 
		// log it! 
		if (_snprintf(msg, sizeof(msg), 
			"%s\t%s\t%s\t%s:%d\t%s:%d\t%s", 
			(request->result == FILTER_ALLOW) ? "ALLOW" : "DENY", 
			(request->proto == IPPROTO_TCP) ? "TCP" : 
				(request->proto == IPPROTO_UDP ? "UDP" : "RawIP"), 
			(request->direction == DIRECTION_IN) ? "IN" : "OUT", 
			addr_from, from_port, 
			addr_to, to_port, pname) == -1) 
			msg[sizeof(msg) - 1] = '\0'; 
 
		log_msg(msg, request->result == FILTER_ALLOW ? MSGTYPE_ALLOW : MSGTYPE_DENY);	 
	} 
} 
 
/* 
 * write message to console, log file or probably to eventlog 
 * 
 * don't call any error functions! to cause infinite recurse. 
 */ 
void 
log_msg(const char *msg, int type) 
{ 
	static int file_day = 0;	// for midnight checking 
	static ULONG event_num = 1;	// number of event in log file 
 
	// if working in console mode write message to console 
	if (g_console) { 
		if (type == MSGTYPE_ERROR) 
			fprintf(stderr, "%s\n", msg); 
		else 
			printf("%s\n", msg); 
	} 
 
	// write to eventlog if filter is set 
	if ((type == MSGTYPE_ALLOW && g_eventlog_allow) || 
		(type == MSGTYPE_DENY && g_eventlog_deny) || 
		(type == MSGTYPE_ERROR && g_eventlog_error)) { 
 
		HANDLE hEventSource; 
		const char *strings; 
 
		// write message to event log 
        // Use event logging to log the error. 
        // 
        hEventSource = RegisterEventSource(NULL, "tdi_fw_svc"); 
 
        if (hEventSource != NULL) { 
			WORD event_type; 
 
			strings = msg;		// write message without timestamp 
 
			event_type = (type == MSGTYPE_ALLOW) ? EVENTLOG_AUDIT_SUCCESS : 
				(type == MSGTYPE_DENY ? EVENTLOG_AUDIT_FAILURE : EVENTLOG_ERROR_TYPE); 
 
            if (!ReportEvent(hEventSource, // handle of event source 
					event_type, 
					0,                    // event category 
					MSG,                  // event ID 
					NULL,                 // current user's SID 
					1,                    // strings in lpszStrings 
					0,                    // no bytes of raw data 
					&strings,             // array of error strings 
					NULL)) {              // no raw data 
				 
#ifdef _DEBUG 
				MessageBeep(0); 
				OutputDebugString("log_msg: ReportEvent\n"); 
#endif 
			} 
 
            DeregisterEventSource(hEventSource); 
		} 
	} else { 
 
		// write message to log 
 
		time_t tv; 
		struct tm *tm; 
		char tm_msg[1024]; 
 
		time(&tv); 
		tm = localtime(&tv); 
		if (tm == NULL) { 
#ifdef _DEBUG 
			MessageBeep(0); 
			OutputDebugString("log_msg: localtime\n"); 
#endif 
			return; 
		} 
 
		if (g_logfile == NULL || tm->tm_mday != file_day) { 
			char fname[MAX_PATH]; 
 
			if (g_logfile != NULL) { 
				// wow! we're working at midnight! change log file! 
				fprintf(g_logfile, "--- midnight ---\n"); 
				fclose(g_logfile); 
				g_logfile = NULL; 
			} 
 
			file_day = tm->tm_mday; 
			 
			// open logfile 
			 
			if (_snprintf(fname, sizeof(fname), 
				"%s\\system32\\LogFiles\\tdi_fw\\%04d%02d%02d.log", 
				getenv("SystemRoot"), 
				tm->tm_year + 1900, 
				tm->tm_mon + 1, 
				tm->tm_mday) == -1) { 
 
#ifdef _DEBUG 
				MessageBeep(0); 
				OutputDebugString("log_msg: _snprintf overflow!\n"); 
#endif 
				return; 
			} 
 
			g_logfile = fopen(fname, "a"); 
			if (g_logfile == NULL) { 
#ifdef _DEBUG 
				MessageBeep(0); 
				OutputDebugString("log_msg: fopen error!\n"); 
#endif 
				return; 
			} 
 
			fprintf(g_logfile, "--- begin ---\n"); 
		} 
 
		if (_snprintf(tm_msg, sizeof(tm_msg), 
			"%010u %02d:%02d:%02d\t%s", 
			event_num++, tm->tm_hour, tm->tm_min, tm->tm_sec, msg) == -1) 
			tm_msg[sizeof(tm_msg) - 1] = '\0'; 
 
		// write it to log file 
		fprintf(g_logfile, "%s\n", tm_msg); 
		fflush(g_logfile); 
	 
	} 
} 
 
#define MAX_SECTION_SIZE	(16 * 1024) 
 
BOOL 
load_config(const char *config) 
{ 
	char *section, *p; 
	DWORD n, pids[100]; 
	BOOL result = FALSE; 
	int line, chain; 
 
	if (!read_config(config)) 
		return FALSE; 
 
	// parse & add rules default and process-related 
 
	section = (char *)malloc(MAX_SECTION_SIZE); 
	if (section == NULL) { 
		liberr("malloc"); 
		goto done; 
	} 
 
	GetPrivateProfileSection("_main_", section, MAX_SECTION_SIZE, config); 
	 
	// get lines 
	chain = 0; 
	for (p = section, line = 1; *p != '\0';p += strlen(p) + 1, line++) { 
		char *p2; 
 
		if (*p == ';') 
			continue;		// comment 
 
		p2 = strchr(p, '='); 
		if (p2 == NULL) { 
			error("%s:[_main_]:%d: invalid line format (no '=' character)"); 
			continue; 
		} 
 
		if (chain >= MAX_CHAINS_COUNT) { 
			error("%s:[_main_]:%d: too many rules lines!"); 
			break; 
		} 
 
		*p2 = '\0';		// temporary kill '=' 
		add_rules_name(p, config, chain); 
		*p2 = '=';		// recover '=' to make strlen(p) works right 
 
		chain++; 
	} 
 
	// try to get names for existing processes 
	if (EnumProcesses(pids, sizeof(pids), &n)) { 
		DWORD i; 
 
		n /= sizeof(DWORD); 
 
		for (i = 0; i < n; i++) { 
			char pname[MAX_PATH]; 
			if (get_pname_by_pid(pids[i], pname + sizeof(DWORD), sizeof(pname) - sizeof(DWORD))) { 
				// send information to driver about pid and pname 
				 
				DWORD nn; 
				 
				*(DWORD *)pname = pids[i]; 
 
				if (!DeviceIoControl(g_device, IOCTL_CMD_SETPNAME, pname, 
					sizeof(DWORD) + strlen(pname + sizeof(DWORD)) + 1, 
					NULL, 0, &nn, NULL)) 
					winerr("DeviceIoControl"); 
			} 
		} 
	} 
 
	result = TRUE; 
done: 
	free(section); 
	return result; 
} 
 
BOOL 
read_config(const char *config) 
{ 
	static char config_name[] = "_config_"; 
	static char signature_name[] = "_signature_"; 
	static char signature_value[] = "$tdi_fw$"; 
 
	char buf[100]; 
 
	// first, check config file signature 
	GetPrivateProfileString(signature_name, signature_name, "", buf, sizeof(buf), config); 
	if (strcmp(buf, signature_value) != 0) { 
		error("\"%s\": invalid configuration file", config); 
		return FALSE; 
	} 
 
	// get g_eventlog_xxx values 
 
	g_eventlog_allow = GetPrivateProfileInt(config_name, "eventlog_allow", FALSE, config); 
	g_eventlog_deny = GetPrivateProfileInt(config_name, "eventlog_deny", FALSE, config); 
	g_eventlog_error = GetPrivateProfileInt(config_name, "eventlog_error", FALSE, config); 
 
	// get other values 
 
	g_rules_resolve_addr = GetPrivateProfileInt(config_name, "rules_resolve_addr", FALSE, config); 
	g_logs_resolve_addr = GetPrivateProfileInt(config_name, "logs_resolve_addr", FALSE, config); 
 
	return TRUE; 
} 
 
BOOL 
add_rules_name(const char *main_name, const char *config, int chain) 
{ 
	char buf[1024], *p, *p2, *section = NULL; 
	BOOL result = FALSE; 
	DWORD n; 
 
	section = (char *)malloc(MAX_SECTION_SIZE); 
	if (section == NULL) { 
		liberr("malloc"); 
		goto done; 
	} 
 
	/* 1. get ruleset string */ 
 
	GetPrivateProfileString("_main_", main_name, "", buf, sizeof(buf), config); 
 
	if (*buf == '\0') 
		goto done;		// no rules 
 
	// reset all rules for chain 
	if (!DeviceIoControl(g_device, IOCTL_CMD_CLEARCHAIN, &chain, sizeof(chain), 
		NULL, 0, &n, NULL)) { 
		winerr("DeviceIoControl"); 
		goto done; 
	} 
 
	if (chain != 0) { 
		// set chain name 
		int len = sizeof(int) + strlen(main_name) + 1; 
		char *data = (char *)malloc(len); 
		if (data == NULL) { 
			liberr("malloc"); 
			goto done; 
		} 
 
		*(int *)data = chain; 
		strcpy(data + sizeof(int), main_name); 
		 
		if (!DeviceIoControl(g_device, IOCTL_CMD_SETCHAINPNAME, data, len, 
			NULL, 0, &n, NULL)) { 
			winerr("DeviceIoControl"); 
			free(data); 
			goto done; 
		} 
		free(data); 
	} 
 
	/* 2. for each word in main_name string */ 
 
	p = buf; 
	 
	while (p != NULL) { 
 
		p2 = strchr(p, ' '); 
		if (p2 != NULL) { 
			while (*p2 == ' ') 
				*(p2++) = '\0'; 
		} 
		 
		if (*p != '\0') { 
			// get section by name in p 
			GetPrivateProfileSection(p, section, MAX_SECTION_SIZE, config); 
			if (*section == '\0') 
				error("\"%s\": unexistant or empty section %s", config, p); 
			else 
				add_rules(section, p, chain); 
		} 
 
		p = p2; 
	} 
 
	result = TRUE; 
 
done: 
	free(section); 
	return result; 
} 
 
void 
add_rules(char *buf, const char *name, int chain) 
{ 
	char *p, *p2; 
	int n_str; 
 
	for (p = buf, n_str = 1; *p != '\0'; p = p2, n_str++) { 
		struct flt_rule rule; 
		DWORD n; 
 
		p2 = p + strlen(p) + 1; 
 
		if (*buf == ';' || *buf == '\0') 
			continue;		// empty line or comment 
 
		memset(&rule, 0, sizeof(rule)); 
		 
		if (!parse_rule(p, &rule)) { 
			error("Error in line #%d of section [%s]", n_str, name); 
			continue; 
		} 
 
		// set chain 
		rule.chain = chain; 
 
		// append rule 
		if (!DeviceIoControl(g_device, IOCTL_CMD_APPENDRULE, &rule, sizeof(rule), 
				NULL, 0, &n, NULL)) { 
			winerr("start: DeviceIoControl"); 
			return; 
		} 
	} 
} 
 
BOOL 
get_pname_by_pid(u_long pid, char *buf, int buf_size) 
{ 
	BOOL result; 
	HANDLE h_process; 
	HMODULE h_module; 
	DWORD n; 
 
	// try to resolve pid to pname 
	 
	h_process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); 
	if (h_process != NULL) 
		EnumProcessModules(h_process, &h_module, sizeof(h_module), &n); 
 
	// on error write pid 
	if (h_module == NULL || 
		GetModuleFileNameEx(h_process, h_module, buf, buf_size) == 0) { 
 
		// for "System" process last error value is: 
		// * ERROR_PARTIAL_COPY (on 2k) 
		// * ERROR_NOACCESS (on NT4) 
		// ??? on other (TODO: think about another way) 
		if (GetLastError() == ERROR_PARTIAL_COPY || 
			GetLastError() == ERROR_NOACCESS) { 
			 
			strncpy(buf, "System", buf_size); 
			buf[buf_size - 1] = '\0'; 
			 
			result = TRUE; 
		 
		} else { 
			*buf = '\0'; 
			result = FALSE; 
		} 
 
	} else { 
		if (strchr(buf, '~') != NULL) {		// XXX is it a right way? 
			// try to convert long name to short name 
			char long_name[MAX_PATH]; 
			 
			my_GetLongPathName(buf, long_name, sizeof(long_name)); 
				 
			strncpy(buf, long_name, buf_size - 1); 
			buf[buf_size - 1] = '\0'; 
		} 
 
		result = TRUE; 
	} 
 
	return result; 
} 
 
void 
prepare_addr(char *buf, int size, u_long addr) 
{ 
	if (g_logs_resolve_addr) { 
		// TODO: doesn't work correctly (maybe remove it?) 
		struct hostent *he; 
 
		if (addr == INADDR_ANY) { 
			strcpy(buf, "ANY"); 
			return; 
		} 
 
		// try to resolve addr 
		he = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); 
		if (he != NULL) { 
			// set name in buffer (and reserve space for ) 
			size_t len = size - sizeof(""); 
			strncpy(buf, he->h_name, len); 
			buf[len - 1] = '\0'; 
		} else 
			buf[0] = '\0'; 
 
		// add  
		strcat(buf, "["); 
		strcat(buf, inet_ntoa(*(struct in_addr *)&addr)); 
		strcat(buf, "]"); 
	} else 
		strcpy(buf, inet_ntoa(*(struct in_addr *)&addr)); 
} 
 
// GetLongPathName is in Windows 2000 only so make it for NT4 
void 
my_GetLongPathName(LPCSTR lpszShortPath, LPSTR lpszLongPath, DWORD cchBuffer) 
{ 
	LPSTR p_l; 
	LPCSTR p_s; 
 
	lpszLongPath[0] = lpszShortPath[0]; 
	lpszLongPath[1] = lpszShortPath[1]; 
	lpszLongPath[2] = lpszShortPath[2]; 
	lpszLongPath[3] = '\0'; 
	 
	p_l = &lpszLongPath[3]; 
	p_s = &lpszShortPath[3]; 
 
	for (;;) { 
		LPSTR p_s2; 
		HANDLE search; 
		WIN32_FIND_DATA ffd; 
		size_t len; 
		BOOL overflow; 
 
		p_s2 = strchr(p_s, '\\'); 
 
		if (p_s2 != NULL) 
			len = p_s2 - p_s; 
		else 
			len = strlen(p_s); 
 
		// check the buffer 
		if (len + (p_l - lpszLongPath) >= cchBuffer) { 
			// buffer overflow: copy and exit 
			len = cchBuffer - (p_l - lpszLongPath) - 1; 
			overflow = TRUE; 
		} else 
			overflow = FALSE; 
		 
		memcpy(p_l, p_s, len); 
		p_l[len] = '\0'; 
	 
		if (overflow) 
			break; 
 
		search = FindFirstFile(lpszLongPath, &ffd); 
		if (search != INVALID_HANDLE_VALUE) { 
 
			len = strlen(ffd.cFileName); 
			if (p_s2 != NULL) 
				len++; 
 
			// check the buffer 
			if (len + (p_l - lpszLongPath) >= cchBuffer) { 
				// buffer overflow: copy and exit 
				len = cchBuffer - (p_l - lpszLongPath) - 1; 
				p_s2 = NULL; 
			} 
 
			memcpy(p_l, ffd.cFileName, len); 
			 
			if (p_s2 != NULL) { 
				p_l[len - 1] = '\\'; 
				p_l[len] = '\0'; 
			} else 
				p_l[len] = '\0'; 
 
			FindClose(search); 
		} 
 
		if (p_s2 == NULL) 
			break; 
 
		p_l += strlen(p_l); 
		p_s = p_s2 + 1; 
	} 
 
	// ignore status 
}