GetAddrInfo() löst IPv6-Localhost falsch auf
-
Hallo!
Ich wundere mich etwas, wieso folgender Code nicht wie erwartet funktioniert:ADDRINFOT hints = { 0 }, *ai = 0; hints.ai_family = AF_UNSPEC; hints.ai_socktype = Protocol::socket_type; hints.ai_protocol = Protocol::protocol_family; if(!host) hints.ai_flags = AI_PASSIVE; ICantBelieveThatIuseGoto: switch(::GetAddrInfo(const_cast<TCHAR*>(host), const_cast<TCHAR*>(service), &hints, &ai)) { case WSAHOST_NOT_FOUND: case EAI_AGAIN: if(tryings > 0) { --tryings; goto ICantBelieveThatIuseGoto; } else throw net_error("number of address resolve tryings exceeded"); break; case NO_ERROR: break; default: throw net_error("address resolving error"); }
Der Code hat immer gut funktioniert. Sobald ich als Host aber den String "localhost" oder "[::1]" u. als Port z.B. 1234 angebe, wird für IPv6 das immer auf [::]:1234 aufgelöst, also die Adresse ist komplett null. Woran kann das liegen? Für die IPv4-Interfaces klappt's wunderbar.
-
Hat keiner eine Idee?
-
bring doch mal ein beispiel ohne dein framework und ohne son goto gedöns
-
Hab das genauere Übel gefunden:
#include <iostream> #include <WinSock2.h> int main() try { WSADATA data = { 0 }; if(WSAStartup(MAKEWORD(2, 2), &data) == SOCKET_ERROR) throw "startup error"; int tryings = 3; ADDRINFOT hints = { 0 }, *ai = 0; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; ICantBelieveThatIuseGoto: switch(::GetAddrInfo("localhost", "80", &hints, &ai)) { case WSAHOST_NOT_FOUND: case EAI_AGAIN: if(tryings > 0) { --tryings; goto ICantBelieveThatIuseGoto; } else throw "number of address resolve tryings exceeded"; break; case NO_ERROR: break; default: throw "address resolving error"; } char buf[INET6_ADDRSTRLEN * 2] = { 0 }; DWORD length = INET6_ADDRSTRLEN * 2; SOCKADDR_STORAGE ss = { 0 }; std::memcpy(&ss, ai->ai_addr, sizeof sockaddr); if(::WSAAddressToString(reinterpret_cast<SOCKADDR*>(&ss), sizeof(SOCKADDR_STORAGE), 0, buf, &length) == SOCKET_ERROR) { ::FreeAddrInfo(ai); throw "address to string conversion error"; } std::cout << "Adress: " << buf << std::endl; std::system("pause"); ::WSACleanup(); ::FreeAddrInfo(ai); } catch(const char* msg) { std::cerr << msg << "\nErrno: " << GetLastError() << '\n'; std::system("pause"); ::WSACleanup(); }
An der Stelle wo ich den Inhalt von
ai->ai_addr
inss
kopiere, gehen irgendwie Informationen verloren. Als Ausgabe bekomme ich dann [::]:80. Wenn ich hingegen die ai->ai_addr direkt inWSAAdressToString()
konvertiere, kommt tatsächlich [::1]:80.
Das verrückte: wenn ich beim memcpy die Größe des Zielspeichers (also sizeof(SOCKADDR_STORAGE) angebe, wird undefinierter Speicher kopiert (da ja ai->ai_addr nur sizeof(SOCKADDR) hat). Allerdings ist genau dieses Byte richtig gesetzt, wodurch ich dann [::1]:80 erhalte (weiter hinten ist in der Sockaddr-Storage aber dann natürlich Speicher-Müll) -> undefiniertes Verhalten. Zufall?
-
Du musst beim memcpy ai->ai_addrlen benutzen und nicht sizeof sockaddr.
-
Hoppla, das ist peinlich.
Genau das war das Problem, vielen Dank!PS: Ich empfinde das Goto hier übrigens recht elegant. Innerhalb einer Schleife kann ich "break" durch das "switch()" nicht benutzen. Warum also erst irgendein Schleifen-Konstrukt mit "if..else..else if" basteln, wenn's mit Goto ganz unkompliziert geht?
-
Du könntest das ganze ja in einer Funktion kapseln, dann kannste return benutzen.
Also ungefähr so:
void GetAddrInfoWithRetry(..., ..., ..., ...) { const int RETRIES = 3; for(int i = 0; i < RETRIES; ++i) { int result = GetAddrInfo(..., ..., ..., ...); switch(result) { case WSAHOST_NOT_FOUND: case EAI_AGAIN: break; case 0: return; default: throw "address resolving error"; } } throw "number of address resolve tryings exceeded"; }
-
Naja, void kann die Funktion ja nicht als Rückgabetyp haben, da diese ja die Adresse zurückgeben muss. Das kann natürlich dann im Fehlerfall ein 0-Zeiger sein. Das dann aber erst abzufragen etc. ist wesentlich mehr Aufwand als ein simples goto.