// link with the Ws2_32.lib
#include <winsock2.h>
// link with the Rpcrt4.lib
#include <rpc.h>
#include <stdio.h>

// Print out the contents of a WSAPROTOCOL_INFO structure in
// a readable format.
static void PrintBufContent(WSAPROTOCOL_INFO *pProtocolBuf)
{
	unsigned char *pszUuid;
	int i;
	
	printf("\nProtocol <%s>\n", pProtocolBuf->szProtocol);
	// A guid is the same as a uuid.
	UuidToString(&pProtocolBuf->ProviderId, (RPC_WSTR *)&pszUuid);
	printf("  ProviderId {%s}\n", pszUuid);
	RpcStringFree((RPC_WSTR *)&pszUuid);
	
	if (!pProtocolBuf->dwServiceFlags1)
		printf("  dwServiceFlags1: 0\n");
	else
		printf("  dwServiceFlags1: 0x%08X\n",pProtocolBuf->dwServiceFlags1);
	
	// Check which bit flags are set.
	printf("\nChecking the set bit flags...\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_CONNECTIONLESS)
		printf("    XP1_CONNECTIONLESS\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_GUARANTEED_DELIVERY)
		printf("    XP1_GUARANTEED_DELIVERY\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_GUARANTEED_ORDER)
		printf("    XP1_GUARANTEED_ORDER\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_MESSAGE_ORIENTED)
		printf("    XP1_MESSAGE_ORIENTED\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_PSEUDO_STREAM)
		printf("    XP1_PSEUDO_STREAM\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_GRACEFUL_CLOSE)
		printf("    XP1_GRACEFUL_CLOSE\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_EXPEDITED_DATA)
		printf("    XP1_EXPEDITED_DATA\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_CONNECT_DATA)
		printf("    XP1_CONNECT_DATA\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_DISCONNECT_DATA)
		printf("    XP1_DISCONNECT_DATA\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_SUPPORT_BROADCAST)
		printf("    XP1_SUPPORT_BROADCAST\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_SUPPORT_MULTIPOINT)
		printf("    XP1_SUPPORT_MULTIPOINT\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_MULTIPOINT_CONTROL_PLANE)
		printf("    XP1_MULTIPOINT_CONTROL_PLANE\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_MULTIPOINT_DATA_PLANE)
		printf("    XP1_MULTIPOINT_DATA_PLANE\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_QOS_SUPPORTED)
		printf("    XP1_QOS_SUPPORTED\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_INTERRUPT)
		printf("    XP1_INTERRUPT\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_UNI_SEND)
		printf("    XP1_UNI_SEND\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_UNI_RECV)
		printf("    XP1_UNI_RECV\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_IFS_HANDLES)
		printf("    XP1_IFS_HANDLES\n");
	if (pProtocolBuf->dwServiceFlags1 & XP1_PARTIAL_MESSAGE)
		printf("    XP1_PARTIAL_MESSAGE\n");
	
	printf("  dwServiceFlags2: reserved\n");
	printf("  dwServiceFlags3: reserved\n");
	printf("  dwServiceFlags4: reserved\n");
	
	printf("\nChecking the Provider flags...\n");
	printf("  dwProviderFlags:\n");
	if (pProtocolBuf->dwProviderFlags & PFL_MULTIPLE_PROTO_ENTRIES)
		printf("    PFL_MULTIPLE_PROTO_ENTRIES\n");
	if (pProtocolBuf->dwProviderFlags & PFL_RECOMMENDED_PROTO_ENTRY)
		printf("    PFL_RECOMMENDED_PROTO_ENTRY\n");
	if (pProtocolBuf->dwProviderFlags & PFL_HIDDEN)
		printf("    PFL_HIDDEN\n");
	if (pProtocolBuf->dwProviderFlags & PFL_MATCHES_PROTOCOL_ZERO)
		printf("    PFL_MATCHES_PROTOCOL_ZERO\n");
	
	printf("\nMore digging...\n");
	printf("  dwCatalogEntryId = %u\n", pProtocolBuf->dwCatalogEntryId);
	
	printf("  ProtocolChain.ChainLen = %d ",  pProtocolBuf->ProtocolChain.ChainLen);
	if (pProtocolBuf->ProtocolChain.ChainLen == 1)
		printf("  (This is a base service provider)\n");
	else if (pProtocolBuf->ProtocolChain.ChainLen > 1)
	{
		printf("  (ProtocolChain layered to base protocol)\n");
		for (i=0; i<pProtocolBuf->ProtocolChain.ChainLen; i++)
		{
			printf("    Chain Catalog Entry Id = %u\n", pProtocolBuf->ProtocolChain.ChainEntries[i]);
		}
	}
	else if (pProtocolBuf->ProtocolChain.ChainLen == 0)
		printf("  (This is a layered service provider)\n");
	else
		printf("  Invalid\n");
	
	printf("  iVersion = %d\n", pProtocolBuf->iVersion);
	printf("  iAddressFamily = %d\n", pProtocolBuf->iAddressFamily);
	printf("  iMaxSockAddr = %d\n", pProtocolBuf->iMaxSockAddr);
	printf("  iMinSockAddr = %d\n", pProtocolBuf->iMinSockAddr);
	
	// iProtocols returns a negative number for Microsoft NetBIOS
	// service providers corresponding to the lana number * -1 (for
	// example, -2 implies lana 2), except for lana 0 which is equal to
	// 0x80000000 because protocol 0 is reserved for special use.
	printf("  iProtocol = %d\n", pProtocolBuf->iProtocol);
	printf("  iProtocolMaxOffset = %d\n", pProtocolBuf->iProtocolMaxOffset);
	printf("  iNetworkByteOrder = %s\n", ((pProtocolBuf->iNetworkByteOrder == LITTLEENDIAN) ?
		"LittleEndian" : "BigEndian"));
	printf("  iSecurityScheme = %d\n", pProtocolBuf->iSecurityScheme);
	printf("  dwMessageSize = %u\n", pProtocolBuf->dwMessageSize);
	printf("  dwProviderReserved = reserved\n");
	return;
}

int main(int argc, char *argv[])
{
	WSADATA WSAData;
	int i, nRet;
	DWORD dwErr;
	WSAPROTOCOL_INFO *lpProtocolBuf = NULL;
	DWORD dwBufLen = 0;
	
	// The WSAStartup() function directly returns the extended error code
	// in the return value for this function. A call to the WSAGetLastError
	// function is not needed and should not be used
	if (WSAStartup(MAKEWORD(2,2), &WSAData) != 0)
		printf("WSAStartup() error with code %d\n", WSAGetLastError());
	else
	{
		printf("WSAStartup() is OK!\n");
		// First, have WSAEnumProtocols tell you how big a buffer you need.
		nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen);
		
		if (nRet != SOCKET_ERROR)
			printf("WSAEnumProtocols() looks OK!\n");
		else if ((dwErr = WSAGetLastError()) != WSAENOBUFS)
			// WSAEnumProtocols() failed for some reason not relating to buffer size
			printf("WSAEnumProtocols() failed with code %d\n", WSAGetLastError());
		else
		{
			// WSAEnumProtocols failed for the "expected" reason.
			// Now you need to allocate a buffer that is the right size.
			lpProtocolBuf = (WSAPROTOCOL_INFO *)malloc(dwBufLen);
			if (lpProtocolBuf)
			{
				// Now you can call WSAEnumProtocols again with the
				// expectation that it will succeed
				// because you have allocated a big enough buffer.
				nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen);
				
				if (nRet == SOCKET_ERROR)
					printf("WSAEnumProtocols() failed with code %d\n", WSAGetLastError());
				else
				{
					// Enumerate the protocols.
					printf("Enumerating the protocols...");
					for (i=0; i<nRet; i++)
					{
						PrintBufContent(&lpProtocolBuf[i]);
						printf("\nPress <Enter> for more info, if any...\n");
						_fgetchar();
					}
				}
				// Free up the allocated resource
				free(lpProtocolBuf);
			}
		}
	}
	if(WSACleanup() != 0)
		printf("WSACleanup() failed!...\n");
	else
		printf("WSACleanup() is OK...\n");
	return 0;
}