Exemple #1
0
// Create an adapter list item
SU_ADAPTER_LIST *SuAdapterInfoToAdapterList(SL_ADAPTER_INFO *info)
{
    SU_ADAPTER_LIST t;
    char tmp[MAX_SIZE];
    // Validate arguments
    if (info == NULL)
    {
        return NULL;
    }

    Zero(&t, sizeof(t));
    Copy(&t.Info, info, sizeof(SL_ADAPTER_INFO));

    UniToStr(tmp, sizeof(tmp), info->AdapterId);
    if (IsEmptyStr(tmp) || IsEmptyStr(info->FriendlyName) || StartWith(tmp, SL_ADAPTER_ID_PREFIX) == false)
    {
        // Name is invalid
        return NULL;
    }

    // GUID (Part after "SELOW_A_" prefix)
    StrCpy(t.Guid, sizeof(t.Guid), tmp + StrLen(SL_ADAPTER_ID_PREFIX));

    // Name
    StrCpy(t.Name, sizeof(t.Name), tmp);

    // Key for sort
    if (GetClassRegKeyWin32(t.SortKey, sizeof(t.SortKey), tmp, sizeof(tmp), t.Guid) == false)
    {
        // Can not be found
        return NULL;
    }

    return Clone(&t, sizeof(t));
}
Exemple #2
0
// Get the string type value
bool CfgGetStr(FOLDER *f, char *name, char *str, UINT size)
{
	wchar_t *tmp;
	UINT tmp_size;
	// Validate arguments
	if (f == NULL || name == NULL || str == NULL)
	{
		return false;
	}

	str[0] = 0;

	// Get unicode string temporarily
	tmp_size = size * 4 + 10; // Just to make sure, a quantity of this amount is secured.
	tmp = Malloc(tmp_size);
	if (CfgGetUniStr(f, name, tmp, tmp_size) == false)
	{
		// Failure
		Free(tmp);
		return false;
	}

	// Copy to the ANSI string
	UniToStr(str, size, tmp);
	Free(tmp);

	return true;
}
Exemple #3
0
// Save the Unicode cache
void SaveUnicodeCache(wchar_t *strfilename, UINT strfilesize, UCHAR *hash)
{
	UNICODE_CACHE c;
	BUF *b;
	UINT i;
	IO *io;
	wchar_t name[MAX_PATH];
	UCHAR binhash[MD5_SIZE];
	// Validate arguments
	if (strfilename == NULL || hash == NULL)
	{
		return;
	}

	Zero(&c, sizeof(c));
	UniToStr(c.StrFileName, sizeof(c.StrFileName), strfilename);
	c.StrFileSize = strfilesize;
	GetMachineName(c.MachineName, sizeof(c.MachineName));
	c.OsType = GetOsInfo()->OsType;
	Copy(c.hash, hash, MD5_SIZE);

#ifdef	OS_UNIX
	GetCurrentCharSet(c.CharSet, sizeof(c.CharSet));
#else	// OS_UNIX
	{
		UINT id = MsGetThreadLocale();
		Copy(c.CharSet, &id, sizeof(id));
	}
#endif	// OS_UNIX

	b = NewBuf();
	WriteBuf(b, &c, sizeof(c));

	WriteBufInt(b, LIST_NUM(TableList));
	for (i = 0;i < LIST_NUM(TableList);i++)
	{
		TABLE *t = LIST_DATA(TableList, i);
		WriteBufInt(b, StrLen(t->name));
		WriteBuf(b, t->name, StrLen(t->name));
		WriteBufInt(b, StrLen(t->str));
		WriteBuf(b, t->str, StrLen(t->str));
		WriteBufInt(b, UniStrLen(t->unistr));
		WriteBuf(b, t->unistr, UniStrLen(t->unistr) * sizeof(wchar_t));
	}

	Hash(binhash, b->Buf, b->Size, false);
	WriteBuf(b, binhash, MD5_SIZE);

	GenerateUnicodeCacheFileName(name, sizeof(name), strfilename, strfilesize, hash);

	io = FileCreateW(name);
	if (io != NULL)
	{
		SeekBuf(b, 0, 0);
		BufToFile(io, b);
		FileClose(io);
	}

	FreeBuf(b);
}
Exemple #4
0
// Get a line from standard input
bool GetLine(char *str, UINT size)
{
	bool ret;
	wchar_t *unistr;
	UINT unistr_size = (size + 1) * sizeof(wchar_t);

	unistr = Malloc(unistr_size);

	ret = UniGetLine(unistr, unistr_size);

	UniToStr(str, size, unistr);

	Free(unistr);

	return ret;
}
Exemple #5
0
// Format the string (argument list)
void FormatArgs(char *buf, UINT size, char *fmt, va_list args)
{
	wchar_t *tag;
	wchar_t *ret;
	// Validate arguments
	if (buf == NULL || fmt == NULL)
	{
		return;
	}

	tag = CopyStrToUni(fmt);
	ret = InternalFormatArgs(tag, args, true);

	UniToStr(buf, size, ret);
	Free(ret);
	Free(tag);
}
Exemple #6
0
// Create a user name attribute for Radius
BUF *RadiusCreateUserName(wchar_t *username)
{
	BUF *b;
	UCHAR code, size;
	UCHAR utf8[254];
	// Validate arguments
	if (username == NULL)
	{
		return NULL;
	}

	// Convert the user name to a Unicode string
	UniToStr(utf8, sizeof(utf8), username);
	utf8[253] = 0;

	b = NewBuf();
	code = 1;
	size = 2 + (UCHAR)StrLen(utf8);
	WriteBuf(b, &code, 1);
	WriteBuf(b, &size, 1);
	WriteBuf(b, utf8, StrLen(utf8));

	return b;
}
LIST *GetEthAdapterListInternal()
{
	LIST *o;
	LIST *ret;
	UINT size;
	char *buf;
	UINT i, j;
	char *qos_tag = " (Microsoft's Packet Scheduler)";
	SU *su = NULL;
	LIST *su_adapter_list = NULL;

	// Try to use SeLow
	if (enable_selow)
	{
		su = SuInit();
	}

	o = NewListFast(CompareWpAdapter);

	size = 200000;
	buf = ZeroMalloc(size);

	// Try to enumerate with SeLow
	if (su != NULL)
	{
		su_adapter_list = SuGetAdapterList(su);

		if (su_adapter_list == NULL)
		{
			// Fail to enumerate
			SuFree(su);
			su = NULL;
			//WHERE;
			is_using_selow = false;
		}
		else
		{
			//WHERE;
			is_using_selow = true;
		}
	}
	else
	{
		is_using_selow = false;
	}

	if (su_adapter_list != NULL)
	{
		// If 1 or more adapters are enumerated by SeLow, create adapter list object
		UINT i;

		for (i = 0;i < LIST_NUM(su_adapter_list);i++)
		{
			SU_ADAPTER_LIST *t = LIST_DATA(su_adapter_list, i);
			WP_ADAPTER *a = ZeroMalloc(sizeof(WP_ADAPTER));

			StrCpy(a->Name, sizeof(a->Name), t->Name);
			StrCpy(a->Guid, sizeof(a->Guid), t->Guid);
			StrCpy(a->Title, sizeof(a->Title), t->Info.FriendlyName);

			TrimCrlf(a->Title);
			Trim(a->Title);
			TrimCrlf(a->Title);
			Trim(a->Title);

			if (EndWith(a->Title, qos_tag))
			{
				a->Title[StrLen(a->Title) - StrLen(qos_tag)] = 0;
				TrimCrlf(a->Title);
				Trim(a->Title);
				TrimCrlf(a->Title);
				Trim(a->Title);
			}

			Add(o, a);
		}
	}
	else
	{
		// When SeLow is not used, create adapter list with SEE or WinPcap
		if (wp->PacketGetAdapterNames(buf, &size) == false)
		{
			Free(buf);
			return o;
		}

		i = 0;

		if (OS_IS_WINDOWS_NT(GetOsInfo()->OsType))
		{
			// Windows NT
			if (size >= 2 && buf[0] != 0 && buf[1] != 0)
			{
				goto ANSI_STR;
			}

			while (true)
			{
				wchar_t tmp[MAX_SIZE];
				WP_ADAPTER *a;
				UniStrCpy(tmp, sizeof(tmp), L"");

				if (*((wchar_t *)(&buf[i])) == 0)
				{
					i += sizeof(wchar_t);
					break;
				}

				for (;*((wchar_t *)(&buf[i])) != 0;i += sizeof(wchar_t))
				{
					wchar_t str[2];
					str[0] = *((wchar_t *)(&buf[i]));
					str[1] = 0;
					UniStrCat(tmp, sizeof(tmp), str);
				}

				i += sizeof(wchar_t);

				a = ZeroMalloc(sizeof(WP_ADAPTER));
				UniToStr(a->Name, sizeof(a->Name), tmp);

				Add(o, a);
			}
		}
		else
		{
			// Windows 9x
ANSI_STR:
			while (true)
			{
				char tmp[MAX_SIZE];
				WP_ADAPTER *a;
				StrCpy(tmp, sizeof(tmp), "");

				if (*((char *)(&buf[i])) == 0)
				{
					i += sizeof(char);
					break;
				}

				for (;*((char *)(&buf[i])) != 0;i += sizeof(char))
				{
					char str[2];
					str[0] = *((char *)(&buf[i]));
					str[1] = 0;
					StrCat(tmp, sizeof(tmp), str);
				}

				i += sizeof(char);

				a = ZeroMalloc(sizeof(WP_ADAPTER));
				StrCpy(a->Name, sizeof(a->Name), tmp);

				Add(o, a);
			}
		}

		for (j = 0;j < LIST_NUM(o);j++)
		{
			WP_ADAPTER *a = LIST_DATA(o, j);

			StrCpy(a->Title, sizeof(a->Title), &buf[i]);
			i += StrSize(a->Title);

			// If device description is "Unknown" in Win9x, skip 1 byte
			if (OS_IS_WINDOWS_9X(GetOsInfo()->OsType))
			{
				if (StrCmp(a->Title, "Unknown") == 0)
				{
					if (buf[i] == 0)
					{
						i+=sizeof(char);
					}
				}
			}

			TrimCrlf(a->Title);
			Trim(a->Title);
			TrimCrlf(a->Title);
			Trim(a->Title);

			if (EndWith(a->Title, qos_tag))
			{
				a->Title[StrLen(a->Title) - StrLen(qos_tag)] = 0;
				TrimCrlf(a->Title);
				Trim(a->Title);
				TrimCrlf(a->Title);
				Trim(a->Title);
			}
		}
	}

	for (j = 0;j < LIST_NUM(o);j++)
	{
		// Extract GUID
		WP_ADAPTER *a = LIST_DATA(o, j);

		if (IsEmptyStr(a->Guid))
		{
			StrCpy(a->Guid, sizeof(a->Guid), a->Name);
			ReplaceStr(a->Guid, sizeof(a->Guid), a->Guid, "\\Device\\SEE_", "");
			ReplaceStr(a->Guid, sizeof(a->Guid), a->Guid, "\\Device\\NPF_", "");
			ReplaceStr(a->Guid, sizeof(a->Guid), a->Guid, "\\Device\\PCD_", "");
		}
	}

	// Sort
	if (su_adapter_list != NULL)
	{
		// Since adapter list made by SeLow is already sorted, don't sort here
		Sort(o);
	}

	ret = NewListFast(CompareWpAdapter);

	for (i = 0;i < LIST_NUM(o);i++)
	{
		WP_ADAPTER *a = LIST_DATA(o, i);
		ADAPTER *ad;
		bool is_ethernet = false;
		bool ok = false;

		if (SearchStrEx(a->Title, "ppp", 0, false) != INFINITE ||
			SearchStrEx(a->Title, "wan", 0, false) != INFINITE ||
			SearchStrEx(a->Title, "dialup", 0, false) != INFINITE ||
			SearchStrEx(a->Title, "pptp", 0, false) != INFINITE ||
			SearchStrEx(a->Title, "telepho", 0, false) != INFINITE ||
			SearchStrEx(a->Title, "modem", 0, false) != INFINITE ||
			SearchStrEx(a->Title, "ras", 0, false) != INFINITE)
		{
			Free(a);
			continue;
		}

		// Determine whether the adapter type is Ethernet
		if (su == NULL)
		{
			// Determine with See
			ad = wp->PacketOpenAdapter(a->Name);
			if (ad != NULL)
			{
				NetType type;
				if (wp->PacketGetNetType(ad, &type))
				{
					if (type.LinkType == 0)
					{
						is_ethernet = true;
					}
				}

				wp->PacketCloseAdapter(ad);
			}
		}
		else
		{
			// In using SeLow, all devices should be Ethernet device
			is_ethernet = true;
		}

		if (is_ethernet)
		{
			// Add only Ethernet device
			char tmp[MAX_SIZE];
			UINT k;

			StrCpy(tmp, sizeof(tmp), a->Title);

			for (k = 0;;k++)
			{
				if (k == 0)
				{
					StrCpy(tmp, sizeof(tmp), a->Title);
				}
				else
				{
					Format(tmp, sizeof(tmp), "%s (%u)", a->Title, k + 1);
				}

				ok = true;
				for (j = 0;j < LIST_NUM(ret);j++)
				{
					WP_ADAPTER *aa = LIST_DATA(ret, j);
					if (StrCmpi(aa->Title, tmp) == 0)
					{
						ok = false;
					}
				}

				if (ok)
				{
					break;
				}
			}

			StrCpy(a->Title, sizeof(a->Title), tmp);
			a->Id = Win32EthGenIdFromGuid(a->Guid);
			Add(ret, a);
		}

		if (ok == false)
		{
			Free(a);
		}
	}

	Free(buf);

	Sort(ret);

	ReleaseList(o);

	if (su != NULL)
	{
		SuFreeAdapterList(su_adapter_list);

		SuFree(su);
	}

	return ret;
}
Exemple #8
0
// Plaintext password authentication of user
bool SamAuthUserByPlainPassword(CONNECTION *c, HUB *hub, char *username, char *password, bool ast, UCHAR *mschap_v2_server_response_20, RADIUS_LOGIN_OPTION *opt)
{
	bool b = false;
	wchar_t *name = NULL;
	bool auth_by_nt = false;
	HUB *h;
	// Validate arguments
	if (hub == NULL || c == NULL || username == NULL)
	{
		return false;
	}

	if (GetGlobalServerFlag(GSF_DISABLE_RADIUS_AUTH) != 0)
	{
		return false;
	}

	h = hub;

	AddRef(h->ref);

	// Get the user name on authentication system
	AcLock(hub);
	{
		USER *u;
		u = AcGetUser(hub, ast == false ? username : "******");
		if (u)
		{
			Lock(u->lock);
			{
				if (u->AuthType == AUTHTYPE_RADIUS)
				{
					// Radius authentication
					AUTHRADIUS *auth = (AUTHRADIUS *)u->AuthData;
					if (ast || auth->RadiusUsername == NULL || UniStrLen(auth->RadiusUsername) == 0)
					{
						name = CopyStrToUni(username);
					}
					else
					{
						name = CopyUniStr(auth->RadiusUsername);
					}
					auth_by_nt = false;
				}
				else if (u->AuthType == AUTHTYPE_NT)
				{
					// NT authentication
					AUTHNT *auth = (AUTHNT *)u->AuthData;
					if (ast || auth->NtUsername == NULL || UniStrLen(auth->NtUsername) == 0)
					{
						name = CopyStrToUni(username);
					}
					else
					{
						name = CopyUniStr(auth->NtUsername);
					}
					auth_by_nt = true;
				}
			}
			Unlock(u->lock);
			ReleaseUser(u);
		}
	}
	AcUnlock(hub);

	if (name != NULL)
	{
		if (auth_by_nt == false)
		{
			// Radius authentication
			char radius_server_addr[MAX_SIZE];
			UINT radius_server_port;
			char radius_secret[MAX_SIZE];
			char suffix_filter[MAX_SIZE];
			wchar_t suffix_filter_w[MAX_SIZE];
			UINT interval;

			Zero(suffix_filter, sizeof(suffix_filter));
			Zero(suffix_filter_w, sizeof(suffix_filter_w));

			// Get the Radius server information
			if (GetRadiusServerEx2(hub, radius_server_addr, sizeof(radius_server_addr), &radius_server_port, radius_secret, sizeof(radius_secret), &interval, suffix_filter, sizeof(suffix_filter)))
			{
				Unlock(hub->lock);

				StrToUni(suffix_filter_w, sizeof(suffix_filter_w), suffix_filter);

				if (UniIsEmptyStr(suffix_filter_w) || UniEndWith(name, suffix_filter_w))
				{
					// Attempt to login
					b = RadiusLogin(c, radius_server_addr, radius_server_port,
						radius_secret, StrLen(radius_secret),
						name, password, interval, mschap_v2_server_response_20, opt);
				}

				Lock(hub->lock);
			}
			else
			{
				HLog(hub, "LH_NO_RADIUS_SETTING", name);
			}
		}
		else
		{
			// NT authentication (Not available for non-Win32)
#ifdef	OS_WIN32
			IPC_MSCHAP_V2_AUTHINFO mschap;
			Unlock(hub->lock);

			if (ParseAndExtractMsChapV2InfoFromPassword(&mschap, password) == false)
			{
				// Plaintext password authentication
				b = MsCheckLogon(name, password);
			}
			else
			{
				UCHAR challenge8[8];
				UCHAR nt_pw_hash_hash[16];
				char nt_name[MAX_SIZE];

				UniToStr(nt_name, sizeof(nt_name), name);

				// MS-CHAPv2 authentication
				MsChapV2_GenerateChallenge8(challenge8, mschap.MsChapV2_ClientChallenge,
					mschap.MsChapV2_ServerChallenge,
					mschap.MsChapV2_PPPUsername);

				Debug("MsChapV2_PPPUsername = %s, nt_name = %s\n", mschap.MsChapV2_PPPUsername, nt_name);

				b = MsPerformMsChapV2AuthByLsa(nt_name, challenge8, mschap.MsChapV2_ClientResponse, nt_pw_hash_hash);

				if (b)
				{
					if (mschap_v2_server_response_20 != NULL)
					{
						MsChapV2Server_GenerateResponse(mschap_v2_server_response_20, nt_pw_hash_hash,
							mschap.MsChapV2_ClientResponse, challenge8);
					}
				}
			}

			Lock(hub->lock);
#else	// OS_WIN32
			// Nothing to do other than Win32
#endif	// OS_WIN32
		}

		// Memory release
		Free(name);
	}

	ReleaseHub(h);

	return b;
}
Exemple #9
0
// Interpret a line
TABLE *ParseTableLine(char *line, char *prefix, UINT prefix_size, LIST *replace_list)
{
	UINT i, len;
	UINT len_name;
	UINT string_start;
	char *name;
	char *name2;
	UINT name2_size;
	wchar_t *unistr;
	char *str;
	UINT unistr_size, str_size;
	TABLE *t;
	// Validate arguments
	if (line == NULL || prefix == NULL)
	{
		return NULL;
	}
	TrimLeft(line);

	// No line
	len = StrLen(line);
	if (len == 0)
	{
		return NULL;
	}

	// Comment
	if (line[0] == '#' || (line[0] == '/' && line[1] == '/'))
	{
		return NULL;
	}

	// Search to the end position of the name
	len_name = 0;
	for (i = 0;;i++)
	{
		if (line[i] == 0)
		{
			// There is only one token
			return NULL;
		}
		if (line[i] == ' ' || line[i] == '\t')
		{
			break;
		}
		len_name++;
	}

	name = Malloc(len_name + 1);
	StrCpy(name, len_name + 1, line);

	string_start = len_name;
	for (i = len_name;i < len;i++)
	{
		if (line[i] != ' ' && line[i] != '\t')
		{
			break;
		}
		string_start++;
	}
	if (i == len)
	{
		Free(name);
		return NULL;
	}

	// Unescape
	UnescapeStr(&line[string_start]);

	// Convert to Unicode
	unistr_size = CalcUtf8ToUni(&line[string_start], StrLen(&line[string_start]));
	if (unistr_size == 0)
	{
		Free(name);
		return NULL;
	}
	unistr = Malloc(unistr_size);
	Utf8ToUni(unistr, unistr_size, &line[string_start], StrLen(&line[string_start]));

	if (UniInChar(unistr, L'$'))
	{
		// Replace the replacement string
		wchar_t *tmp;
		UINT tmp_size = (UniStrSize(unistr) + 1024) * 2;
		UINT i;

		tmp = Malloc(tmp_size);

		UniStrCpy(tmp, tmp_size, unistr);

		for (i = 0; i < LIST_NUM(replace_list);i++)
		{
			TABLE *r = LIST_DATA(replace_list, i);

			UniReplaceStrEx(tmp, tmp_size, tmp, (wchar_t *)r->name, r->unistr, false);
		}

		unistr = CopyUniStr(tmp);

		Free(tmp);
	}

	// Convert to ANSI
	str_size = CalcUniToStr(unistr);
	if (str_size == 0)
	{
		str_size = 1;
		str = Malloc(1);
		str[0] = 0;
	}
	else
	{
		str = Malloc(str_size);
		UniToStr(str, str_size, unistr);
	}

	if (StrCmpi(name, "PREFIX") == 0)
	{
		// Prefix is specified
		StrCpy(prefix, prefix_size, str);
		Trim(prefix);

		if (StrCmpi(prefix, "$") == 0 || StrCmpi(prefix, "NULL") == 0)
		{
			prefix[0] = 0;
		}

		Free(name);
		Free(str);
		Free(unistr);

		return NULL;
	}

	name2_size = StrLen(name) + StrLen(prefix) + 2;
	name2 = ZeroMalloc(name2_size);

	if (prefix[0] != 0)
	{
		StrCat(name2, name2_size, prefix);
		StrCat(name2, name2_size, "@");
	}

	StrCat(name2, name2_size, name);

	Free(name);

	// Create a TABLE
	t = Malloc(sizeof(TABLE));
	StrUpper(name2);
	t->name = name2;
	t->str = str;
	t->unistr = unistr;

	return t;
}
Exemple #10
0
// Reading the Unicode cache
bool LoadUnicodeCache(wchar_t *strfilename, UINT strfilesize, UCHAR *hash)
{
	UNICODE_CACHE c, t;
	BUF *b;
	UINT i, num;
	IO *io;
	wchar_t name[MAX_PATH];
	UCHAR binhash[MD5_SIZE];
	UCHAR binhash_2[MD5_SIZE];
	// Validate arguments
	if (strfilename == NULL || hash == NULL)
	{
		return false;
	}

	GenerateUnicodeCacheFileName(name, sizeof(name), strfilename, strfilesize, hash);

	io = FileOpenW(name, false);
	if (io == NULL)
	{
		return false;
	}

	b = FileToBuf(io);
	if (b == NULL)
	{
		FileClose(io);
		return false;
	}

	SeekBuf(b, 0, 0);
	FileClose(io);

	Hash(binhash, b->Buf, b->Size >= MD5_SIZE ? (b->Size - MD5_SIZE) : 0, false);
	Copy(binhash_2, ((UCHAR *)b->Buf) + (b->Size >= MD5_SIZE ? (b->Size - MD5_SIZE) : 0), MD5_SIZE);
	if (Cmp(binhash, binhash_2, MD5_SIZE) != 0)
	{
		FreeBuf(b);
		return false;
	}

	Zero(&c, sizeof(c));
	UniToStr(c.StrFileName, sizeof(c.StrFileName), strfilename);
	c.StrFileSize = strfilesize;
	DisableNetworkNameCache();
	GetMachineName(c.MachineName, sizeof(c.MachineName));
	EnableNetworkNameCache();
	c.OsType = GetOsInfo()->OsType;
	Copy(c.hash, hash, MD5_SIZE);

#ifdef	OS_UNIX
	GetCurrentCharSet(c.CharSet, sizeof(c.CharSet));
#else	// OS_UNIX
	{
		UINT id = MsGetThreadLocale();
		Copy(c.CharSet, &id, sizeof(id));
	}
#endif	// OS_UNIX

	Zero(&t, sizeof(t));
	ReadBuf(b, &t, sizeof(t));

	if (Cmp(&c, &t, sizeof(UNICODE_CACHE)) != 0)
	{
		FreeBuf(b);
		return false;
	}

	num = ReadBufInt(b);

	FreeTable();
	TableList = NewList(CmpTableName);

	for (i = 0;i < num;i++)
	{
		UINT len;
		TABLE *t = ZeroMalloc(sizeof(TABLE));

		len = ReadBufInt(b);
		t->name = ZeroMalloc(len + 1);
		ReadBuf(b, t->name, len);

		len = ReadBufInt(b);
		t->str = ZeroMalloc(len + 1);
		ReadBuf(b, t->str, len);

		len = ReadBufInt(b);
		t->unistr = ZeroMalloc((len + 1) * sizeof(wchar_t));
		ReadBuf(b, t->unistr, len * sizeof(wchar_t));

		Add(TableList, t);
	}

	FreeBuf(b);

	Sort(TableList);

	return true;
}