/*---------------------------------------------------------------------------* Project: Horizon File: NtpClient.cpp Copyright 2009 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev$ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include "Importer.h" #include "CommonLogger.h" namespace ConsoleRestore { namespace { const size_t NTP_THREAD_STACK_SIZE = 0x1000; nn::os::Thread s_NtpThread; nn::os::StackBuffer s_NtpThreadStack; bool s_NtpSyncSuccessed = false; struct NTP_Packet{ // NTPパケット u32 controlWord; u32 rootDelay; u32 rootDispersion; u32 referenceId; s64 referenceTimestamp; s64 startTimestamp; s64 receiveTimestamp; u32 transmitTimestampSeconds; u32 transmitTimestampFractions; }; const size_t TIMEOUT_MILLISECOND = 5000; // タイムアウト ミリ秒数 NTP_Packet s_NTPSendPacket; // 送信するNTPパケット NTP_Packet s_NTPRecvPacket; // 受信するNTPパケット const u32 NTP_PORT_NUM = 123; nn::Result InitializeNetwork(void) { nn::Result result; nn::ac::Config config; result = nn::ac::Initialize(); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); // 接続要求用のパラメータを作成 result = nn::ac::CreateDefaultConfig(&config); if (result.IsFailure()) { COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); return result; } // デバッグ用に接続テストを無効化 nn::ac::DebugSetNetworkArea(&config, nn::ac::NETWORK_AREA_LAN); // 接続要求を発行 result = nn::ac::ConnectWithoutEula(config); if (result.IsFailure()) { COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); return result; } return nn::ResultSuccess(); } nn::Result FinalizeNetwork(void) { nn::Result result; // 接続要求用のパラメータを作成 result = nn::ac::Close(); NN_UTIL_RETURN_IF_FAILED(result); result = nn::ac::Finalize(); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); return nn::ResultSuccess(); } bool GetNtpTime(u32* ntpTime) { nn::Result result; bool retval = true; NN_LOG("Initializing network.\n"); // 本体に書き込まれているネットワーク設定を使ってネットワーク接続を初期化 result = InitializeNetwork(); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); { NN_LOG("Initializing socket..\n"); // 一つのスレッドからソケット API を利用する const s32 sessionCount = 1; // ソケットの送受信バッファとして 64 KB を割り当て const size_t bufferSizeForSockets = 65536; // ソケットライブラリに必要なワークサイズを求める const size_t workSizeForLibrary = nn::socket::GetRequiredMemorySize(bufferSizeForSockets, sessionCount); // ワークメモリを確保して 4KB にアラインにする u8* pWorkMemory = new u8[workSizeForLibrary + 4096]; uptr workMemoryAddress = nn::math::RoundUp(reinterpret_cast (pWorkMemory), 4096); // ソケットライブラリの初期化 result = nn::socket::Initialize(workMemoryAddress, workSizeForLibrary, bufferSizeForSockets, sessionCount); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); { s32 ret; nn::socket::InAddr addr, netmask; ret = nn::socket::GetPrimaryAddress(reinterpret_cast (&addr), reinterpret_cast (&netmask)); NN_ASSERT(ret == 0); COMMON_LOGGER("host : %s\n", nn::socket::InetNtoA(addr)); COMMON_LOGGER("netmask : %s\n", nn::socket::InetNtoA(netmask)); nn::socket::InAddr dns1, dns2; ret = nn::socket::GetResolver(reinterpret_cast (&dns1), reinterpret_cast (&dns2)); if (ret == 0) { COMMON_LOGGER("dns1 : %s\n", nn::socket::InetNtoA(dns1)); COMMON_LOGGER("dns2 : %s\n", nn::socket::InetNtoA(dns2)); } nn::socket::InAddr gateway; ret = nn::socket::GetDefaultGateway(reinterpret_cast (&gateway)); if (ret == 0) { COMMON_LOGGER("gateway : %s\n", nn::socket::InetNtoA(gateway)); } COMMON_LOGGER("\n"); #ifndef NN_SWITCH_DISABLE_DEBUG_PRINT nn::socket::DumpRoutingTable(); #endif } { s32 socket = nn::socket::Socket(nn::socket::PF_INET, nn::socket::SOCK_DGRAM, 0); NN_LOG("socket = %d\n", socket); // クライアントアドレスの設定 nn::socket::SockAddrIn host_addr; host_addr.len = sizeof(nn::socket::SockAddrIn); host_addr.family = nn::socket::AF_INET; host_addr.addr.addr = 0; host_addr.port = nn::socket::HtoNs(NTP_PORT_NUM); // ローカルアドレスをバインド s32 ret = nn::socket::Bind(socket, &host_addr); NN_LOG("bind = %d\n", ret); // ******************************************************************************** // NTPパケットを生成して送る // ******************************************************************************** // サーバアドレスの設定 nn::socket::SockAddrIn serverSockAddrIn; serverSockAddrIn.len = sizeof(nn::socket::SockAddrIn); serverSockAddrIn.family = nn::socket::AF_INET; // GetHostByNameを使う場合 nn::socket::HostEnt* serverHostent; u64 serveraddr = 0; serverHostent = nn::socket::GetHostByName(GetNtpServerName()); if (serverHostent == NULL) { COMMON_LOGGER("Error: GetHostByName %s\n", GetNtpServerName()); retval = false; } else { // サーバのホスト情報からIPアドレスをコピー serveraddr = *(reinterpret_cast (serverHostent->addrList[0])); } serverSockAddrIn.addr.addr = serveraddr; COMMON_LOGGER("Destination address: %s\n", nn::socket::InetNtoA(serverSockAddrIn.addr)); serverSockAddrIn.port = nn::socket::HtoNs(NTP_PORT_NUM); // ポート番号 // NTPパケットをSNTP用に初期化する s_NTPSendPacket.controlWord = nn::socket::HtoNl(0x0B000000); s_NTPSendPacket.rootDelay = 0; s_NTPSendPacket.rootDispersion = 0; s_NTPSendPacket.referenceId = 0; s_NTPSendPacket.referenceTimestamp = 0; s_NTPSendPacket.startTimestamp = 0; s_NTPSendPacket.receiveTimestamp = 0; s_NTPSendPacket.transmitTimestampSeconds = 0; s_NTPSendPacket.transmitTimestampFractions = 0; // サーバを指定してNTPパケットを送信する if ((ret = nn::socket::SendTo(socket, reinterpret_cast (&s_NTPSendPacket), sizeof(s_NTPSendPacket), 0, &serverSockAddrIn)) < 0) { COMMON_LOGGER("Error: Failed Send to Server, %d\n", ret); retval = false; } NN_LOG("SendTo finished\n"); // 受信待ち nn::socket::PollFd pollFd; pollFd.fd = socket; pollFd.events = nn::socket::POLLRDNORM; if ((ret = nn::socket::Poll(&pollFd, 1, TIMEOUT_MILLISECOND)) < 0) { COMMON_LOGGER("Error: recv error, %d\n", ret); retval = false; } NN_LOG("Poll Finished\n"); switch (pollFd.revents) { case nn::socket::POLLERR: // ソケットにエラーが発生しました。 COMMON_LOGGER("Error: POLLERR %s %d\n", __FILE__, __LINE__); retval = false; break; case nn::socket::POLLHUP: // ストリーム・ソケットが未接続です。 COMMON_LOGGER("Error: POLLHUP %s %d\n", __FILE__, __LINE__); retval = false; break; case nn::socket::POLLNVAL: // 不正なソケット記述子です。 COMMON_LOGGER("Error: POLLNVAL %s %d\n", __FILE__, __LINE__); retval = false; break; default: break; } // サーバから時刻情報を受信する // サーバを指定して受信を行う // 受信するまで待たされる if ((ret = nn::socket::RecvFrom(socket, reinterpret_cast (&s_NTPRecvPacket), sizeof(s_NTPRecvPacket), nn::socket::MSG_DONTWAIT, &serverSockAddrIn)) < 0) { COMMON_LOGGER("Error: RecvFrom, %d\n", ret); retval = false; } NN_LOG("RecvFrom finished\n"); // NTPサーバから取得した時刻を現地時間に変換する *ntpTime = nn::socket::NtoHl(s_NTPRecvPacket.transmitTimestampSeconds) - 2208988800; /* 1970/01/01 からの秒数に変換 */ NN_LOG("ntp_time = %d\n", ntpTime); nn::socket::Close(socket); NN_UNUSED_VAR(ret); } NN_LOG("Finalizing socket..\n"); // ソケットライブラリの終了 result = nn::socket::Finalize(); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); } NN_LOG("Finalizing network.\n"); result = FinalizeNetwork(); COMMON_LOGGER_RESULT_IF_FAILED_WITH_LINE(result); return retval; } void RestoreCurrentInternetSetting() { COMMON_LOGGER("Restore Current Internet Setting\n"); nn::Result result; if (GetTempNetworkSetting()->isValid) { result = nn::ac::CTR::UpdateNetworkSetting(0, GetTempNetworkSetting()->setting); COMMON_LOGGER_RESULT_IF_FAILED(result); } else { // 無効の場合は消去しておく result = nn::ac::CTR::RemoveNetworkSetting(0); COMMON_LOGGER_RESULT_IF_FAILED(result); } result = nn::ac::FlushNetworkSetting(); COMMON_LOGGER_RESULT_IF_FAILED(result); result = nn::ac::FinalizeInternal(); COMMON_LOGGER_RESULT_IF_FAILED(result); } } void NtpThreadFunc() { // NTP時間を取得する u32 ntpTime; if (GetNtpTime(&ntpTime)) { // タイムゾーンを考慮してDateTimeに変換する TimeZone timeZone = GetTimeZone(); // 1970/01/01 nn::fnd::DateTime utc = nn::fnd::DateTime(1970, 1, 1, 0, 0, 0, 0); nn::fnd::DateTime current = utc + nn::fnd::TimeSpan::FromSeconds(ntpTime); if (timeZone.isMinus) { current -= (nn::fnd::TimeSpan::FromHours(timeZone.hour) + nn::fnd::TimeSpan::FromMinutes(timeZone.minutes)); } else { current += nn::fnd::TimeSpan::FromHours(timeZone.hour) + nn::fnd::TimeSpan::FromMinutes(timeZone.minutes); } // SWCを書き込む nn::ptm::CTR::SetUserTime(current); COMMON_LOGGER("Set User Time %04d/%02d/%02d %02d:%02d:%02d\n", current.GetYear(), current.GetMonth(), current.GetDay(), current.GetHour(), current.GetMinute(), current.GetSecond()); s_NtpSyncSuccessed = true; } else { COMMON_LOGGER("Failed Get Ntp Time\n"); s_NtpSyncSuccessed = false; } // インターネット設定を元に戻す RestoreCurrentInternetSetting(); } bool IsTimeAdjustFinished() { // Initialize済みかつ終了 return s_NtpThread.IsValid() && !s_NtpThread.IsAlive(); } bool IsTimeAdjustSuccessed() { return s_NtpSyncSuccessed; } void AdjustTime() { nn::Result result; result = nn::ac::CTR::InitializeInternal(); COMMON_LOGGER_RESULT_IF_FAILED(result); if(IsTimeAdjustFinished()) { s_NtpThread.Join(); s_NtpThread.Finalize(); } s_NtpThread.Start( NtpThreadFunc, s_NtpThreadStack); } }