diff --git a/dll/win32/ifmon/ip.c b/dll/win32/ifmon/ip.c index 9984e7afa86..8ed9c98554d 100644 --- a/dll/win32/ifmon/ip.c +++ b/dll/win32/ifmon/ip.c @@ -31,15 +31,19 @@ #define REGISTER_PRIMARY 2 #define REGISTER_BOTH 3 +#define ADDRESS_ALL 1 + static FN_HANDLE_CMD IpAddAddress; static FN_HANDLE_CMD IpAddDns; +static FN_HANDLE_CMD IpDeleteAddress; static FN_HANDLE_CMD IpDeleteArpCache; +static FN_HANDLE_CMD IpDeleteDns; static FN_HANDLE_CMD IpSetAddress; static FN_HANDLE_CMD IpSetDns; static FN_HANDLE_CMD IpShowAddresses; static FN_HANDLE_CMD IpShowConfig; static FN_HANDLE_CMD IpShowDns; - +static FN_HANDLE_CMD IpReset; static CMD_ENTRY @@ -53,7 +57,9 @@ static CMD_ENTRY IpDeleteCommands[] = { - {L"arpcache", IpDeleteArpCache, IDS_HLP_IP_DELETE_ARPCACHE, IDS_HLP_IP_DELETE_ARPCACHE_EX, 0} + {L"address", IpDeleteAddress, IDS_HLP_IP_DELETE_ADDRESS, IDS_HLP_IP_DELETE_ADDRESS_EX, 0}, + {L"arpcache", IpDeleteArpCache, IDS_HLP_IP_DELETE_ARPCACHE, IDS_HLP_IP_DELETE_ARPCACHE_EX, 0}, + {L"dns", IpDeleteDns, IDS_HLP_IP_DELETE_DNS, IDS_HLP_IP_DELETE_DNS_EX, 0} }; static @@ -83,6 +89,13 @@ IpGroups[] = {L"show", IDS_HLP_IP_SHOW, sizeof(IpShowCommands) / sizeof(CMD_ENTRY), 0, IpShowCommands, NULL}, }; +static +CMD_ENTRY +IpTopCommands[] = +{ + {L"reset", IpReset, IDS_HLP_IP_RESET, IDS_HLP_IP_RESET_EX, 0} +}; + static HRESULT @@ -446,6 +459,51 @@ AppendParameterValue( return TRUE; } +static +BOOL +DeleteParameterValue( + PWSTR pszParameters, + PWSTR pszParameter, + PWSTR pszValue) +{ + PWSTR pToken, pStart, pEnd, pValStart, pValEnd; + + pToken = wcsstr(pszParameters, pszParameter); + if (pToken == NULL) + return FALSE; + + pEnd = wcschr(pToken, L';'); + if (pEnd == NULL) + return FALSE; + + pStart = pToken + wcslen(pszParameter) + 1; + if (pStart == pEnd) + return TRUE; + + if (pszValue == NULL) + { + MoveMemory(pStart, pEnd, (wcslen(pEnd) + 1) * sizeof(WCHAR)); + } + else + { + pValStart = wcsstr(pStart, pszValue); + if ((pValStart >= pStart) & (pValStart < pEnd)) + { + pValEnd = pValStart + wcslen(pszValue); + if (*pValEnd == L',') + pValEnd++; + else if ((*pValEnd == L';') && (*(pValStart - 1) == L',')) + pValStart--; + MoveMemory(pValStart, pValEnd, (wcslen(pValEnd) + 1) * sizeof(WCHAR)); + } + else + { + return FALSE; + } + } + + return TRUE; +} static DWORD @@ -900,6 +958,228 @@ done: } +static +DWORD +WINAPI +IpDeleteAddress( + LPCWSTR pwszMachine, + LPWSTR *argv, + DWORD dwCurrentIndex, + DWORD dwArgCount, + DWORD dwFlags, + LPCVOID pvData, + BOOL *pbDone) +{ + TAG_TYPE pttTags[] = {{L"name", NS_REQ_ZERO, FALSE}, + {L"addr", NS_REQ_ZERO, FALSE}, + {L"gateway", NS_REQ_ZERO, FALSE}}; + GUID InterfaceGUID; + PDWORD pdwTagType = NULL; + DWORD i; + BOOL bHaveName = FALSE, bHaveAddress = FALSE, bHaveGateway = FALSE; + IN_ADDR Address, Gateway; + PWSTR pszName = NULL, pszAddress = NULL, pszGateway = NULL; + PWSTR pszNewIpAddress = NULL, pszNewSubnetMask = NULL, pszNewParameters = NULL; +// DWORD dwLength; + PCWSTR Term; + PTCPIP_PROPERTIES pProperties = NULL; + TCPIP_PROPERTIES NewProperties; + HRESULT hr; + NTSTATUS Status; + DWORD dwError = ERROR_SUCCESS; + + DPRINT("IpDeleteAddress()\n"); + + pdwTagType = HeapAlloc(GetProcessHeap(), + 0, + (dwArgCount - dwCurrentIndex) * sizeof(DWORD)); + if (pdwTagType == NULL) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + dwError = MatchTagsInCmdLine(hDllInstance, + argv, + dwCurrentIndex, + dwArgCount, + pttTags, + ARRAYSIZE(pttTags), + pdwTagType); + if (dwError != ERROR_SUCCESS) + { + DPRINT1("MatchTagsInCmdLine() failed (Error %lu)\n", dwError); + HeapFree(GetProcessHeap(), 0, pdwTagType); + return dwError; + } + + for (i = 0; i < (dwArgCount - dwCurrentIndex); i++) + { + DPRINT("Tag %lu: %lu\n", i, pdwTagType[i]); + + switch (pdwTagType[i]) + { + case 0: /* name */ + DPRINT("Tag: name (%S)\n", argv[i + dwCurrentIndex]); + dwError = NhGetGuidFromInterfaceName(argv[i + dwCurrentIndex], + &InterfaceGUID, + 0, 0); + if (dwError != ERROR_SUCCESS) + { + DPRINT1("NhGetGuidFromInterfaceName() failed (Error %lu)\n", dwError); + PrintMessageFromModule(hDllInstance, + IDS_ERROR_INVALID_INTERFACE, + argv[i + dwCurrentIndex]); + dwError = ERROR_SUPPRESS_OUTPUT; + break; + } + pszName = argv[i + dwCurrentIndex]; + DPRINT("Interface: {%08lx-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", + InterfaceGUID.Data1, InterfaceGUID.Data2, InterfaceGUID.Data3, InterfaceGUID.Data4[0], InterfaceGUID.Data4[1], + InterfaceGUID.Data4[2], InterfaceGUID.Data4[3], InterfaceGUID.Data4[4], InterfaceGUID.Data4[5], InterfaceGUID.Data4[6], InterfaceGUID.Data4[7]); + bHaveName = TRUE; + break; + + case 1: /* addr */ + DPRINT("Tag: addr (%S)\n", argv[i + dwCurrentIndex]); + Status = RtlIpv4StringToAddressW(argv[i + dwCurrentIndex], + TRUE, + &Term, + &Address); + if (Status != 0 /*STATUS_SUCCESS*/) + { + DPRINT("RtlIpv4StringToAddressW() failed (Status 0x%08lx)\n", Status); + PrintMessageFromModule(hDllInstance, + IDS_ERROR_BAD_VALUE, + argv[i + dwCurrentIndex], + pttTags[pdwTagType[i]].pwszTag); + dwError = ERROR_SUPPRESS_OUTPUT; + break; + } + DPRINT("IP Address: %u.%u.%u.%u\n", + Address.S_un.S_un_b.s_b1, Address.S_un.S_un_b.s_b2, Address.S_un.S_un_b.s_b3, Address.S_un.S_un_b.s_b4); + pszAddress = argv[i + dwCurrentIndex]; + DPRINT("IP Address: %S\n", pszAddress); + bHaveAddress = TRUE; + break; + + case 2: /* gateway */ + DPRINT("Tag: gateway (%S)\n", argv[i + dwCurrentIndex]); + Status = RtlIpv4StringToAddressW(argv[i + dwCurrentIndex], + TRUE, + &Term, + &Gateway); + if (Status != 0 /*STATUS_SUCCESS*/) + { + DPRINT("RtlIpv4StringToAddressW() failed (Status 0x%08lx)\n", Status); + PrintMessageFromModule(hDllInstance, + IDS_ERROR_BAD_VALUE, + argv[i + dwCurrentIndex], + pttTags[pdwTagType[i]].pwszTag); + dwError = ERROR_SUPPRESS_OUTPUT; + break; + } + pszGateway = argv[i + dwCurrentIndex]; + DPRINT("Gateway: %u.%u.%u.%u\n", + Gateway.S_un.S_un_b.s_b1, Gateway.S_un.S_un_b.s_b2, Gateway.S_un.S_un_b.s_b3, Gateway.S_un.S_un_b.s_b4); + bHaveGateway = TRUE; + break; + + default: + DPRINT1("Unknown tag type %lu\n", pdwTagType[i]); + break; + } + } + + if (pdwTagType) + HeapFree(GetProcessHeap(), 0, pdwTagType); + + if (dwError != ERROR_SUCCESS) + return dwError; + + /* Check parameters */ + + /* The interface name is mandatory */ + if (bHaveName == FALSE) + return ERROR_INVALID_SYNTAX; + + hr = GetInterfaceProperties(&InterfaceGUID, &pProperties); + if (FAILED(hr)) + { + PrintMessageFromModule(hDllInstance, + IDS_ERROR_GET_PROPERTIES, + pszName); + return ERROR_SUPPRESS_OUTPUT; + } + + if (bHaveAddress && pszAddress) + { +#if 0 + dwLength = wcslen(pProperties->pszIpAddress) + 1; + pszNewIpAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(WCHAR)); + if (pszNewIpAddress == NULL) + { + dwError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + wcscpy(pszNewIpAddress, pProperties->pszIpAddress); + /* TODO */ + + dwLength = wcslen(pProperties->pszSubnetMask) + 1; + pszNewSubnetMask = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(WCHAR)); + if (pszNewSubnetMask == NULL) + { + dwError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + wcscpy(pszNewSubnetMask, pProperties->pszSubnetMask); + /* TODO */ +#endif + } + + if (bHaveGateway && pszGateway) + { +#if 0 + dwLength = wcslen(pProperties->pszParameters) + 1; + + pszNewParameters = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(WCHAR)); + if (pszNewParameters == NULL) + { + dwError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + wcscpy(pszNewParameters, pProperties->pszParameters); + /* TODO */ +#endif + } + + NewProperties.dwDhcp = 0; + NewProperties.pszIpAddress = pszNewIpAddress ? pszNewIpAddress : pProperties->pszIpAddress; + NewProperties.pszSubnetMask = pszNewSubnetMask ? pszNewSubnetMask : pProperties->pszSubnetMask; + NewProperties.pszParameters = pszNewParameters ? pszNewParameters : pProperties->pszParameters; + + SetInterfaceProperties(&InterfaceGUID, &NewProperties); + +//done: + if (pszNewIpAddress) + HeapFree(GetProcessHeap(), 0, pszNewIpAddress); + + if (pszNewSubnetMask) + HeapFree(GetProcessHeap(), 0, pszNewSubnetMask); + + if (pszNewParameters) + HeapFree(GetProcessHeap(), 0, pszNewParameters); + + CoTaskMemFree(pProperties); + pProperties = NULL; + + DPRINT("IpDeleteAddress() done (Error %lu)\n", dwError); + return dwError; +} + + static DWORD WINAPI @@ -1020,6 +1300,186 @@ IpDeleteArpCache( } +static +DWORD +WINAPI +IpDeleteDns( + LPCWSTR pwszMachine, + LPWSTR *argv, + DWORD dwCurrentIndex, + DWORD dwArgCount, + DWORD dwFlags, + LPCVOID pvData, + BOOL *pbDone) +{ + TAG_TYPE pttTags[] = {{L"name", NS_REQ_ZERO, FALSE}, + {L"addr", NS_REQ_ZERO, FALSE}}; + TOKEN_VALUE ptvAddress[] = {{L"all", ADDRESS_ALL}}; + GUID InterfaceGUID; + PDWORD pdwTagType = NULL; + DWORD i; + BOOL bHaveName = FALSE, bHaveAddress = FALSE; + IN_ADDR Address; + PWSTR pszName = NULL, pszAddress = NULL; + PWSTR pszNewParameters = NULL; + DWORD dwAddress, dwLength; + PCWSTR Term; + PTCPIP_PROPERTIES pProperties = NULL; + TCPIP_PROPERTIES NewProperties; + HRESULT hr; + NTSTATUS Status; + DWORD dwError = ERROR_SUCCESS; + + DPRINT1("IpDeleteDns()\n"); + + pdwTagType = HeapAlloc(GetProcessHeap(), + 0, + (dwArgCount - dwCurrentIndex) * sizeof(DWORD)); + if (pdwTagType == NULL) + { + return ERROR_NOT_ENOUGH_MEMORY; + } + + dwError = MatchTagsInCmdLine(hDllInstance, + argv, + dwCurrentIndex, + dwArgCount, + pttTags, + ARRAYSIZE(pttTags), + pdwTagType); + if (dwError != ERROR_SUCCESS) + { + DPRINT1("MatchTagsInCmdLine() failed (Error %lu)\n", dwError); + HeapFree(GetProcessHeap(), 0, pdwTagType); + return dwError; + } + + for (i = 0; i < (dwArgCount - dwCurrentIndex); i++) + { + DPRINT1("Tag %lu: %lu\n", i, pdwTagType[i]); + + switch (pdwTagType[i]) + { + case 0: /* name */ + DPRINT1("Tag: name (%S)\n", argv[i + dwCurrentIndex]); + dwError = NhGetGuidFromInterfaceName(argv[i + dwCurrentIndex], + &InterfaceGUID, + 0, 0); + if (dwError != ERROR_SUCCESS) + { + DPRINT1("NhGetGuidFromInterfaceName() failed (Error %lu)\n", dwError); + PrintMessageFromModule(hDllInstance, + IDS_ERROR_INVALID_INTERFACE, + argv[i + dwCurrentIndex]); + dwError = ERROR_SUPPRESS_OUTPUT; + break; + } + pszName = argv[i + dwCurrentIndex]; + DPRINT1("Interface: {%08lx-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", + InterfaceGUID.Data1, InterfaceGUID.Data2, InterfaceGUID.Data3, InterfaceGUID.Data4[0], InterfaceGUID.Data4[1], + InterfaceGUID.Data4[2], InterfaceGUID.Data4[3], InterfaceGUID.Data4[4], InterfaceGUID.Data4[5], InterfaceGUID.Data4[6], InterfaceGUID.Data4[7]); + bHaveName = TRUE; + break; + + case 1: /* addr */ + DPRINT1("Tag: addr (%S)\n", argv[i + dwCurrentIndex]); + dwError = MatchEnumTag(hDllInstance, + argv[i + dwCurrentIndex], + ARRAYSIZE(ptvAddress), + ptvAddress, + &dwAddress); + if (dwError == ERROR_SUCCESS) + { + pszAddress = NULL; + } + else + { + dwError = ERROR_SUCCESS; + Status = RtlIpv4StringToAddressW(argv[i + dwCurrentIndex], + TRUE, + &Term, + &Address); + if (Status != 0 /*STATUS_SUCCESS*/) + { + DPRINT1("RtlIpv4StringToAddressW() failed (Status 0x%08lx)\n", Status); + PrintMessageFromModule(hDllInstance, + IDS_ERROR_BAD_VALUE, + argv[i + dwCurrentIndex], + pttTags[pdwTagType[i]].pwszTag); + dwError = ERROR_SUPPRESS_OUTPUT; + break; + } + + DPRINT1("IP Address: %u.%u.%u.%u\n", + Address.S_un.S_un_b.s_b1, Address.S_un.S_un_b.s_b2, Address.S_un.S_un_b.s_b3, Address.S_un.S_un_b.s_b4); + pszAddress = argv[i + dwCurrentIndex]; + DPRINT1("IP Address: %S\n", pszAddress); + } + bHaveAddress = TRUE; + break; + + default: + DPRINT1("Unknown tag type %lu\n", pdwTagType[i]); + break; + } + } + + if (pdwTagType) + HeapFree(GetProcessHeap(), 0, pdwTagType); + + if (dwError != ERROR_SUCCESS) + return dwError; + + /* Check parameters */ + + /* The interface name is mandatory */ + if (bHaveName == FALSE) + return ERROR_INVALID_SYNTAX; + + /* The address is mandatory */ + if (!bHaveAddress) + return ERROR_INVALID_SYNTAX; + + hr = GetInterfaceProperties(&InterfaceGUID, &pProperties); + if (FAILED(hr)) + { + PrintMessageFromModule(hDllInstance, + IDS_ERROR_GET_PROPERTIES, + pszName); + return ERROR_SUPPRESS_OUTPUT; + } + + dwLength = wcslen(pProperties->pszParameters) + 1; + + pszNewParameters = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength * sizeof(WCHAR)); + if (pszNewParameters == NULL) + { + dwError = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + wcscpy(pszNewParameters, pProperties->pszParameters); + DeleteParameterValue(pszNewParameters, L"DNS", pszAddress); + + NewProperties.dwDhcp = 0; + NewProperties.pszIpAddress = pProperties->pszIpAddress; + NewProperties.pszSubnetMask = pProperties->pszSubnetMask; + NewProperties.pszParameters = pszNewParameters; + + SetInterfaceProperties(&InterfaceGUID, &NewProperties); + +done: + if (pszNewParameters) + HeapFree(GetProcessHeap(), 0, pszNewParameters); + + CoTaskMemFree(pProperties); + pProperties = NULL; + + DPRINT("IpDeleteDns() done (Error %lu)\n", dwError); + return dwError; +} + + static DWORD WINAPI @@ -1774,6 +2234,23 @@ IpShowDns( } +static +DWORD +WINAPI +IpReset( + LPCWSTR pwszMachine, + LPWSTR *argv, + DWORD dwCurrentIndex, + DWORD dwArgCount, + DWORD dwFlags, + LPCVOID pvData, + BOOL *pbDone) +{ + DPRINT1("IpReset()\n"); + return ERROR_SUCCESS; +} + + static DWORD WINAPI @@ -1903,8 +2380,8 @@ IpStart( ContextAttributes.pwszContext = L"ip"; ContextAttributes.guidHelper = GUID_IFMON_IP; - ContextAttributes.ulNumTopCmds = 0; - ContextAttributes.pTopCmds = NULL; + ContextAttributes.ulNumTopCmds = sizeof(IpTopCommands) / sizeof(CMD_ENTRY); + ContextAttributes.pTopCmds = IpTopCommands; ContextAttributes.ulNumGroups = sizeof(IpGroups) / sizeof(CMD_GROUP_ENTRY); ContextAttributes.pCmdGroups = IpGroups; diff --git a/dll/win32/ifmon/lang/en-US.rc b/dll/win32/ifmon/lang/en-US.rc index e4ec3549efe..0263a107bfa 100644 --- a/dll/win32/ifmon/lang/en-US.rc +++ b/dll/win32/ifmon/lang/en-US.rc @@ -44,7 +44,22 @@ Examples:\n\n\ %1!s! ""Local Area Connection"" 10.0.0.1\n\ %1!s! ""Local Area Connection"" 10.0.0.3 index=2\n" - IDS_HLP_IP_DELETE "Deletes a configuration entry from a table.\n" + IDS_HLP_IP_DELETE "Deletes a configuration entry from a table.\n" + IDS_HLP_IP_DELETE_ADDRESS "Deletes an IP address or default gateway from the specified interface.\n" + IDS_HLP_IP_DELETE_ADDRESS_EX "\nUsage: %1!s! [name=] [[addr=]IP address] [[gateway=]IP address|ALL]\n\n\ +Parameters:\n\n\ + Tag Value\n\ + name - The name of the interface.\n\ + addr - A static IP address for the interface specified by name.\n\ + gateway - One of the following values:\n\ + : A specific default gateway IP address for\n\ + the static IP address you are deleting.\n\ + ALL: Deletes all default gateway IP addresses for\n\ + the static IP address you are deleting.\n\n\ +Remarks: Deletes an IP address from an interface with multiple static IP\n\ + addresses, or deletes a default gateway from a specified interface.\n\n\ +Examples:\n\n\ + %1!s! ""Local Area Connection"" addr=10.0.0.1 gateway=all\n" IDS_HLP_IP_DELETE_ARPCACHE "Flushes the ARP cache for one or all interfaces.\n" IDS_HLP_IP_DELETE_ARPCACHE_EX "\nUsage: %1!s! [name=]\n\n\ @@ -59,6 +74,21 @@ Examples:\n\n\ %1!s!\n\ %1!s! name=""Local Area Connection""\n" + IDS_HLP_IP_DELETE_DNS "Deletes the DNS server from the specified interface.\n" + IDS_HLP_IP_DELETE_DNS_EX "\nUsage: %1!s! [name=] [[addr=]|ALL ]\n\n\ +Parameters:\n\n\ + Tag Value\n\ + name - The name of the interface where DNS servers are deleted.\n\ + addr - One of the following values:\n\ + : A specific IP address of a DNS server\n\ + you are deleting.\n\ + ALL: Deletes all configured IP addresses for DNS. servers.\n\n\ +Remarks: Deletes statically configured DNS server IP addresses for a\n\ + specific interface.\n\n\ +Examples:\n\n\ + %1!s! ""Local Area Connection"" 10.0.0.1\n\ + %1!s! ""Local Area Connection"" all\n" + IDS_HLP_IP_SET "Sets configuration information.\n" IDS_HLP_IP_SET_ADDRESS "Sets the IP address or default gateway to the specified interface.\n" IDS_HLP_IP_SET_ADDRESS_EX "\nUsage: %1!s! [name=] \n\ @@ -142,6 +172,16 @@ Remarks: Displays the DNS server configuration for a specific interface\nor inte Examples:\n\n\ %1!s! ""Local Area Network""\n" + IDS_HLP_IP_RESET "Resets TCP/IP and related components to a clean state.\n" + IDS_HLP_IP_RESET_EX "\nUsage: %1!s! [name=]\n\n\ +Parameters:\n\n\ + Tag Value\n\ + name - The name of a file to which to append information\n\ + regarding what settings were reset.\n\n\ +Remarks: Resets TCP/IP and related components to a clean state.\n\n\ +Examples:\n\n\ + %1!s! resetlog.txt\n" + IDS_HLP_WINSOCK_RESET "Resets the Winsock Catalog to a clean state.\n" IDS_HLP_WINSOCK_RESET_EX "\nUsage: %1!s!\n\n\ Remarks: Resets the Winsock Catalog to a clean state.\n" diff --git a/dll/win32/ifmon/resource.h b/dll/win32/ifmon/resource.h index 90ec887fe38..065110752e5 100644 --- a/dll/win32/ifmon/resource.h +++ b/dll/win32/ifmon/resource.h @@ -10,8 +10,12 @@ #define IDS_HLP_IP_ADD_DNS_EX 204 #define IDS_HLP_IP_DELETE 205 +#define IDS_HLP_IP_DELETE_ADDRESS 206 +#define IDS_HLP_IP_DELETE_ADDRESS_EX 207 #define IDS_HLP_IP_DELETE_ARPCACHE 208 #define IDS_HLP_IP_DELETE_ARPCACHE_EX 209 +#define IDS_HLP_IP_DELETE_DNS 210 +#define IDS_HLP_IP_DELETE_DNS_EX 211 #define IDS_HLP_IP_SET 212 #define IDS_HLP_IP_SET_ADDRESS 213 @@ -27,6 +31,9 @@ #define IDS_HLP_IP_SHOW_DNS 225 #define IDS_HLP_IP_SHOW_DNS_EX 226 +#define IDS_HLP_IP_RESET 227 +#define IDS_HLP_IP_RESET_EX 228 + #define IDS_HLP_WINSOCK_RESET 250 #define IDS_HLP_WINSOCK_RESET_EX 251 #define IDS_HLP_WINSOCK_SHOW 252