From 91dbebbcc5007c3286f0f9948c2225ff5b5c8260 Mon Sep 17 00:00:00 2001
From: Ryan Loebs <obsidianx@gmail.com>
Date: Mon, 28 Mar 2016 23:34:34 -0700
Subject: [PATCH 1/9] SOC Updates

-Implement GetSockOpt / SetSockOpt
-Fix bug in RecvFrom where sending from localhost
 does not fill in src_addr/src_addr_len on Linux
---
 src/core/hle/service/soc_u.cpp | 49 +++++++++++++++++++++++++++++++---
 1 file changed, 46 insertions(+), 3 deletions(-)

diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index ff0af8f12..efda8bd4f 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -568,7 +568,7 @@ static void RecvFrom(Service::Interface* self) {
     socklen_t src_addr_len = sizeof(src_addr);
     int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len);
 
-    if (buffer_parameters.output_src_address_buffer != 0) {
+    if (buffer_parameters.output_src_address_buffer != 0 && src_addr_len > 0) {
         CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(buffer_parameters.output_src_address_buffer));
         *ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
     }
@@ -724,6 +724,49 @@ static void ShutdownSockets(Service::Interface* self) {
     cmd_buffer[1] = 0;
 }
 
+static void GetSockOpt(Service::Interface* self) {
+    u32* cmd_buffer = Kernel::GetCommandBuffer();
+    u32 socket_handle = cmd_buffer[1];
+    u32 level = cmd_buffer[2];
+    u32 optname = cmd_buffer[3];
+    u32 optlen = cmd_buffer[4];
+
+    // 0x100 = static buffer offset (bytes)
+    // + 0x4 = 2nd pointer (u32) position
+    // >> 2  = convert to u32 offset instead of byte offset (cmd_buffer = u32*)
+    u8* optval = Memory::GetPointer(cmd_buffer[0x104 >> 2]);
+
+    int ret = ::getsockopt(socket_handle, level, optname, &optval, &optlen);
+    int err = 0;
+    if(ret == SOCKET_ERROR_VALUE) {
+        err = TranslateError(GET_ERRNO);
+    }
+
+    cmd_buffer[0] = IPC::MakeHeader(0x11, 4, 2);
+    cmd_buffer[1] = ret;
+    cmd_buffer[2] = err;
+    cmd_buffer[3] = optlen;
+}
+
+static void SetSockOpt(Service::Interface* self) {
+    u32* cmd_buffer = Kernel::GetCommandBuffer();
+    u32 socket_handle = cmd_buffer[1];
+    u32 level = cmd_buffer[2];
+    u32 optname = cmd_buffer[3];
+    socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]);
+    void *optval = Memory::GetPointer(cmd_buffer[8]);
+
+    int ret = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen));
+    int err = 0;
+    if(ret == SOCKET_ERROR_VALUE) {
+        err = TranslateError(GET_ERRNO);
+    }
+
+    cmd_buffer[0] = IPC::MakeHeader(0x12, 4, 4);
+    cmd_buffer[1] = ret;
+    cmd_buffer[2] = err;
+}
+
 const Interface::FunctionInfo FunctionTable[] = {
     {0x00010044, InitializeSockets,             "InitializeSockets"},
     {0x000200C2, Socket,                        "Socket"},
@@ -741,8 +784,8 @@ const Interface::FunctionInfo FunctionTable[] = {
     {0x000E00C2, nullptr,                       "GetHostByAddr"},
     {0x000F0106, nullptr,                       "GetAddrInfo"},
     {0x00100102, nullptr,                       "GetNameInfo"},
-    {0x00110102, nullptr,                       "GetSockOpt"},
-    {0x00120104, nullptr,                       "SetSockOpt"},
+    {0x00110102, GetSockOpt,                    "GetSockOpt"},
+    {0x00120104, SetSockOpt,                    "SetSockOpt"},
     {0x001300C2, Fcntl,                         "Fcntl"},
     {0x00140084, Poll,                          "Poll"},
     {0x00150042, nullptr,                       "SockAtMark"},

From 65883d9327030adb33938c9b0de276b4cfd74a46 Mon Sep 17 00:00:00 2001
From: Ryan Loebs <obsidianx@gmail.com>
Date: Tue, 29 Mar 2016 04:42:58 -0700
Subject: [PATCH 2/9] Addressing PR comments

---
 src/core/hle/service/soc_u.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index efda8bd4f..ea301f71f 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -568,7 +568,7 @@ static void RecvFrom(Service::Interface* self) {
     socklen_t src_addr_len = sizeof(src_addr);
     int ret = ::recvfrom(socket_handle, (char*)output_buff, len, flags, &src_addr, &src_addr_len);
 
-    if (buffer_parameters.output_src_address_buffer != 0 && src_addr_len > 0) {
+    if (ret >= 0 && buffer_parameters.output_src_address_buffer != 0 && src_addr_len > 0) {
         CTRSockAddr* ctr_src_addr = reinterpret_cast<CTRSockAddr*>(Memory::GetPointer(buffer_parameters.output_src_address_buffer));
         *ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
     }
@@ -736,7 +736,7 @@ static void GetSockOpt(Service::Interface* self) {
     // >> 2  = convert to u32 offset instead of byte offset (cmd_buffer = u32*)
     u8* optval = Memory::GetPointer(cmd_buffer[0x104 >> 2]);
 
-    int ret = ::getsockopt(socket_handle, level, optname, &optval, &optlen);
+    int ret = ::getsockopt(socket_handle, level, optname, optval, &optlen);
     int err = 0;
     if(ret == SOCKET_ERROR_VALUE) {
         err = TranslateError(GET_ERRNO);
@@ -754,11 +754,11 @@ static void SetSockOpt(Service::Interface* self) {
     u32 level = cmd_buffer[2];
     u32 optname = cmd_buffer[3];
     socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]);
-    void *optval = Memory::GetPointer(cmd_buffer[8]);
+    u8 *optval = Memory::GetPointer(cmd_buffer[8]);
 
     int ret = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen));
     int err = 0;
-    if(ret == SOCKET_ERROR_VALUE) {
+    if (ret == SOCKET_ERROR_VALUE) {
         err = TranslateError(GET_ERRNO);
     }
 

From aa5bb3b997d56b33ea9de8c7435f06d3849b61fd Mon Sep 17 00:00:00 2001
From: Ryan Loebs <obsidianx@gmail.com>
Date: Tue, 29 Mar 2016 04:45:17 -0700
Subject: [PATCH 3/9] Formatting...

---
 src/core/hle/service/soc_u.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index ea301f71f..43194345c 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -738,7 +738,7 @@ static void GetSockOpt(Service::Interface* self) {
 
     int ret = ::getsockopt(socket_handle, level, optname, optval, &optlen);
     int err = 0;
-    if(ret == SOCKET_ERROR_VALUE) {
+    if (ret == SOCKET_ERROR_VALUE) {
         err = TranslateError(GET_ERRNO);
     }
 

From b8422b24bd733c745c519ddd0e1d45a9191aca76 Mon Sep 17 00:00:00 2001
From: Ryan Loebs <obsidianx@gmail.com>
Date: Tue, 29 Mar 2016 14:24:03 -0700
Subject: [PATCH 4/9] Compiling on Windows now

---
 src/core/hle/service/soc_u.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index 43194345c..4606ad743 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -729,12 +729,12 @@ static void GetSockOpt(Service::Interface* self) {
     u32 socket_handle = cmd_buffer[1];
     u32 level = cmd_buffer[2];
     u32 optname = cmd_buffer[3];
-    u32 optlen = cmd_buffer[4];
+    int optlen = (int)cmd_buffer[4];
 
     // 0x100 = static buffer offset (bytes)
     // + 0x4 = 2nd pointer (u32) position
     // >> 2  = convert to u32 offset instead of byte offset (cmd_buffer = u32*)
-    u8* optval = Memory::GetPointer(cmd_buffer[0x104 >> 2]);
+    char* optval = reinterpret_cast<char*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
 
     int ret = ::getsockopt(socket_handle, level, optname, optval, &optlen);
     int err = 0;
@@ -754,7 +754,7 @@ static void SetSockOpt(Service::Interface* self) {
     u32 level = cmd_buffer[2];
     u32 optname = cmd_buffer[3];
     socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]);
-    u8 *optval = Memory::GetPointer(cmd_buffer[8]);
+    const char *optval = reinterpret_cast<const char*>(Memory::GetPointer(cmd_buffer[8]));
 
     int ret = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen));
     int err = 0;

From 64815a8b1609e874d5e3f403c93c8456bd4a9ccb Mon Sep 17 00:00:00 2001
From: Ryan Loebs <obsidianx@gmail.com>
Date: Tue, 29 Mar 2016 14:33:32 -0700
Subject: [PATCH 5/9] But of course, Windows uses 'int' while Linux uses
 'socklen_t'

---
 src/core/hle/service/soc_u.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index 4606ad743..b7c8eff57 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -729,7 +729,11 @@ static void GetSockOpt(Service::Interface* self) {
     u32 socket_handle = cmd_buffer[1];
     u32 level = cmd_buffer[2];
     u32 optname = cmd_buffer[3];
+#ifdef _WIN32
     int optlen = (int)cmd_buffer[4];
+#else
+    socklen_t optlen = (socklen_t)cmd_buffer[4];
+#endif
 
     // 0x100 = static buffer offset (bytes)
     // + 0x4 = 2nd pointer (u32) position

From 0a7d53692adb1eae98a2935a445efea327370d9f Mon Sep 17 00:00:00 2001
From: Ryan Loebs <obsidianx@gmail.com>
Date: Tue, 29 Mar 2016 14:48:25 -0700
Subject: [PATCH 6/9] Derp: win32: typedef int socklen_t;

---
 src/core/hle/service/soc_u.cpp | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index b7c8eff57..9d836349a 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -729,11 +729,7 @@ static void GetSockOpt(Service::Interface* self) {
     u32 socket_handle = cmd_buffer[1];
     u32 level = cmd_buffer[2];
     u32 optname = cmd_buffer[3];
-#ifdef _WIN32
-    int optlen = (int)cmd_buffer[4];
-#else
     socklen_t optlen = (socklen_t)cmd_buffer[4];
-#endif
 
     // 0x100 = static buffer offset (bytes)
     // + 0x4 = 2nd pointer (u32) position

From b1f89408dd0072a4fc7baac1384dc9744374107c Mon Sep 17 00:00:00 2001
From: Ryan Loebs <obsidianx@gmail.com>
Date: Wed, 30 Mar 2016 00:25:19 -0700
Subject: [PATCH 7/9] Added GetSockOptName

Filter out and translate invalid sockopt names.
---
 src/core/hle/service/soc_u.cpp | 73 +++++++++++++++++++++++++++-------
 1 file changed, 58 insertions(+), 15 deletions(-)

diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index 9d836349a..1c3ea03a2 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -295,6 +295,26 @@ union CTRSockAddr {
     }
 };
 
+/// Filters valid sockopt names and converts from platform-specific name if necessary
+static int GetSockOptName(u32 name) {
+    switch(name) {
+        case SO_RCVLOWAT:
+#ifdef _WIN32
+            // LOWAT not supported by WinSock
+            return -1;
+#endif
+        case SO_REUSEADDR:
+        case SO_SNDBUF:
+        case SO_RCVBUF:
+        case SO_TYPE:
+        case SO_ERROR:
+            return name;
+        default:
+            // all other options are either ineffectual or unsupported
+            return -1;
+    }
+}
+
 /// Holds info about the currently open sockets
 static std::unordered_map<u32, SocketHolder> open_sockets;
 
@@ -728,18 +748,29 @@ static void GetSockOpt(Service::Interface* self) {
     u32* cmd_buffer = Kernel::GetCommandBuffer();
     u32 socket_handle = cmd_buffer[1];
     u32 level = cmd_buffer[2];
-    u32 optname = cmd_buffer[3];
+    int optname = GetSockOptName(cmd_buffer[3]);
     socklen_t optlen = (socklen_t)cmd_buffer[4];
 
-    // 0x100 = static buffer offset (bytes)
-    // + 0x4 = 2nd pointer (u32) position
-    // >> 2  = convert to u32 offset instead of byte offset (cmd_buffer = u32*)
-    char* optval = reinterpret_cast<char*>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
-
-    int ret = ::getsockopt(socket_handle, level, optname, optval, &optlen);
+    int ret = -1;
     int err = 0;
-    if (ret == SOCKET_ERROR_VALUE) {
-        err = TranslateError(GET_ERRNO);
+
+    if(optname < 0) {
+#ifdef _WIN32
+        err = WSAEINVAL;
+#else
+        err = EINVAL;
+#endif
+    } else {
+        // 0x100 = static buffer offset (bytes)
+        // + 0x4 = 2nd pointer (u32) position
+        // >> 2  = convert to u32 offset instead of byte offset (cmd_buffer = u32*)
+        char *optval = reinterpret_cast<char *>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
+
+        ret = ::getsockopt(socket_handle, level, optname, optval, &optlen);
+        err = 0;
+        if (ret == SOCKET_ERROR_VALUE) {
+            err = TranslateError(GET_ERRNO);
+        }
     }
 
     cmd_buffer[0] = IPC::MakeHeader(0x11, 4, 2);
@@ -752,14 +783,26 @@ static void SetSockOpt(Service::Interface* self) {
     u32* cmd_buffer = Kernel::GetCommandBuffer();
     u32 socket_handle = cmd_buffer[1];
     u32 level = cmd_buffer[2];
-    u32 optname = cmd_buffer[3];
-    socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]);
-    const char *optval = reinterpret_cast<const char*>(Memory::GetPointer(cmd_buffer[8]));
+    int optname = GetSockOptName(cmd_buffer[3]);
 
-    int ret = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen));
+    int ret = -1;
     int err = 0;
-    if (ret == SOCKET_ERROR_VALUE) {
-        err = TranslateError(GET_ERRNO);
+
+    if(optname < 0) {
+#ifdef _WIN32
+        err = WSAEINVAL;
+#else
+        err = EINVAL;
+#endif
+    } else {
+        socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]);
+        const char *optval = reinterpret_cast<const char *>(Memory::GetPointer(cmd_buffer[8]));
+
+        ret = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen));
+        err = 0;
+        if (ret == SOCKET_ERROR_VALUE) {
+            err = TranslateError(GET_ERRNO);
+        }
     }
 
     cmd_buffer[0] = IPC::MakeHeader(0x12, 4, 4);

From 2faafff1b961ce735a1e8ffb46f175bd0f993af3 Mon Sep 17 00:00:00 2001
From: Ryan Loebs <obsidianx@gmail.com>
Date: Wed, 30 Mar 2016 13:51:34 -0700
Subject: [PATCH 8/9] Code style

---
 src/core/hle/service/soc_u.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index 1c3ea03a2..6ab246ba8 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -764,7 +764,7 @@ static void GetSockOpt(Service::Interface* self) {
         // 0x100 = static buffer offset (bytes)
         // + 0x4 = 2nd pointer (u32) position
         // >> 2  = convert to u32 offset instead of byte offset (cmd_buffer = u32*)
-        char *optval = reinterpret_cast<char *>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
+        char* optval = reinterpret_cast<char *>(Memory::GetPointer(cmd_buffer[0x104 >> 2]));
 
         ret = ::getsockopt(socket_handle, level, optname, optval, &optlen);
         err = 0;
@@ -796,7 +796,7 @@ static void SetSockOpt(Service::Interface* self) {
 #endif
     } else {
         socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]);
-        const char *optval = reinterpret_cast<const char *>(Memory::GetPointer(cmd_buffer[8]));
+        const char* optval = reinterpret_cast<const char *>(Memory::GetPointer(cmd_buffer[8]));
 
         ret = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen));
         err = 0;

From 58ee548ed88122086712f58bf05495655b5fd3f7 Mon Sep 17 00:00:00 2001
From: Ryan Loebs <obsidianx@gmail.com>
Date: Fri, 1 Apr 2016 22:19:21 -0700
Subject: [PATCH 9/9] Rework sockopt translation to match the error translation
 code already in place

---
 src/core/hle/service/soc_u.cpp | 52 ++++++++++++++++++++--------------
 1 file changed, 30 insertions(+), 22 deletions(-)

diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp
index 6ab246ba8..d3e5d4bca 100644
--- a/src/core/hle/service/soc_u.cpp
+++ b/src/core/hle/service/soc_u.cpp
@@ -151,6 +151,34 @@ static int TranslateError(int error) {
     return error;
 }
 
+/// Holds the translation from system network socket options to 3DS network socket options
+/// Note: -1 = No effect/unavailable
+static const std::unordered_map<int, int> sockopt_map = { {
+    { 0x0004,   SO_REUSEADDR },
+    { 0x0080,   -1 },
+    { 0x0100,   -1 },
+    { 0x1001,   SO_SNDBUF },
+    { 0x1002,   SO_RCVBUF },
+    { 0x1003,   -1 },
+#ifdef _WIN32
+    /// Unsupported in WinSock2
+    { 0x1004,   -1 },
+#else
+    { 0x1004,   SO_RCVLOWAT },
+#endif
+    { 0x1008,   SO_TYPE },
+    { 0x1009,   SO_ERROR },
+}};
+
+/// Converts a socket option from 3ds-specific to platform-specific
+static int TranslateSockOpt(int console_opt_name) {
+    auto found = sockopt_map.find(console_opt_name);
+    if (found != sockopt_map.end()) {
+        return found->second;
+    }
+    return console_opt_name;
+}
+
 /// Holds information about a particular socket
 struct SocketHolder {
     u32 socket_fd; ///< The socket descriptor
@@ -295,26 +323,6 @@ union CTRSockAddr {
     }
 };
 
-/// Filters valid sockopt names and converts from platform-specific name if necessary
-static int GetSockOptName(u32 name) {
-    switch(name) {
-        case SO_RCVLOWAT:
-#ifdef _WIN32
-            // LOWAT not supported by WinSock
-            return -1;
-#endif
-        case SO_REUSEADDR:
-        case SO_SNDBUF:
-        case SO_RCVBUF:
-        case SO_TYPE:
-        case SO_ERROR:
-            return name;
-        default:
-            // all other options are either ineffectual or unsupported
-            return -1;
-    }
-}
-
 /// Holds info about the currently open sockets
 static std::unordered_map<u32, SocketHolder> open_sockets;
 
@@ -748,7 +756,7 @@ static void GetSockOpt(Service::Interface* self) {
     u32* cmd_buffer = Kernel::GetCommandBuffer();
     u32 socket_handle = cmd_buffer[1];
     u32 level = cmd_buffer[2];
-    int optname = GetSockOptName(cmd_buffer[3]);
+    int optname = TranslateSockOpt(cmd_buffer[3]);
     socklen_t optlen = (socklen_t)cmd_buffer[4];
 
     int ret = -1;
@@ -783,7 +791,7 @@ static void SetSockOpt(Service::Interface* self) {
     u32* cmd_buffer = Kernel::GetCommandBuffer();
     u32 socket_handle = cmd_buffer[1];
     u32 level = cmd_buffer[2];
-    int optname = GetSockOptName(cmd_buffer[3]);
+    int optname = TranslateSockOpt(cmd_buffer[3]);
 
     int ret = -1;
     int err = 0;