#define WIN32_LEAN_AND_MEAN #define _WINSOCK_DEPRECATED_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include #define MINIUPNP_STATICLIB #include static const char* k_SsdpSearchFormatString = "M-SEARCH * HTTP/1.1\r\n" "HOST: %s:1900\r\n" "ST: ssdp:all\r\n" "MAN: \"ssdp:discover\"\r\n" "MX: 5\r\n" "\r\n"; struct UPNPDev* getUPnPDevicesByAddress(IN_ADDR address) { SOCKET s; SOCKADDR_IN connAddr; char searchBuffer[512]; int chars; s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (s == INVALID_SOCKET) { printf("socket() failed: %d\n", WSAGetLastError()); return nullptr; } connAddr = {}; connAddr.sin_family = AF_INET; connAddr.sin_port = htons(1900); connAddr.sin_addr = address; // Use connect() to ensure we don't get responses from other devices if (connect(s, (struct sockaddr*)&connAddr, sizeof(connAddr)) == SOCKET_ERROR) { printf("connect() failed: %d\n", WSAGetLastError()); closesocket(s); return nullptr; } // Send the first search message with HOST set properly chars = snprintf(searchBuffer, ARRAYSIZE(searchBuffer), k_SsdpSearchFormatString, inet_ntoa(address)); if (send(s, searchBuffer, chars, 0) == SOCKET_ERROR) { printf("send() failed: %d\n", WSAGetLastError()); closesocket(s); return nullptr; } // Send another search message with HOST set to 239.255.255.250 to avoid issues // on routers that explicitly check for that HOST value chars = snprintf(searchBuffer, ARRAYSIZE(searchBuffer), k_SsdpSearchFormatString, "239.255.255.250"); if (send(s, searchBuffer, chars, 0) == SOCKET_ERROR) { printf("send() failed: %d\n", WSAGetLastError()); closesocket(s); return nullptr; } Sleep(5000); // Switch to non-blocking mode to read the responses u_long mode = 1; ioctlsocket(s, FIONBIO, &mode); char responseBuffer[2048]; struct UPNPDev* deviceList = nullptr; for (;;) { int bytesRead = recv(s, responseBuffer, sizeof(responseBuffer) - 1, 0); if (bytesRead == SOCKET_ERROR) { if (WSAGetLastError() == WSAEMSGSIZE) { // Skip packets larger than our buffer printf("recv() message too large\n"); continue; } else if (WSAGetLastError() != WSAEWOULDBLOCK) { printf("recv() failed: %d\n", WSAGetLastError()); } break; } // Null-terminate the buffer responseBuffer[bytesRead] = 0; // Parse the first status line: // HTTP/1.1 200 OK char* protocol = strtok(responseBuffer, " "); char* statusCodeStr = strtok(nullptr, " "); char* statusMessage = strtok(nullptr, "\r"); // Check for a valid response header if (protocol == nullptr) { printf("Missing protocol in SSDP header\n"); continue; } else if (statusCodeStr == nullptr) { printf("Missing status code in SSDP header\n"); continue; } // FIXME: Should we require statusMessage too? else if (_stricmp(protocol, "HTTP/1.0") && _stricmp(protocol, "HTTP/1.1")) { printf("Unexpected protocol: %s\n", protocol); continue; } else if (atoi(statusCodeStr) != 200) { printf("Unexpected status: %s %s\n", statusCodeStr, statusMessage); continue; } // Parse the header options // SERVER: FreeBSD/11.2-RELEASE-p2 UPnP/1.1 MiniUPnPd/2.0\r\n char* location = nullptr; char* st = nullptr; while (char* headerName = strtok(nullptr, "\r\n:")) { char* headerValue = strtok(nullptr, "\r"); if (headerValue == nullptr) { printf("Unexpected end of SSDP header\n"); break; } // Skip leading spaces while (*headerValue == ' ') headerValue++; if (!_stricmp(headerName, "LOCATION")) { location = headerValue; } else if (!_stricmp(headerName, "ST")) { st = headerValue; } } if (!location || location[0] == 0 || !st || st[0] == 0) { printf("Required value missing: \"%s\" \"%s\"\n", location, st); continue; } struct UPNPDev* newDev = (struct UPNPDev*)malloc(sizeof(*newDev) + strlen(location) + strlen(st) + 2); newDev->pNext = deviceList; newDev->usn = &newDev->buffer[0]; newDev->buffer[0] = 0; newDev->descURL = strcpy(newDev->usn + strlen(newDev->usn) + 1, location); newDev->st = strcpy(newDev->descURL + strlen(newDev->descURL) + 1, st); newDev->scope_id = 0; // IPv6 only deviceList = newDev; } closesocket(s); return deviceList; } // Start at TTL 2 to skip contacting our default gateway #define TTL_START 2 bool getHopsIP4(IN_ADDR* hopAddress, int* hopAddressCount) { HANDLE icmpFile; struct hostent* host; const char* requestBuffer = "Test"; union { ICMP_ECHO_REPLY replies[ANYSIZE_ARRAY]; char replyBuffer[128]; }; host = gethostbyname("google.com"); if (host == nullptr) { printf("gethostbyname() failed: %d\n", WSAGetLastError()); return false; } icmpFile = IcmpCreateFile(); if (icmpFile == INVALID_HANDLE_VALUE) { printf("IcmpCreateFile() failed: %d\n", GetLastError()); return false; } int ttl; for (ttl = TTL_START; ttl - TTL_START < *hopAddressCount; ttl++) { IP_OPTION_INFORMATION ipOptions; ipOptions.Ttl = ttl; ipOptions.Tos = 0; ipOptions.Flags = 0; ipOptions.OptionsSize = 0; DWORD replyCount = IcmpSendEcho(icmpFile, *(IPAddr*)host->h_addr, (LPVOID)requestBuffer, sizeof(requestBuffer), &ipOptions, replyBuffer, sizeof(replyBuffer), 3000); if (replyCount == 0) { printf("IcmpSendEcho() failed: %d\n", GetLastError()); break; } else if (replyCount != 1) { printf("Got extra replies: %d\n", replyCount); break; } if (replies[0].Status == IP_TTL_EXPIRED_TRANSIT) { // Get the IP address that responded to us printf("Hop %d: %s\n", ttl - TTL_START, inet_ntoa(*(IN_ADDR*)&replies[0].Address)); hopAddress[ttl - TTL_START] = *(IN_ADDR*)&replies[0].Address; } else { // Bail on anything else printf("Hop %d: %s (error %d)\n", ttl - TTL_START, inet_ntoa(*(IN_ADDR*)&replies[0].Address), replies[0].Status); break; } } IcmpCloseHandle(icmpFile); *hopAddressCount = ttl - TTL_START; return true; }