diff --git a/lang/Czech.ini b/lang/Czech.ini index cc5281ca..ac3cd95f 100644 --- a/lang/Czech.ini +++ b/lang/Czech.ini @@ -167,7 +167,7 @@ LOCAL_PLAYER_MODEL_ONLY = "Pouze lokální model hráče" INFO_TITLE = "INFO" WARN_DISCORD = "Pozvat hráče pravým kliknutím na jejich profil a potom kliknout na \n'\\#d0d0ff\\Pozvat do Hry\\#dcdcdc\\'.\n\nMůžete pozvat i kanály a servery pomocí kliknutí na tlačíto \\#d0d0ff\\plus\\#dcdcdc\\ vedle okna na chat.\n\nHerní aktivita \\#ffa0a0\\musí být\\#dcdcdc\\ zapnutá ve vašich\nDiscord nastavení.\n\nZobranení jako neviditelný \\#ffa0a0\\zabrání\\#dcdcdc\\ posílání pozvánek." WARN_DISCORD2 = "\\#ffa0a0\\Chyba:\\#dcdcdc\\ Discord se nepodařilo najít.\n\\#a0a0a0\\Zkuste zavřít hru, restartovat Discord a znovu hru otevřít" -WARN_SOCKET = "Přímé spojení \\#ffa0a0\\vás vyžaduje,\\#dcdcdc\\ aby jste si nastavili přesměrování portu.\n\nPřesměrujte port '\\#d0d0ff\\%d\\#dcdcdc\\' s UDP." +WARN_SOCKET = "Ujistěte se, že je vaše brána firewall správně nakonfigurována.\nPřímá připojení \\#ffa0a0\\vyžadují\\#dcdcdc\\, abyste v routeru nakonfigurovali přesměrování portů pro přijetí příchozích IPv4 připojení.\n\nPřesměrujte port '\\#d0d0ff\\%d\\#dcdcdc\\' pro UDP. IPv6 je také podporováno." HOST = "Hostovat" [HOST_MODS] diff --git a/lang/Dutch.ini b/lang/Dutch.ini index ec15f3a8..48b09b8e 100644 --- a/lang/Dutch.ini +++ b/lang/Dutch.ini @@ -167,7 +167,7 @@ LOCAL_PLAYER_MODEL_ONLY = "Alleen lokaal spelermodel" INFO_TITLE = "INFORMATIE" WARN_DISCORD = "Nodig je vrienden uit door op hun reachts klik op hun username te gebruiken en op '\\#d0d0ff\\Invite to Game\\#dcdcdc\\' te klikken.\n\nJe kan kanalen van servers ook uitnodigen door op de \\#d0d0ff\\plus\\#dcdcdc\\ knop te drukken naast de plek waar je chat.\n\nGame activiteit \\#ffa0a0\\moet\\#dcdcdc\\ aaan staan in je \nDiscord gebruikers opties.\n\nOp offline staan \\#ffa0a0\\houd uitnodigingen versturen tegen.\\#dcdcdc\\ " WARN_DISCORD2 = "\\#ffa0a0\\Error:\\#dcdcdc\\ Kan Discord niet vinden.\n\\#a0a0a0\\Probeer om het spel af te sluiten, Discord opnieuw opstarten, en het spel weer op starten." -WARN_SOCKET = "Directe verbindingen \\#ffa0a0\\verplichten je\\#dcdcdc\\ om je router te port-forwarden.\n\nForward port '\\#d0d0ff\\%d\\#dcdcdc\\' for UDP." +WARN_SOCKET = "Zorg ervoor dat uw firewall correct is geconfigureerd.\nDirecte verbindingen \\#ffa0a0\\vereisen\\#dcdcdc\\ dat u poortdoorschakeling configureert in uw router om IPv4 inkomende verbindingen te accepteren.\n\nSchakel poort '\\#d0d0ff\\%d\\#dcdcdc\\' door voor UDP. IPv6 wordt ook ondersteund." HOST = "Organisator" [HOST_MODS] diff --git a/lang/English.ini b/lang/English.ini index 5cbb9df6..01b6bc87 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -167,7 +167,7 @@ LOCAL_PLAYER_MODEL_ONLY = "Local Player Model Only" INFO_TITLE = "INFO" WARN_DISCORD = "Invite friends by right clicking their name on Discord and clicking on\n'\\#d0d0ff\\Invite to Game\\#dcdcdc\\'.\n\nYou can invite channels of servers as well by clicking the \\#d0d0ff\\plus\\#dcdcdc\\ button next to the place where you enter chat.\n\nGame Activity \\#ffa0a0\\must be\\#dcdcdc\\ enabled in your\nDiscord user settings.\n\nAppearing offline \\#ffa0a0\\will prevent\\#dcdcdc\\ invites from being sent." WARN_DISCORD2 = "\\#ffa0a0\\Error:\\#dcdcdc\\ Could not detect Discord.\n\n\\#a0a0a0\\Try closing the game,\nrestarting Discord,\nand opening the game again." -WARN_SOCKET = "Direct connections \\#ffa0a0\\require you\\#dcdcdc\\ to configure port forwarding in your router.\n\nForward port '\\#d0d0ff\\%d\\#dcdcdc\\' for UDP." +WARN_SOCKET = "Make sure your firewall is properly configured.\nDirect connections \\#ffa0a0\\requires you\\#dcdcdc\\ to configure port forwarding in your router to accept IPv4 inbound connections.\n\nForward port '\\#d0d0ff\\%d\\#dcdcdc\\' for UDP. IPv6 is also supported." HOST = "Host" [HOST_MODS] diff --git a/lang/French.ini b/lang/French.ini index 55a1fc71..e29c207a 100644 --- a/lang/French.ini +++ b/lang/French.ini @@ -167,7 +167,7 @@ LOCAL_PLAYER_MODEL_ONLY = "Modèle de joueur local seulement" INFO_TITLE = "INFORMATIONS" WARN_DISCORD = "Invitez des amis en faisant un clic droit sur \nleur pseudo Discord puis en cliquant sur \n'\\#d0d0ff\\Inviter à rejoindre\\#dcdcdc\\'.\n\nVous pouvez envoyer des invitations dans les chats de serveurs en cliquant\nsur le bouton \\#d0d0ff\\+\\#dcdcdc\\ à coté de la barre de chat.\n\nLe statut d'activité \\#ffa0a0\\doit-être\\#dcdcdc\\ activé dans les paramètres utilisateurs Discord.\n\nApparaître hors-ligne \\#ffa0a0\\empêchera\\#dcdcdc\\ les invitations\nd'être envoyées." WARN_DISCORD2 = "\\#ffa0a0\\Erreur:\\#dcdcdc\\ Discord n'est pas détecté.\n\n\\#a0a0a0\\Essayez de fermer le jeu,\nrelancer Discord,\net relancer le jeu." -WARN_SOCKET = "La Connexion Directe \\#ffa0a0\\vous oblige\\#dcdcdc\\ à configurer un\nport dans votre routeur.\n\nDéfinissez le port sur '\\#d0d0ff\\%d\\#dcdcdc\\' pour l'UDP." +WARN_SOCKET = "Assurez-vous que votre pare-feu est correctement configuré.\nLes connexions directes \\#ffa0a0\\vous oblige\\#dcdcdc\\ à configurer le transfert de port sur votre routeur pour accepter les connexions entrantes IPv4.\n\nRedirigez le port '\\#d0d0ff\\%d\\#dcdcdc\\' pour UDP. IPv6 est également pris en charge." HOST = "Héberger" [HOST_MODS] diff --git a/lang/German.ini b/lang/German.ini index 52af0e1c..01152f04 100644 --- a/lang/German.ini +++ b/lang/German.ini @@ -167,7 +167,7 @@ LOCAL_PLAYER_MODEL_ONLY = "Nur lokales Spielermodell" INFO_TITLE = "INFO" WARN_DISCORD = "Lade Freunde über Discord ein, indem du rechtsklick auf ihren Namen machst und '\\#d0d0ff\\Zum Spiel einladen\\#dcdcdc\\' auswählst. Kanäle können auch über das \\#d0d0ff\\Plus-Symbol\\#dcdcdc\\ eingeladen werden. Stelle sicher, dass die Spielaktivität in den Discord-Einstellungen aktiviert ist. Wenn du offline angezeigt wirst, kannst du keine Einladungen senden." WARN_DISCORD2 = "\\#ffa0a0\\Fehlermeldung:\\#dcdcdc\\ Discord nicht gefunden. Versuche das Spiel zu schließen, Discord zu starten und dann das Spiel erneut zu öffnen." -WARN_SOCKET = "Für direkte Verbindungen \\#ffa0a0\\musst du\\#dcdcdc\\ die Portweiterleitung in deinem Router konfigurieren. Leite den Port '\\#d0d0ff\\%d\\#dcdcdc\\' für UDP weiter." +WARN_SOCKET = "Stelle sicher, dass Deine Firewall ordnungsgemäß konfiguriert ist. Direkte Verbindungen \\#ffa0a0\\erfordern\\#dcdcdc\\ die Konfiguration der Portweiterleitung (Port Forwarding) in Deinem Router, um eingehende IPv4-Verbindungen zu akzeptieren.\n\nLeite den Port '\\#d0d0ff\\%d\\#dcdcdc\\' für UDP weiter. IPv6 wird ebenfalls unterstützt." HOST = "Hosten" [HOST_MODS] diff --git a/lang/Italian.ini b/lang/Italian.ini index 0894572d..84995d7f 100644 --- a/lang/Italian.ini +++ b/lang/Italian.ini @@ -165,7 +165,7 @@ LOCAL_PLAYER_MODEL_ONLY = "Solo modello giocatore locale" INFO_TITLE = "INFO" WARN_DISCORD = "Invita gli amici facendo tasto destro sul loro nome in Discord e cliccando\n'\\#d0d0ff\\Invito a giocare\\#dcdcdc\\'.\n\npuoi invitare anche i canali dei server cliccando il pulsante \\#d0d0ff\\più\\#dcdcdc\\ vicino al posto dove scrivi.\n\nLo Stato delle Attività \\#ffa0a0\\deve essere\\#dcdcdc\\ attivo nelle\nimpostazioni utente di Discord.\n\nApparire offline \\#ffa0a0\\ti impedirà\\#dcdcdc\\ di inviare inviti." WARN_DISCORD2 = "\\#ffa0a0\\Errore:\\#dcdcdc\\ Impossibile individuare Discord.\n\n\\#a0a0a0\\prova a chiudre il gioco,\nriavviare Discord,\ne aprire di nuovo il gioco." -WARN_SOCKET = "La connessione diretta \\#ffa0a0\\richiede\\#dcdcdc\\ una configurazione port forwarding nel tuo router.\n\nTrasmetti una connessione '\\#d0d0ff\\%d\\#dcdcdc\\' per l'UDP." +WARN_SOCKET = "Assicurati che il tuo firewall sia configurato correttamente.\nLe connessioni dirette \\#ffa0a0\\richiede\\#dcdcdc\\ configurare l'inoltro delle porte nel tuo router per accettare connessioni in entrata IPv4.\n\nInoltra la porta '\\#d0d0ff\\%d\\#dcdcdc\\' per UDP. IPv6 è anche supportato." HOST = "Crea" [HOST_MODS] diff --git a/lang/Polish.ini b/lang/Polish.ini index c038876f..3c8b999b 100644 --- a/lang/Polish.ini +++ b/lang/Polish.ini @@ -167,7 +167,7 @@ LOCAL_PLAYER_MODEL_ONLY = "Tylko lokalny model gracza" INFO_TITLE = "INFORMACJA" WARN_DISCORD = "Zaproś znajomych do gry, klikając PPM w ich nazwę na Discordzie, a potem klikając\n'\\#d0d0ff\\Zaproś do gry\\#c8c8c8\\'.\n\nMożesz zapraszać na kanałach serwerów klikając w \\#d0d0ff\\plusik\\#c8c8c8\\ obok paska czatu.\n\n \\#ffa0a0\\Należy\\#c8c8c8\\ mieć włączoną Aktywność w grze w\nUstawieniach użytkownika Discorda.\n\nTryb offline \\#ffa0a0\\uniemożliwi\\#c8c8c8\\ wysyłanie zaproszeń." WARN_DISCORD2 = "\\#ffa0a0\\Błąd:\\#c8c8c8\\ Nie wykryto Discorda.\n\n\\#a0a0a0\\Spróbuj zamknąć grę,\nzrestartować Discorda\ni uruchomić grę ponownie." -WARN_SOCKET = "Połączenie bezpośrednie \\#ffa0a0\\wymaga\\#c8c8c8\\ konfiguracji przekierowania portów w routerze.\n\nPrzekieruj port '\\#d0d0ff\\%d\\#c8c8c8\\' dla UDP." +WARN_SOCKET = "Upewnij się, że twoja zapora jest poprawnie skonfigurowana.\nBezpośrednie połączenia \\#ffa0a0\\wymagają\\#dcdcdc\\ skonfigurowania przekierowania portów w routerze, aby akceptować przychodzące połączenia IPv4.\n\nPrzekieruj port '\\#d0d0ff\\%d\\#dcdcdc\\' dla UDP. IPv6 jest również obsługiwane." HOST = "Hostuj" [HOST_MODS] diff --git a/lang/Portuguese.ini b/lang/Portuguese.ini index 1b3c57cf..e756ea23 100644 --- a/lang/Portuguese.ini +++ b/lang/Portuguese.ini @@ -167,7 +167,7 @@ LOCAL_PLAYER_MODEL_ONLY = "Apenas modelo de jogador local" INFO_TITLE = "INFO" WARN_DISCORD = "Convide amigos clicando com o botão direito do mouse no nome deles no Discord e clicando em\n'\\#d0d0ff\\convidar para o jogo\\#dcdcdc\\'.\n\n Você também pode convidar em canais de servidores clicando no símbolo de \\#d0d0ff\\mais\\#dcdcdc\\ na caixa de texto abaixo das mensagens.\n\nÉ preciso configurar a \\#ffa0a0\\privacidade das atividades\\#dcdcdc\\ nas suas\n configurações do Discord.\n\n Se o seu status estiver como offline, \\#ffa0a0\\não poderá\\#dcdcdc\\ enviar convites." WARN_DISCORD2 = "\\#ffa0a0\\Erro:\\#dcdcdc\\ Discord não detectado.\n\n\\#a0a0a0\\tente fechar o jogo,\nreiniciar o Discord,\n e abrir o jogo novamente." -WARN_SOCKET = "Você precisa \\#ffa0a0\\configurar o encaminhamento de porta em seu roteador\\#dcdcdc\\ para usar a conexão direta.\n\nAbre porta '\\#d0d0ff\\%d\\#dcdcdc\\' no protocolo UDP." +WARN_SOCKET = "Verifique se o seu firewall está bem configurado.\nAo usar conexão direta, você precisa \\#ffa0a0\\configurar o encaminhamento de porta em seu roteador\\#dcdcdc\\ para aceitar conexões entrantes em IPv4.\n\nEncaminhe a porta '\\#d0d0ff\\%d\\#dcdcdc\\' para UDP. IPv6 também é suportado." HOST = "Criar" [HOST_MODS] diff --git a/lang/Russian.ini b/lang/Russian.ini index 5fbf4ced..f03f4759 100644 --- a/lang/Russian.ini +++ b/lang/Russian.ini @@ -166,7 +166,7 @@ LOCAL_PLAYER_MODEL_ONLY = "Только локальная модель игро INFO_TITLE = "INFO" WARN_DISCORD = "Пригласите друзей, щелкнув правой кнопкой мыши их имя в Дискорд, и, выбрав\n'\\#d0d0ff\\Пригласить в игру\\#dcdcdc\\'.\n\nВы также можете пригласить каналы серверов, нажав кнопку \\#d0d0ff\\плюс,\\#dcdcdc\\ кнопку рядом с местом входа в чат.\n\nИгровая активность \\#ffa0a0\\должна быть\\#dcdcdc\\ включена в ваших\n настройках Дискорда.\n\nИспользование офлайн статуса \\#ffa0a0\\предотвратит отправку \\#dcdcdc\\ приглашений." WARN_DISCORD2 = "\\#ffa0a0\\Error:\\#dcdcdc\\ Не удалось обнаружить Дискорд.\n\n\\#a0a0a0\\Попробуйте закрыть игру,\nперезапустите Дискорд,\nи снова откройте игру." -WARN_SOCKET = "Локальные соединения\\#ffa0a0\\требуют\\#dcdcdc\\ настроить переадресацию портов в роутере.\n\nПереадресация портов '\\#d0d0ff\\%d\\#dcdcdc\\' для UDP." +WARN_SOCKET = "Убедитесь, что ваш файрвол настроен правильно.\nПрямые подключения \\#ffa0a0\\требуют от вас\\#dcdcdc\\ настройки проброса портов на вашем маршрутизаторе для приeма входящих подключений по IPv4.\n\nПеренаправьте порт '\\#d0d0ff\\%d\\#dcdcdc\\' для UDP. IPv6 также поддерживается." HOST = "Хост" [HOST_MODS] diff --git a/lang/Spanish.ini b/lang/Spanish.ini index de970f56..90f9fc7d 100644 --- a/lang/Spanish.ini +++ b/lang/Spanish.ini @@ -167,7 +167,7 @@ LOCAL_PLAYER_MODEL_ONLY = "Solo modelo de jugador local" INFO_TITLE = "INFO" WARN_DISCORD = "Invita a amigos haciendo click derecho en su nombre en Discord y seleccionando\n'\\#d0d0ff\\Invitar a unirse\\#dcdcdc\\'.\n\nPuedes invitar en canales de un servidor también presionando el botón \\#d0d0ff\\+\\#dcdcdc\\ al lado del cuadro de texto del chat.\n\nEl estado de Actividad Actual \\#ffa0a0\\debe estar\\#dcdcdc\\ activado en tus ajustes de Discord.\n\nEstar invisible \\#ffa0a0\\te prevendrá\\#dcdcdc\\ de crear invitaciones." WARN_DISCORD2 = "\\#ffa0a0\\Error:\\#dcdcdc\\ No se ha detectado Discord.\n\n\\#a0a0a0\\Prueba a cerrar el juego,\nreiniciar Discord,\ny abrir el juego de nuevo." -WARN_SOCKET = "Las conexiones directas \\#ffa0a0\\requieren\\#dcdcdc\\ que abras los puertos en tu router.\n\nAbre el puerto '\\#d0d0ff\\%d\\#dcdcdc\\' con protocolo UDP." +WARN_SOCKET = "Asegúrate de que tu firewall esté configurado correctamente.\nLas conexiones directas \\#ffa0a0\\requieren que\\#dcdcdc\\ configures el reenvío de puertos en tu router para aceptar conexiones entrantes IPv4.\n\nReenvía el puerto '\\#d0d0ff\\%d\\#dcdcdc\\' para UDP. IPv6 también es suportado." HOST = "Crear" [HOST_MODS] diff --git a/src/pc/djui/djui_panel_host_message.c b/src/pc/djui/djui_panel_host_message.c index 283b5a80..af08f5fc 100644 --- a/src/pc/djui/djui_panel_host_message.c +++ b/src/pc/djui/djui_panel_host_message.c @@ -51,9 +51,9 @@ void djui_panel_host_message_create(struct DjuiBase* caller) { char* warningMessage = NULL; bool hideHostButton = false; - warningLines = 5; - warningMessage = calloc(256, sizeof(char)); - snprintf(warningMessage, 256, DLANG(HOST_MESSAGE, WARN_SOCKET), configHostPort); + warningLines = 7; + warningMessage = calloc(512, sizeof(char)); + snprintf(warningMessage, 512, DLANG(HOST_MESSAGE, WARN_SOCKET), configHostPort); f32 textHeight = 32 * 0.8125f * warningLines + 8; diff --git a/src/pc/djui/djui_panel_join_direct.c b/src/pc/djui/djui_panel_join_direct.c index a2690505..433609e8 100644 --- a/src/pc/djui/djui_panel_join_direct.c +++ b/src/pc/djui/djui_panel_join_direct.c @@ -98,25 +98,70 @@ static void djui_panel_join_direct_ip_text_change(struct DjuiBase* caller) { static void djui_panel_join_direct_ip_text_set_new(void) { char buffer[256] = { 0 }; + char orig_ip[256] = { 0 }; if (snprintf(buffer, 256, "%s", sInputboxIp->buffer) < 0) { LOG_INFO("truncating IP"); } + // copy original buffer for storing to gGetHostName + memcpy(&orig_ip, &buffer, 256); + bool afterSpacer = false; + bool is_ipv6 = false; int port = 0; + + // check if address starts with [ (meaning it's a direct IPv6 address. + // This is needed because we need to know when to get the port number. Example: [2001:db8::1000]:7777 + // If this character is not in the first character in the buffer, it will be treated as an IPv4 address or hostname. + if (buffer[0] == '[') { + memcpy(&buffer, &buffer[1], 255); + is_ipv6 = true; + } + + if (is_ipv6) { + LOG_INFO("Detected direct IPv6 address"); + } else { + LOG_INFO("Detected direct IPv4 address or hostname"); + } + + // this needs cleaning for (int i = 0; i < 256; i++) { - if (buffer[i] == ' ' || buffer[i] == ':') { - buffer[i] = '\0'; - afterSpacer = true; - } else if (buffer[i] == '\0') { - break; - } else if (afterSpacer && buffer[i] >= '0' && buffer[i] <= '9') { - port *= 10; - port += buffer[i] - '0'; + // Direct IPv6 address + if (is_ipv6 == true) { + // Check if it reached end of address "]:", or a space as a fail safe. + if ((buffer[i] == ']') || buffer[i] == ' ') { + afterSpacer = true; + memset(&orig_ip, 0, 256); + memcpy(&orig_ip[1], &buffer, i+1); + buffer[i] = '\0'; + orig_ip[0] = '['; + // skip over the port separator + if (buffer[i+1] == ':') { + i += 1; + } + } else if (buffer[i] == '\0') { + break; + } else if (afterSpacer && buffer[i] >= '0' && buffer[i] <= '9') { + port *= 10; + port += buffer[i] - '0'; + } + } else { + // Direct IPv4 address or hostname + // Check if it reached end of address ":", or a space as a fail safe. + if (buffer[i] == ' ' || buffer[i] == ':') { + afterSpacer = true; + buffer[i] = '\0'; + memcpy(&orig_ip, &buffer, i+1); + } else if (buffer[i] == '\0') { + break; + } else if (afterSpacer && buffer[i] >= '0' && buffer[i] <= '9') { + port *= 10; + port += buffer[i] - '0'; + } } } - snprintf(gGetHostName, MAX_CONFIG_STRING, "%s", buffer); + snprintf(gGetHostName, MAX_CONFIG_STRING, "%s", orig_ip); if (snprintf(configJoinIp, MAX_CONFIG_STRING, "%s", buffer) < 0) { LOG_INFO("truncating IP"); } diff --git a/src/pc/network/socket/socket.c b/src/pc/network/socket/socket.c index c32447ad..edf17920 100644 --- a/src/pc/network/socket/socket.c +++ b/src/pc/network/socket/socket.c @@ -5,28 +5,91 @@ #include "pc/djui/djui.h" static SOCKET sCurSocket = INVALID_SOCKET; -static struct sockaddr_in sAddr[MAX_PLAYERS] = { 0 }; +static struct sockaddr_in6 sAddr[MAX_PLAYERS] = { 0 }; +struct addrinfo hints; +struct addrinfo *result, *i; char gGetHostName[MAX_CONFIG_STRING] = ""; -void resolve_domain(void) { - struct hostent *remoteHost = gethostbyname(configJoinIp); - if (remoteHost && remoteHost->h_addrtype == AF_INET) { - struct in_addr addr; - for (int i = 0; remoteHost->h_addr_list[i] != 0; i++) { - memcpy(&addr, remoteHost->h_addr_list[i], sizeof(struct in_addr)); - snprintf(configJoinIp, MAX_CONFIG_STRING, "%s", inet_ntoa(addr)); +// Resolves a hostname to an IP address. Current limitation: It still only gets the first address it sees and returns. +// getaddrinfo() is smart enough to prioritize IPv4 if the user is not in an IPv6 enabled network, so this shouldn't be a problem for now. +// TODO: Store all found addresses somewhere and make the game try to connect to each of them if one fails. +void resolve_domain(struct sockaddr_in6 *addr) { + // non zero value if getaddrinfo throws an error. + int error; + + // set hints + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_DGRAM; + + // sanity check: remove square brackets from configJoinIp. getaddrinfo doesn't like those, at least on Linux. + if (configJoinIp[0] == '[') { + LOG_INFO("sanity check: found opening square bracket on configJoinIp, removing it."); + for (int i = 0; i < MAX_CONFIG_STRING; i++) { + if (configJoinIp[i] == '\0') { break; } + if (configJoinIp[i] == ']') { + configJoinIp[i] = '\0'; + memcpy(&configJoinIp, &configJoinIp[1], MAX_CONFIG_STRING-1); + break; + } } } + + // Get host addresses + error = getaddrinfo(configJoinIp, NULL, &hints, &result); + + // If it was successful: + if (error == 0) { + // Iterate through results + for (i = result; i != NULL; i = i->ai_next) { + // buffer for IPv6 address + char str[INET6_ADDRSTRLEN]; + // IPv6 address: + if (i->ai_addr->sa_family == AF_INET6) { + struct sockaddr_in6 *p = (struct sockaddr_in6 *)i->ai_addr; + // copy address to sockaddr_in6 struct + memcpy(&addr->sin6_addr, &p->sin6_addr, sizeof(struct in6_addr)); + // set new join IP for config file + snprintf(configJoinIp, MAX_CONFIG_STRING, "%s", inet_ntop(AF_INET6, &p->sin6_addr, str, sizeof(str))); + // Free results from memory and return + freeaddrinfo(result); + return; + + } else if (i->ai_addr->sa_family == AF_INET) { // IPv4 address. Convert it to an IPv6-mapped IPv4 address so it's compatible with the IPv6 socket. + struct sockaddr_in *p = (struct sockaddr_in *)i->ai_addr; + struct in6_addr ipv6_mapped_addr; + // clear out IPv6-mapped IPv4 address buffer + memset(&ipv6_mapped_addr, 0, sizeof(struct in6_addr)); + // ::ffff: Prefix + ipv6_mapped_addr.s6_addr[10] = 0xff; + ipv6_mapped_addr.s6_addr[11] = 0xff; + // then copy the IPv4 address to the end of the IPv6 address. The address is now properly formed. + memcpy(&ipv6_mapped_addr.s6_addr[12], &p->sin_addr, sizeof(p->sin_addr)); + // copy address to sockaddr_in6 struct + memcpy(&addr->sin6_addr, &ipv6_mapped_addr, sizeof(struct in6_addr)); + + // set new join IP for config file + snprintf(configJoinIp, MAX_CONFIG_STRING, "%s", inet_ntop(AF_INET6, &ipv6_mapped_addr, str, sizeof(str))); + // Free results from memory and return + freeaddrinfo(result); + return; + } + } + } else { + LOG_ERROR("getaddrinfo() failed with error code %i: %s", error, gai_strerror(error)); + } } static int socket_bind(SOCKET socket, unsigned int port) { - struct sockaddr_in rxAddr; - rxAddr.sin_family = AF_INET; - rxAddr.sin_port = htons(port); - rxAddr.sin_addr.s_addr = htonl(INADDR_ANY); + struct sockaddr_in6 rxAddr; + // Clean struct to prevent rare cases of binding errors due to garbage data in that memory location. This just happened to me randomly on Windows and left me very confused. + memset(&rxAddr, 0, sizeof(struct sockaddr_in6)); + rxAddr.sin6_family = AF_INET6; + rxAddr.sin6_port = htons(port); + rxAddr.sin6_addr = in6addr_any; + + int rc = bind(socket, (SOCKADDR *)&rxAddr, sizeof(rxAddr)); - int rc = bind(socket, (SOCKADDR*)&rxAddr, sizeof(rxAddr)); if (rc != 0) { LOG_ERROR("bind failed with error %d", SOCKET_LAST_ERROR); } @@ -34,8 +97,8 @@ static int socket_bind(SOCKET socket, unsigned int port) { return rc; } -static int socket_send(SOCKET socket, struct sockaddr_in* addr, u8* buffer, u16 bufferLength) { - int addrSize = sizeof(struct sockaddr_in); +static int socket_send(SOCKET socket, struct sockaddr_in6* addr, u8* buffer, u16 bufferLength) { + int addrSize = sizeof(struct sockaddr_in6); int rc = sendto(socket, (char*)buffer, bufferLength, 0, (struct sockaddr*)addr, addrSize); if (rc != SOCKET_ERROR) { return NO_ERROR; } @@ -46,14 +109,14 @@ static int socket_send(SOCKET socket, struct sockaddr_in* addr, u8* buffer, u16 return rc; } -static int socket_receive(SOCKET socket, struct sockaddr_in* rxAddr, u8* buffer, u16 bufferLength, u16* receiveLength, u8* localIndex) { +static int socket_receive(SOCKET socket, struct sockaddr_in6* rxAddr, u8* buffer, u16 bufferLength, u16* receiveLength, u8* localIndex) { *receiveLength = 0; - RX_ADDR_SIZE_TYPE rxAddrSize = sizeof(struct sockaddr_in); + RX_ADDR_SIZE_TYPE rxAddrSize = sizeof(struct sockaddr_in6); int rc = recvfrom(socket, (char*)buffer, bufferLength, 0, (struct sockaddr*)rxAddr, &rxAddrSize); for (int i = 1; i < MAX_PLAYERS; i++) { - if (memcmp(rxAddr, &sAddr[i], sizeof(struct sockaddr_in)) == 0) { + if (memcmp(rxAddr, &sAddr[i], sizeof(struct sockaddr_in6)) == 0) { *localIndex = i; break; } @@ -85,30 +148,35 @@ static bool ns_socket_initialize(enum NetworkType networkType, UNUSED bool recon int reuse = 1; if (setsockopt(sCurSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0) { LOG_ERROR("setsockopt(SO_REUSEADDR) failed"); - } + } #ifdef SO_REUSEPORT if (setsockopt(sCurSocket, SOL_SOCKET, SO_REUSEPORT, (const char*)&reuse, sizeof(reuse)) < 0) { LOG_ERROR("setsockopt(SO_REUSEPORT) failed"); } #endif - // bind the socket to any address and the specified port. int rc = socket_bind(sCurSocket, port); - if (rc != NO_ERROR) { return false; } + if (rc != NO_ERROR) { + LOG_ERROR("bind returned an error."); + return false; + } LOG_INFO("bound to port %u", port); - } else { + } else if (networkType == NT_CLIENT) { + struct sockaddr_in6 addr; + // set and clean struct to prevent garbage data + memset(&addr, 0, sizeof(struct sockaddr_in6)); // save the port to send to - sAddr[0].sin_family = AF_INET; - sAddr[0].sin_port = htons(port); - resolve_domain(); - sAddr[0].sin_addr.s_addr = inet_addr(configJoinIp); - LOG_INFO("connecting to %s %u", configJoinIp, port); + sAddr[0].sin6_family = AF_INET6; + sAddr[0].sin6_port = htons(port); + // resolve and get address list to connect + resolve_domain(&addr); + sAddr[0].sin6_addr = addr.sin6_addr; + LOG_INFO("connecting to %s, port %u", configJoinIp, port); + // copy hostname to be saved to config file snprintf(configJoinIp, MAX_CONFIG_STRING, "%s", gGetHostName); - } - // kick off first packet - if (networkType == NT_CLIENT) { + // kick off first packet char joinText[128] = { 0 }; snprintf(joinText, 63, "%s %d", configJoinIp, configJoinPort); djui_connect_menu_open(); @@ -132,8 +200,8 @@ static s64 ns_socket_get_id(UNUSED u8 localId) { static char* ns_socket_get_id_str(u8 localId) { if (localId == UNKNOWN_LOCAL_INDEX) { localId = 0; } - static char id_str[INET_ADDRSTRLEN] = { 0 }; - snprintf(id_str, INET_ADDRSTRLEN, "%s", inet_ntoa(sAddr[localId].sin_addr)); + static char id_str[INET6_ADDRSTRLEN] = { 0 }; + snprintf(id_str, INET6_ADDRSTRLEN, "%s", inet_ntop(AF_INET6, &sAddr[localId].sin6_addr, id_str, sizeof(id_str))); return id_str; } @@ -147,18 +215,18 @@ static void ns_socket_save_id(u8 localId, UNUSED s64 networkId) { static void ns_socket_clear_id(u8 localId) { if (localId == 0) { return; } SOFT_ASSERT(localId < MAX_PLAYERS); - memset(&sAddr[localId], 0, sizeof(struct sockaddr_in)); + memset(&sAddr[localId], 0, sizeof(struct sockaddr_in6)); LOG_INFO("cleared addr for id %d", localId); } static void* ns_socket_dup_addr(u8 localIndex) { - void* address = malloc(sizeof(struct sockaddr_in)); - memcpy(address, &sAddr[localIndex], sizeof(struct sockaddr_in)); + void* address = malloc(sizeof(struct sockaddr_in6)); + memcpy(address, &sAddr[localIndex], sizeof(struct sockaddr_in6)); return address; } static bool ns_socket_match_addr(void* addr1, void* addr2) { - return !memcmp(addr1, addr2, sizeof(struct sockaddr_in)); + return !memcmp(addr1, addr2, sizeof(struct sockaddr_in6)); } static void ns_socket_update(void) { @@ -181,8 +249,8 @@ static int ns_socket_send(u8 localIndex, void* address, u8* data, u16 dataLength if (gNetworkType == NT_CLIENT && gNetworkPlayers[localIndex].type != NPT_SERVER) { return SOCKET_ERROR; } } - struct sockaddr_in* userAddr = &sAddr[localIndex]; - if (localIndex == 0 && address != NULL) { userAddr = (struct sockaddr_in*)address; } + struct sockaddr_in6* userAddr = &sAddr[localIndex]; + if (localIndex == 0 && address != NULL) { userAddr = (struct sockaddr_in6*)address; } int rc = socket_send(sCurSocket, userAddr, data, dataLength); if (rc) { @@ -203,7 +271,7 @@ static void ns_socket_shutdown(UNUSED bool reconnecting) { socket_shutdown(sCurSocket); sCurSocket = INVALID_SOCKET; for (u16 i = 0; i < MAX_PLAYERS; i++) { - memset(&sAddr[i], 0, sizeof(struct sockaddr_in)); + memset(&sAddr[i], 0, sizeof(struct sockaddr_in6)); } LOG_INFO("shutdown"); } diff --git a/src/pc/network/socket/socket_linux.c b/src/pc/network/socket/socket_linux.c index 5ab75ad0..a1903b07 100644 --- a/src/pc/network/socket/socket_linux.c +++ b/src/pc/network/socket/socket_linux.c @@ -5,7 +5,7 @@ SOCKET socket_initialize(void) { // initialize socket - SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + SOCKET sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (sock == INVALID_SOCKET) { LOG_ERROR("socket failed with error %d", SOCKET_LAST_ERROR); return INVALID_SOCKET; @@ -18,6 +18,15 @@ SOCKET socket_initialize(void) { return INVALID_SOCKET; } + // Set socket to dual-stack + int v6only = 0; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&v6only, sizeof(v6only)) < 0) { + LOG_ERROR("setsockopt(IPV6_V6ONLY) failed."); + return INVALID_SOCKET; + }; + + LOG_INFO("socket initialized."); + return sock; } diff --git a/src/pc/network/socket/socket_windows.c b/src/pc/network/socket/socket_windows.c index 635b96c3..4435848d 100644 --- a/src/pc/network/socket/socket_windows.c +++ b/src/pc/network/socket/socket_windows.c @@ -13,7 +13,7 @@ SOCKET socket_initialize(void) { } // initialize socket - SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + SOCKET sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); if (sock == INVALID_SOCKET) { LOG_ERROR("socket failed with error %d", SOCKET_LAST_ERROR); return INVALID_SOCKET; @@ -27,6 +27,13 @@ SOCKET socket_initialize(void) { return INVALID_SOCKET; } + // set dual-stack socket mode + int v6only = 0; + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&v6only, sizeof(v6only)) < 0) { + LOG_ERROR("setsockopt(IPV6_V6ONLY) failed."); + return INVALID_SOCKET; + }; + #if MAX_PLAYERS > 4 // on windows, the send buffer for the socket needs to be increased // for the many players case to avoid WSAEWOULDBLOCK on send