#include #include #include #include #include "debug.h" #include "wlbsconfig.h" #include "wlbsparm.h" #define MAXIPSTRLEN 20 //+---------------------------------------------------------------------------- // // Function: IpAddressFromAbcdWsz // // Synopsis:Converts caller's a.b.c.d IP address string to a network byte order IP // address. 0 if formatted incorrectly. // // Arguments: IN const WCHAR* wszIpAddress - ip address in a.b.c.d unicode string // // Returns: DWORD - IPAddr, return INADDR_NONE on failure // // History: fengsun Created Header 12/8/98 // chrisdar COPIED FROM \nt\net\config\netcfg\wlbscfg\utils.cpp BECAUSE COULDN'T RESOLVE RtlAssert WHEN COMPILING THIS FILE INTO PROJECT // //+---------------------------------------------------------------------------- DWORD WINAPI IpAddressFromAbcdWsz(IN const WCHAR* wszIpAddress) { CHAR szIpAddress[MAXIPSTRLEN + 1]; DWORD nboIpAddr; ASSERT(lstrlen(wszIpAddress) < MAXIPSTRLEN); WideCharToMultiByte(CP_ACP, 0, wszIpAddress, -1, szIpAddress, sizeof(szIpAddress), NULL, NULL); nboIpAddr = inet_addr(szIpAddress); return(nboIpAddr); } bool ValidateVipInRule(const PWCHAR pwszRuleString, const WCHAR pwToken, DWORD& dwVipLen) { ASSERT(NULL != pwszRuleString); bool ret = false; dwVipLen = 0; // Find the first occurence of the token string, which will denote the end of // the VIP part of the rule PWCHAR pwcAtSeparator = wcschr(pwszRuleString, pwToken); if (NULL == pwcAtSeparator) { return ret; } // Found the token string. Copy out the VIP and validate it. WCHAR wszIP[WLBS_MAX_CL_IP_ADDR + 1]; DWORD dwStrLen = min((UINT)(pwcAtSeparator - pwszRuleString), WLBS_MAX_CL_IP_ADDR); wcsncpy(wszIP, pwszRuleString, dwStrLen); wszIP[dwStrLen] = '\0'; ASSERT(dwStrLen == wcslen(wszIP)); dwVipLen = dwStrLen; // IpAddressFromAbcdWsz calls inet_addr to check the format of the IP address, but the // allowed formats are very flexible. For our port rule definition of a VIP we require // a rigid a.b.c.d format. To ensure that we only say the IP address is valid for IPs // specified in this manner, ensure that there are 3 and only 3 '.' in the string. DWORD dwTmpCount = 0; PWCHAR pwszTmp = pwszRuleString; while (pwszTmp < pwcAtSeparator) { if (*pwszTmp++ == L'.') { dwTmpCount++; } } if (dwTmpCount == 3 && INADDR_NONE != IpAddressFromAbcdWsz(wszIP)) { ret = true; } return ret; } DWORD testRule(PWCHAR ptr) { WLBS_REG_PARAMS* paramp = new WLBS_REG_PARAMS; DWORD ret = 0; PWLBS_PORT_RULE rp, rulep; /* distinct rule elements for parsing */ typedef enum { vip, start, end, protocol, mode, affinity, load, priority } CVY_RULE_ELEMENT; CVY_RULE_ELEMENT elem = vip; DWORD count = 0; DWORD i; DWORD dwVipLen = 0; const DWORD dwVipAllNameLen = sizeof(CVY_NAME_PORTRULE_VIPALL)/sizeof(WCHAR) - 1; // Used below in a loop. Set it here since it is a constant. WCHAR wszTraceOutputTmp[WLBS_MAX_CL_IP_ADDR + 1]; bool bFallThrough = false; // Used in 'vip' case statement below. rulep = paramp->i_port_rules; while (ptr != NULL) { switch (elem) { case vip: // DO NOT MOVE THIS CASE STATEMENT. IT MUST ALWAYS COME BEFORE THE 'start' CASE STATEMENT. See FALLTHROUGH comment below. bFallThrough = false; dwVipLen = 0; if (ValidateVipInRule(ptr, L',', dwVipLen)) { ASSERT(dwVipLen <= WLBS_MAX_CL_IP_ADDR); // rulep->virtual_ip_addr is a TCHAR and ptr is a WCHAR. // Data is moved from the latter to the former so ASSERT TCHAR is WCHAR. ASSERT(sizeof(TCHAR) == sizeof(WCHAR)); // This is a rule for a specific VIP _tcsncpy(rulep->virtual_ip_addr, ptr, dwVipLen); (rulep->virtual_ip_addr)[dwVipLen] = '\0'; } else { // This is either an 'all' rule, a VIP-less rule or a malformed rule. We can't distinguish a malformed rule // from a VIP-less rule, so we will assume the rule is either an 'all' rule or a VIP-less rule. In both cases // set the VIP component of the rule to be the default or 'all' value. // Copy the 'all' IP into the rule. _tcscpy(rulep->virtual_ip_addr, CVY_DEF_ALL_VIP); if (dwVipAllNameLen != dwVipLen || (_tcsnicmp(ptr, CVY_NAME_PORTRULE_VIPALL, dwVipAllNameLen) != 0)) { // The rule is either VIP-less or it is malformed. We assume it is VIP-less and let the 'start' // case handle the current token as a start_port property by falling through to the next case clause // rather than breaking. bFallThrough = true; // wprintf(L"doing fallthrough...%d, %d\n", dwVipAllNameLen, dwVipLen); _tcsncpy(wszTraceOutputTmp, ptr, dwVipLen); wszTraceOutputTmp[dwVipLen] = '\0'; // TraceMsg(L"-----\n#### VIP element of port rule is invalid = %s\n", wszTraceOutputTmp); } } // TraceMsg(L"-----\n#### Port rule vip = %s\n", rulep->virtual_ip_addr); elem = start; // !!!!!!!!!!!!!!!!!!!! // FALLTHROUGH // !!!!!!!!!!!!!!!!!!!! // When we have a VIP-less port rule, we will fall through this case statement into the 'start' case statement // below so that the current token can be used as the start_port for a port rule. if (!bFallThrough) { // We have a VIP in the port rule. We do a "break;" as std operating procedure. // TraceMsg(L"-----\n#### Fallthrough case statement from port rule vip to start\n"); break; } // NO AUTOMATIC "break;" STATEMENT HERE. Above, we conditionally flow to the 'start' case... case start: // DO NOT MOVE THIS CASE STATEMENT. IT MUST ALWAYS COME AFTER THE 'vip' CASE STATEMENT. // See comments (FALLTHROUGH) inside the 'vip' case statement. rulep->start_port = _wtoi(ptr); // CVY_CHECK_MIN (rulep->start_port, CVY_MIN_PORT); CVY_CHECK_MAX (rulep->start_port, CVY_MAX_PORT); // TraceMsg(L"-----\n#### Start port = %d\n", rulep->start_port); elem = end; break; case end: rulep->end_port = _wtoi(ptr); // CVY_CHECK_MIN (rulep->end_port, CVY_MIN_PORT); CVY_CHECK_MAX (rulep->end_port, CVY_MAX_PORT); // TraceMsg(L"#### End port = %d\n", rulep->end_port); elem = protocol; break; case protocol: switch (ptr [0]) { case L'T': case L't': rulep->protocol = CVY_TCP; // TraceMsg(L"#### Protocol = TCP\n"); break; case L'U': case L'u': rulep->protocol = CVY_UDP; // TraceMsg(L"#### Protocol = UDP\n"); break; default: rulep->protocol = CVY_TCP_UDP; // TraceMsg(L"#### Protocol = Both\n"); break; } elem = mode; break; case mode: switch (ptr [0]) { case L'D': case L'd': rulep->mode = CVY_NEVER; // TraceMsg(L"#### Mode = Disabled\n"); goto end_rule; case L'S': case L's': rulep->mode = CVY_SINGLE; // TraceMsg(L"#### Mode = Single\n"); elem = priority; break; default: rulep->mode = CVY_MULTI; // TraceMsg(L"#### Mode = Multiple\n"); elem = affinity; break; } break; case affinity: switch (ptr [0]) { case L'C': case L'c': rulep->mode_data.multi.affinity = CVY_AFFINITY_CLASSC; // TraceMsg(L"#### Affinity = Class C\n"); break; case L'N': case L'n': rulep->mode_data.multi.affinity = CVY_AFFINITY_NONE; // TraceMsg(L"#### Affinity = None\n"); break; default: rulep->mode_data.multi.affinity = CVY_AFFINITY_SINGLE; // TraceMsg(L"#### Affinity = Single\n"); break; } elem = load; break; case load: if (ptr [0] == L'E' || ptr [0] == L'e') { rulep->mode_data.multi.equal_load = TRUE; rulep->mode_data.multi.load = CVY_DEF_LOAD; // TraceMsg(L"#### Load = Equal\n"); } else { rulep->mode_data.multi.equal_load = FALSE; rulep->mode_data.multi.load = _wtoi(ptr); // CVY_CHECK_MIN (rulep->mode_data.multi.load, CVY_MIN_LOAD); CVY_CHECK_MAX (rulep->mode_data.multi.load, CVY_MAX_LOAD); // TraceMsg(L"#### Load = %d\n", rulep->mode_data.multi.load); } goto end_rule; case priority: rulep->mode_data.single.priority = _wtoi(ptr); CVY_CHECK_MIN (rulep->mode_data.single.priority, CVY_MIN_PRIORITY); CVY_CHECK_MAX (rulep->mode_data.single.priority, CVY_MAX_PRIORITY); // TraceMsg(L"#### Priority = %d\n", rulep->mode_data.single.priority); goto end_rule; default: // TraceMsg(L"#### Bad rule element %d\n", elem); break; } next_field: ptr = wcschr(ptr, L','); if (ptr != NULL) { ptr ++; continue; } else break; end_rule: elem = vip; for (i = 0; i < count; i ++) { rp = paramp->i_port_rules + i; if ((rulep -> start_port < rp -> start_port && rulep -> end_port >= rp -> start_port) || (rulep -> start_port >= rp -> start_port && rulep -> start_port <= rp -> end_port)) { // TraceMsg(L"#### Rule %d (%d - %d) overlaps with rule %d (%d - %d)\n", i, rp -> start_port, rp -> end_port, count, rulep -> start_port, rulep -> end_port); break; } } wprintf(L"vip = %s, start = %d, end = %d, protocol = %d\n", rulep->virtual_ip_addr, rulep->start_port, rulep->end_port, rulep->protocol); wprintf(L"mode = %d, affinity = %d, load = %d, %d\n", rulep->mode, rulep->mode_data.multi.affinity, rulep->mode_data.multi.equal_load, rulep->mode_data.multi.load); wprintf(L"priority = %d\n\n\n", rulep->mode_data.single.priority); rulep -> valid = TRUE; CVY_RULE_CODE_SET (rulep); if (i >= count) { count++; rulep++; if (count >= CVY_MAX_RULES) break; } goto next_field; } delete paramp; return ret; } int __cdecl wmain(int argc, wchar_t * argv[]) { DWORD result = 0; // Good Vip = gv // No Vip = nv // Bad Vip = bv PWCHAR ppGoodRuleStrings[] = { L"1.2.3.4,20,21,Both,Multiple,Single,Equal\n", // gv L"1018,1019,UDP,Multiple,None,Equal\n", // nv L"1.2.3.4,20,21,Both,Multiple,Single,Equal,1018,1019,UDP,Multiple,None,Equal\n", // gv nv L"1018,1019,UDP,Multiple,None,Equal,1.2.3.4,20,21,Both,Multiple,Single,Equal\n", // nv gv L"1.2.3.4,20,21,Both,Multiple,Single,Equal,5.6.7.8,20,21,Both,Multiple,Single,Equal\n", // gv gv L"1018,1019,UDP,Multiple,None,Equal,4018,4019,UDP,Multiple,None,Equal\n", // nv nv L"1.2.3.4,20,21,Both,Multiple,Single,Equal,1018,1019,UDP,Multiple,None,Equal,4018,4019,UDP,Multiple,None,Equal\n", // gv nv nv L"1018,1019,UDP,Multiple,None,Equal,1.2.3.4,20,21,Both,Multiple,Single,Equal,4018,4019,UDP,Multiple,None,Equal\n", // nv gv nv L"1018,1019,UDP,Multiple,None,Equal,4018,4019,UDP,Multiple,None,Equal,1.2.3.4,20,21,Both,Multiple,Single,Equal\n", // nv nv gv L"1.2.3.4,20,21,Both,Multiple,Single,Equal,5.6.7.8,20,21,Both,Multiple,Single,Equal,1018,1019,UDP,Multiple,None,Equal\n", // gv gv nv L"1.2.3.4,20,21,Both,Multiple,Single,Equal,1018,1019,UDP,Multiple,None,Equal,5.6.7.8,20,21,Both,Multiple,Single,Equal\n", // gv nv gv L"1018,1019,UDP,Multiple,None,Equal,1.2.3.4,20,21,Both,Multiple,Single,Equal,5.6.7.8,20,21,Both,Multiple,Single,Equal\n", // nv gv gv L"all,0,19,Both,Multiple,None,Equal,1.2.3.4,20,21,Both,Multiple,Single,Equal,254.254.254.254,22,138,Both,Multiple,None,Equal,207.46.148.249,139,139,Both,Multiple,Single,Equal,157.54.55.192,140,442,Both,Multiple,None,Equal,443,443,Both,Multiple,Single,Equal\n", L"111.222.222.111,1018,1018,TCP,Multiple,None,Equal,\n", NULL }; PWCHAR ppBadRuleStrings[] = { L"", L"\n", L"111.222.333.111,1018,1018,TCP,Multiple,None,Equal,\n", L"443,1000,1001,Both,Multiple,Single,Equal\n", L"1,1000,1001,Both,Multiple,Single,Equal\n", L"1.1,1000,1001,Both,Multiple,Single,Equal\n", L"1.1.1,1000,1001,Both,Multiple,Single,Equal\n", L"allinthefamily,1000,1001,Both,Multiple,Single,Equal\n", NULL }; PWCHAR* ptr = ppGoodRuleStrings; wprintf(L"These rules are valid and should be read properly.\n\n"); while(*ptr != NULL) { wprintf(L"Input rule string is = %s", *ptr); result = testRule(*ptr); wprintf(L"\n\n\n"); ptr++; } wprintf(L"These rules will fail, but should not cause AV, etc.\n\n"); ptr = ppBadRuleStrings; while(*ptr != NULL) { wprintf(L"Input rule string is = %s", *ptr); result = testRule(*ptr); wprintf(L"\n\n\n"); ptr++; } return 0; }