mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-27 11:19:04 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b807013aac |
+1
@@ -30,6 +30,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||||
|
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
|
||||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||||
RENDERER_DEBUG("debug"),
|
RENDERER_DEBUG("debug"),
|
||||||
|
|||||||
+7
@@ -908,6 +908,13 @@ abstract class SettingsItem(
|
|||||||
descriptionId = R.string.enable_buffer_history_description
|
descriptionId = R.string.enable_buffer_history_description
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
put(
|
||||||
|
SwitchSetting(
|
||||||
|
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
|
||||||
|
titleId = R.string.enable_gpu_buffer_readback,
|
||||||
|
descriptionId = R.string.enable_gpu_buffer_readback_description
|
||||||
|
)
|
||||||
|
)
|
||||||
put(
|
put(
|
||||||
SwitchSetting(
|
SwitchSetting(
|
||||||
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
||||||
|
|||||||
+1
@@ -549,6 +549,7 @@ class SettingsFragmentPresenter(
|
|||||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||||
|
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
|
||||||
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
||||||
|
|
||||||
add(HeaderSetting(R.string.hacks))
|
add(HeaderSetting(R.string.hacks))
|
||||||
|
|||||||
@@ -580,6 +580,8 @@
|
|||||||
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
||||||
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
||||||
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
|
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
|
||||||
|
<string name="enable_gpu_buffer_readback">تفعيل قراءة مخزن وحدة معالجة الرسومات</string>
|
||||||
|
<string name="enable_gpu_buffer_readback_description">يحافظ هذا النظام على بيانات المخزن المؤقت المُعدّلة بواسطة وحدة معالجة الرسومات عن طريق قراءتها مرة أخرى قبل التحميل. تتطلب بعض الألعاب ذلك لعرض بعض التأثيرات بشكل صحيح. قد يُسبب ذلك مشاكل إذا لم يتمكن الجهاز من التعامل مع عبء العمل الإضافي.</string>
|
||||||
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
|
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
||||||
|
|
||||||
@@ -1097,6 +1099,7 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">فوري</string>
|
<string name="gpu_fence_behavior_immediate">فوري</string>
|
||||||
<string name="gpu_fence_behavior_balanced">متوازن</string>
|
<string name="gpu_fence_behavior_balanced">متوازن</string>
|
||||||
<string name="gpu_fence_behavior_accurate">دقيق</string>
|
<string name="gpu_fence_behavior_accurate">دقيق</string>
|
||||||
|
<string name="gpu_fence_behavior_strict">صارم</string>
|
||||||
|
|
||||||
<string name="vram_usage_conservative">محافظ</string>
|
<string name="vram_usage_conservative">محافظ</string>
|
||||||
<string name="vram_usage_aggressive">عدواني</string>
|
<string name="vram_usage_aggressive">عدواني</string>
|
||||||
|
|||||||
@@ -967,6 +967,7 @@ Wirklich fortfahren?</string>
|
|||||||
<string name="gpu_fence_behavior_immediate">Direkt</string>
|
<string name="gpu_fence_behavior_immediate">Direkt</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Ausgewogen</string>
|
<string name="gpu_fence_behavior_balanced">Ausgewogen</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Genau</string>
|
<string name="gpu_fence_behavior_accurate">Genau</string>
|
||||||
|
<string name="gpu_fence_behavior_strict">Strikt</string>
|
||||||
|
|
||||||
<string name="vram_usage_conservative">Konservativ</string>
|
<string name="vram_usage_conservative">Konservativ</string>
|
||||||
<string name="vram_usage_aggressive">Aggressiv</string>
|
<string name="vram_usage_aggressive">Aggressiv</string>
|
||||||
|
|||||||
@@ -524,6 +524,8 @@
|
|||||||
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
||||||
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
||||||
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
|
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
|
||||||
|
<string name="enable_gpu_buffer_readback">Activar la lectura del buffer de la GPU</string>
|
||||||
|
<string name="enable_gpu_buffer_readback_description">Conserva los datos del búfer modificados por la GPU leyéndolos antes de subirlos.\nAlgunos juegos requieren esto para renderizar correctamente ciertos efectos.\nPuede causar problemas si el hardware no puede soportar la carga de trabajo adicional.</string>
|
||||||
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
|
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string>
|
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string>
|
||||||
|
|
||||||
@@ -1036,6 +1038,7 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">Inmediato</string>
|
<string name="gpu_fence_behavior_immediate">Inmediato</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Equilibrado</string>
|
<string name="gpu_fence_behavior_balanced">Equilibrado</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Preciso</string>
|
<string name="gpu_fence_behavior_accurate">Preciso</string>
|
||||||
|
<string name="gpu_fence_behavior_strict">Estricto</string>
|
||||||
|
|
||||||
<string name="vram_usage_conservative">Conservador</string>
|
<string name="vram_usage_conservative">Conservador</string>
|
||||||
<string name="vram_usage_aggressive">Agresivo</string>
|
<string name="vram_usage_aggressive">Agresivo</string>
|
||||||
|
|||||||
@@ -569,6 +569,8 @@
|
|||||||
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
<string name="renderer_reactive_flushing_description">Повышение точности рендеринга в некоторых играх за счет снижения производительности.</string>
|
||||||
<string name="enable_buffer_history">Включить историю буфера</string>
|
<string name="enable_buffer_history">Включить историю буфера</string>
|
||||||
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
|
<string name="enable_buffer_history_description">Позволяет обращаться к предыдущим состояниям буфера. Эта опция может повысить качество рендеринга и стабильность производительности в некоторых играх.</string>
|
||||||
|
<string name="enable_gpu_buffer_readback">Включить обратное чтение буфера ГПУ</string>
|
||||||
|
<string name="enable_gpu_buffer_readback_description">Сохраняет измененные ГПУ данные буфера путем чтения их обратно перед выгрузками. Некоторые игры требуют этого, чтобы рендерить определенные эффекты правильно. Может вызывать проблемы если оборудование не может обработать дополнительную рабочую нагрузку.</string>
|
||||||
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
|
<string name="use_optimized_vertex_buffers">Оптимизированные вершинные буферы</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
|
<string name="use_optimized_vertex_buffers_description">Включает оптимизированную привязку вершинного буфера для повышения производительности. Требует Mesa Turnip 26.0+ / QCOM. Приводит к вылету на старых версиях драйверов Turnip (25.3 и ниже).</string>
|
||||||
|
|
||||||
@@ -1086,6 +1088,7 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">Мгновенный</string>
|
<string name="gpu_fence_behavior_immediate">Мгновенный</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Сбалансированный</string>
|
<string name="gpu_fence_behavior_balanced">Сбалансированный</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Точный</string>
|
<string name="gpu_fence_behavior_accurate">Точный</string>
|
||||||
|
<string name="gpu_fence_behavior_strict">Строгий</string>
|
||||||
|
|
||||||
<string name="vram_usage_conservative">Консервативный</string>
|
<string name="vram_usage_conservative">Консервативный</string>
|
||||||
<string name="vram_usage_aggressive">Агрессивный</string>
|
<string name="vram_usage_aggressive">Агрессивный</string>
|
||||||
|
|||||||
@@ -570,6 +570,8 @@
|
|||||||
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
||||||
<string name="enable_buffer_history">启用缓冲区历史</string>
|
<string name="enable_buffer_history">启用缓冲区历史</string>
|
||||||
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
||||||
|
<string name="enable_gpu_buffer_readback">启用 GPU 缓冲区回读</string>
|
||||||
|
<string name="enable_gpu_buffer_readback_description">在上传前回读经由 GPU 修改过的缓冲区数据,以将其保留。一些游戏会用到这项设定以正确渲染某些效果。如果硬件无法处理额外的工作负载,则可能会导致问题。</string>
|
||||||
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
|
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
|
||||||
|
|
||||||
@@ -1087,6 +1089,7 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">即时</string>
|
<string name="gpu_fence_behavior_immediate">即时</string>
|
||||||
<string name="gpu_fence_behavior_balanced">均衡</string>
|
<string name="gpu_fence_behavior_balanced">均衡</string>
|
||||||
<string name="gpu_fence_behavior_accurate">精确</string>
|
<string name="gpu_fence_behavior_accurate">精确</string>
|
||||||
|
<string name="gpu_fence_behavior_strict">严格</string>
|
||||||
|
|
||||||
<string name="vram_usage_conservative">保守式</string>
|
<string name="vram_usage_conservative">保守式</string>
|
||||||
<string name="vram_usage_aggressive">主动式</string>
|
<string name="vram_usage_aggressive">主动式</string>
|
||||||
|
|||||||
@@ -561,6 +561,8 @@
|
|||||||
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度</string>
|
<string name="renderer_reactive_flushing_description">犧牲效能,以改善部分遊戲的轉譯準確度</string>
|
||||||
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
<string name="enable_buffer_history">啟用緩衝區歷史</string>
|
||||||
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態。此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
<string name="enable_buffer_history_description">允許存取先前的緩衝區狀態。此選項可能會改善部分遊戲的渲染品質與效能穩定性</string>
|
||||||
|
<string name="enable_gpu_buffer_readback">啟用 GPU 緩衝區讀回</string>
|
||||||
|
<string name="enable_gpu_buffer_readback_description">透過在上傳之前先將 GPU 修改過的緩衝區資料讀回來保存資料,部分遊戲需要啟用此功能才能正常渲染遊戲特效。如果硬體無法負荷可能會導致錯誤</string>
|
||||||
<string name="use_optimized_vertex_buffers">最佳化頂點緩衝區</string>
|
<string name="use_optimized_vertex_buffers">最佳化頂點緩衝區</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">啟用最佳化的頂點緩衝區綁定。需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機 (25.3版和更低的版本)</string>
|
<string name="use_optimized_vertex_buffers_description">啟用最佳化的頂點緩衝區綁定。需要安裝 Mesa 26.0+ Turnip drivers/Qualcomm drivers。使用舊版 Turnip drivers 會導致當機 (25.3版和更低的版本)</string>
|
||||||
|
|
||||||
|
|||||||
@@ -558,6 +558,7 @@
|
|||||||
<item>@string/gpu_fence_behavior_immediate</item>
|
<item>@string/gpu_fence_behavior_immediate</item>
|
||||||
<item>@string/gpu_fence_behavior_balanced</item>
|
<item>@string/gpu_fence_behavior_balanced</item>
|
||||||
<item>@string/gpu_fence_behavior_accurate</item>
|
<item>@string/gpu_fence_behavior_accurate</item>
|
||||||
|
<item>@string/gpu_fence_behavior_strict</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<integer-array name="gpuFenceBehaviorValues">
|
<integer-array name="gpuFenceBehaviorValues">
|
||||||
<item>0</item>
|
<item>0</item>
|
||||||
|
|||||||
@@ -586,6 +586,8 @@
|
|||||||
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
||||||
<string name="enable_buffer_history">Enable buffer history</string>
|
<string name="enable_buffer_history">Enable buffer history</string>
|
||||||
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
||||||
|
<string name="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
|
||||||
|
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
|
||||||
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
||||||
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
||||||
|
|
||||||
@@ -1136,6 +1138,7 @@
|
|||||||
<string name="gpu_fence_behavior_immediate">Immediate</string>
|
<string name="gpu_fence_behavior_immediate">Immediate</string>
|
||||||
<string name="gpu_fence_behavior_balanced">Balanced</string>
|
<string name="gpu_fence_behavior_balanced">Balanced</string>
|
||||||
<string name="gpu_fence_behavior_accurate">Accurate</string>
|
<string name="gpu_fence_behavior_accurate">Accurate</string>
|
||||||
|
<string name="gpu_fence_behavior_strict">Strict</string>
|
||||||
|
|
||||||
<!-- ASTC Decoding Method Choices -->
|
<!-- ASTC Decoding Method Choices -->
|
||||||
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
<string name="accelerate_astc_cpu" translatable="false">CPU</string>
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ add_library(
|
|||||||
settings_input.h
|
settings_input.h
|
||||||
settings_setting.h
|
settings_setting.h
|
||||||
slot_vector.h
|
slot_vector.h
|
||||||
|
socket_types.h
|
||||||
sparse_large_vector.cpp
|
sparse_large_vector.cpp
|
||||||
sparse_large_vector.h
|
sparse_large_vector.h
|
||||||
stb.cpp
|
stb.cpp
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/internal_network/socket_types.h"
|
#include "common/socket_types.h"
|
||||||
#include "web_service/web_result.h"
|
#include "web_service/web_result.h"
|
||||||
|
|
||||||
namespace AnnounceMultiplayerRoom {
|
namespace AnnounceMultiplayerRoom {
|
||||||
|
|||||||
@@ -178,6 +178,10 @@ bool IsGPUFenceBehaviorAccurate() {
|
|||||||
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate;
|
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Accurate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsGPUFenceBehaviorStrict() {
|
||||||
|
return values.gpu_fence_behavior.GetValue() == GpuFenceBehavior::Strict;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsFastmemEnabled() {
|
bool IsFastmemEnabled() {
|
||||||
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
|
if (values.cpu_accuracy.GetValue() == Settings::CpuAccuracy::Debugging)
|
||||||
return bool(values.cpuopt_fastmem);
|
return bool(values.cpuopt_fastmem);
|
||||||
|
|||||||
@@ -525,7 +525,7 @@ struct Values {
|
|||||||
SwitchableSetting<GpuFenceBehavior, true> gpu_fence_behavior{linkage,
|
SwitchableSetting<GpuFenceBehavior, true> gpu_fence_behavior{linkage,
|
||||||
GpuFenceBehavior::Default,
|
GpuFenceBehavior::Default,
|
||||||
GpuFenceBehavior::Default,
|
GpuFenceBehavior::Default,
|
||||||
GpuFenceBehavior::Accurate,
|
GpuFenceBehavior::Strict,
|
||||||
"gpu_fence_behavior",
|
"gpu_fence_behavior",
|
||||||
Category::RendererAdvanced,
|
Category::RendererAdvanced,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
@@ -655,6 +655,13 @@ struct Values {
|
|||||||
|
|
||||||
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
SwitchableSetting<bool> rescale_hack{linkage, false, "rescale_hack",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
|
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
|
||||||
|
false,
|
||||||
|
"enable_gpu_buffer_readback",
|
||||||
|
Category::RendererAdvanced,
|
||||||
|
Specialization::Default,
|
||||||
|
true,
|
||||||
|
true};
|
||||||
|
|
||||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||||
Category::RendererHacks};
|
Category::RendererHacks};
|
||||||
@@ -972,6 +979,7 @@ bool IsDMALevelSafe();
|
|||||||
bool IsGPUFenceBehaviorDefault();
|
bool IsGPUFenceBehaviorDefault();
|
||||||
bool IsGPUFenceBehaviorBalanced();
|
bool IsGPUFenceBehaviorBalanced();
|
||||||
bool IsGPUFenceBehaviorAccurate();
|
bool IsGPUFenceBehaviorAccurate();
|
||||||
|
bool IsGPUFenceBehaviorStrict();
|
||||||
|
|
||||||
bool IsFastmemEnabled();
|
bool IsFastmemEnabled();
|
||||||
void SetNceEnabled(bool is_64bit);
|
void SetNceEnabled(bool is_64bit);
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ ENUM(VramUsageMode, Conservative, Aggressive);
|
|||||||
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
ENUM(RendererBackend, OpenGL_GLSL, Vulkan, Null, OpenGL_GLASM, OpenGL_SPIRV);
|
||||||
ENUM(GpuAccuracy, Low, High);
|
ENUM(GpuAccuracy, Low, High);
|
||||||
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
ENUM(DmaAccuracy, Default, Unsafe, Safe);
|
||||||
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate);
|
ENUM(GpuFenceBehavior, Default, Immediate, Balanced, Accurate, Strict);
|
||||||
ENUM(CpuBackend, Dynarmic, Nce);
|
ENUM(CpuBackend, Dynarmic, Nce);
|
||||||
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging);
|
ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid, Debugging);
|
||||||
ENUM(CpuClock, Normal, Boost, Overclock)
|
ENUM(CpuClock, Normal, Boost, Overclock)
|
||||||
|
|||||||
@@ -0,0 +1,178 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
/// Address families
|
||||||
|
enum class Domain : u8 {
|
||||||
|
Unspecified, ///< Represents 0, used in getaddrinfo hints
|
||||||
|
INET, ///< Address family for IPv4
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Socket types
|
||||||
|
enum class Type {
|
||||||
|
Unspecified, ///< Represents 0, used in getaddrinfo hints
|
||||||
|
STREAM,
|
||||||
|
DGRAM,
|
||||||
|
RAW,
|
||||||
|
SEQPACKET,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Protocol values for sockets
|
||||||
|
enum class Protocol : u8 {
|
||||||
|
Unspecified, ///< Represents 0, usable in various places
|
||||||
|
IP,
|
||||||
|
ICMP,
|
||||||
|
TCP,
|
||||||
|
UDP,
|
||||||
|
IPV6,
|
||||||
|
RAW,
|
||||||
|
IGMP,
|
||||||
|
GGP,
|
||||||
|
IPV4,
|
||||||
|
ST,
|
||||||
|
EGP,
|
||||||
|
PIGP,
|
||||||
|
RCCMON,
|
||||||
|
NVPII,
|
||||||
|
PUP,
|
||||||
|
ARGUS,
|
||||||
|
EMCON,
|
||||||
|
XNET,
|
||||||
|
CHAOS,
|
||||||
|
MUX,
|
||||||
|
MEAS,
|
||||||
|
HMP,
|
||||||
|
PRM,
|
||||||
|
IDP,
|
||||||
|
TRUNK1,
|
||||||
|
TRUNK2,
|
||||||
|
LEAF1,
|
||||||
|
LEAF2,
|
||||||
|
RDP,
|
||||||
|
IRTP,
|
||||||
|
TP,
|
||||||
|
BLT,
|
||||||
|
NSP,
|
||||||
|
INP,
|
||||||
|
DCCP,
|
||||||
|
//TODO: 3PC,
|
||||||
|
IDPR,
|
||||||
|
XTP,
|
||||||
|
DDP,
|
||||||
|
CMTP,
|
||||||
|
TPXX,
|
||||||
|
IL,
|
||||||
|
SDRP,
|
||||||
|
ROUTING,
|
||||||
|
FRAGMENT,
|
||||||
|
IDRP,
|
||||||
|
RSVP,
|
||||||
|
GRE,
|
||||||
|
MHRP,
|
||||||
|
BHA,
|
||||||
|
ESP,
|
||||||
|
AH,
|
||||||
|
INLSP,
|
||||||
|
SWIPE,
|
||||||
|
NHRP,
|
||||||
|
MOBILE,
|
||||||
|
TLSP,
|
||||||
|
SKIP,
|
||||||
|
ICMPV6,
|
||||||
|
NONE,
|
||||||
|
DSTOPTS,
|
||||||
|
AHIP,
|
||||||
|
CFTP,
|
||||||
|
HELLO,
|
||||||
|
SATEXPAK,
|
||||||
|
KRYPTOLAN,
|
||||||
|
RVD,
|
||||||
|
IPPC,
|
||||||
|
ADFS,
|
||||||
|
SATMON,
|
||||||
|
VISA,
|
||||||
|
IPCV,
|
||||||
|
CPNX,
|
||||||
|
CPHB,
|
||||||
|
WSN,
|
||||||
|
PVP,
|
||||||
|
BRSATMON,
|
||||||
|
ND,
|
||||||
|
WBMON,
|
||||||
|
WBEXPAK,
|
||||||
|
EON,
|
||||||
|
VMTP,
|
||||||
|
SVMTP,
|
||||||
|
VINES,
|
||||||
|
TTP,
|
||||||
|
IGP,
|
||||||
|
DGP,
|
||||||
|
TCF,
|
||||||
|
IGRP,
|
||||||
|
OSPFIGP,
|
||||||
|
SRPC,
|
||||||
|
LARP,
|
||||||
|
MTP,
|
||||||
|
AX25,
|
||||||
|
IPEIP,
|
||||||
|
MICP,
|
||||||
|
SCCSP,
|
||||||
|
ETHERIP,
|
||||||
|
ENCAP,
|
||||||
|
APES,
|
||||||
|
GMTP,
|
||||||
|
IPCOMP,
|
||||||
|
SCTP,
|
||||||
|
MH,
|
||||||
|
UDPLITE,
|
||||||
|
HIP,
|
||||||
|
SHIM6,
|
||||||
|
PIM,
|
||||||
|
CARP,
|
||||||
|
PGM,
|
||||||
|
MPLS,
|
||||||
|
PFSYNC
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Shutdown mode
|
||||||
|
enum class ShutdownHow {
|
||||||
|
RD,
|
||||||
|
WR,
|
||||||
|
RDWR,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Array of IPv4 address
|
||||||
|
using IPv4Address = std::array<u8, 4>;
|
||||||
|
|
||||||
|
/// Cross-platform sockaddr structure
|
||||||
|
struct SockAddrIn {
|
||||||
|
Domain family;
|
||||||
|
IPv4Address ip;
|
||||||
|
u16 portno;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr u32 FLAG_MSG_PEEK = 0x2;
|
||||||
|
constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
|
||||||
|
constexpr u32 FLAG_O_NONBLOCK = 0x800;
|
||||||
|
|
||||||
|
/// Cross-platform addrinfo structure
|
||||||
|
struct AddrInfo {
|
||||||
|
Domain family;
|
||||||
|
Type socket_type;
|
||||||
|
Protocol protocol;
|
||||||
|
SockAddrIn addr;
|
||||||
|
std::optional<std::string> canon_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Network
|
||||||
@@ -6,10 +6,16 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <algorithm>
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#else
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -20,58 +26,60 @@
|
|||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static std::vector<std::pair<u64, u64>> vector_regions {};
|
|
||||||
static std::mutex vector_regions_mutex {};
|
|
||||||
|
|
||||||
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
struct VectorRegion {
|
||||||
|
u64 start_page;
|
||||||
|
u64 end_page;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::mutex& GetVectorRegionsMutex() {
|
||||||
|
static std::mutex* m = new std::mutex();
|
||||||
|
return *m;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<VectorRegion>& GetVectorRegions() {
|
||||||
|
static std::vector<VectorRegion>* v = new std::vector<VectorRegion>();
|
||||||
|
return *v;
|
||||||
|
}
|
||||||
|
|
||||||
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||||
DWORD code = info->ExceptionRecord->ExceptionCode;
|
if (info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) {
|
||||||
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionInformation[1]);
|
|
||||||
|
|
||||||
if (code != EXCEPTION_ACCESS_VIOLATION || info->ExceptionRecord->ExceptionInformation[0] == 1) {
|
|
||||||
// Not our problem
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 addr = 0, addr2 = 0;
|
const u64 fault_addr = info->ExceptionRecord->ExceptionInformation[1];
|
||||||
|
const u64 access_type = info->ExceptionRecord->ExceptionInformation[0];
|
||||||
|
const bool is_write = (access_type == 1);
|
||||||
|
const u64 fault_page = fault_addr >> HostPageBits;
|
||||||
|
|
||||||
|
u64 addr = 0;
|
||||||
|
u64 addr2 = 0;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard lock(vector_regions_mutex);
|
std::lock_guard lock(GetVectorRegionsMutex());
|
||||||
for (auto region: vector_regions) {
|
for (const auto& region : GetVectorRegions()) {
|
||||||
auto addr_shifted = exception_addr >> HostPageBits;
|
if (fault_page >= region.start_page && fault_page < region.end_page) {
|
||||||
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
addr = fault_page;
|
||||||
addr = addr_shifted;
|
|
||||||
}
|
}
|
||||||
|
const u64 page2 = (fault_addr + 0x3F) >> HostPageBits;
|
||||||
// Page-boundary accesses
|
if (page2 != fault_page && page2 >= region.start_page && page2 < region.end_page) {
|
||||||
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
|
addr2 = page2;
|
||||||
addr2 = addr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addr != 0 || addr2 != 0) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (addr != 0 || addr2 != 0) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addr == 0 && addr2 == 0) {
|
if (addr == 0 && addr2 == 0) {
|
||||||
// Not our problem
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", exception_addr);
|
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", fault_addr);
|
||||||
|
|
||||||
// Commit this region
|
if (addr != 0 && !CommitVectorPage(addr << HostPageBits, is_write)) {
|
||||||
if (addr != 0) {
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
if (!CommitVectorPage(addr << HostPageBits, false)) {
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Commit next region if needed
|
if (addr2 != 0 && !CommitVectorPage(addr2 << HostPageBits, is_write)) {
|
||||||
if (addr2 != 0) {
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
if (!CommitVectorPage(addr2 << HostPageBits, false)) {
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return EXCEPTION_CONTINUE_EXECUTION;
|
return EXCEPTION_CONTINUE_EXECUTION;
|
||||||
@@ -79,31 +87,31 @@ static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
|||||||
|
|
||||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
||||||
MEMORY_BASIC_INFORMATION info {};
|
MEMORY_BASIC_INFORMATION info {};
|
||||||
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
const auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||||
|
const DWORD perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||||
|
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
||||||
} else if (info.State == MEM_COMMIT) {
|
} else if (info.State == MEM_COMMIT) {
|
||||||
DWORD old_protect {};
|
DWORD old_protect {};
|
||||||
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
|
||||||
if (!VirtualProtect(reinterpret_cast<void*>(addr), HostPageSize, perm, &old_protect)) {
|
if (!VirtualProtect(reinterpret_cast<void*>(addr), HostPageSize, perm, &old_protect)) {
|
||||||
LOG_ERROR(HW_Memory, "Failed to change permissions of large buffer region at {:#x}, error {}", addr, GetLastError());
|
LOG_ERROR(HW_Memory, "VirtualProtect failed at {:#x}, error {}", addr, GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (info.State != MEM_RESERVE) {
|
} else if (info.State != MEM_RESERVE) {
|
||||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped (state {:#x})", addr, info.State);
|
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} (state {:#x})", addr, info.State);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
if (VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm) == nullptr) {
|
||||||
void* res2 = VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm);
|
|
||||||
if (res2 == nullptr) {
|
|
||||||
LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError());
|
LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MAP_NOCORE
|
#ifndef MAP_NOCORE
|
||||||
@@ -115,59 +123,102 @@ bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
|||||||
|
|
||||||
void DecommitVectorPage(uintptr_t base) noexcept {
|
void DecommitVectorPage(uintptr_t base) noexcept {
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT);
|
if (!VirtualFree(reinterpret_cast<LPVOID>(base), HostPageSize, MEM_DECOMMIT)) {
|
||||||
|
LOG_WARNING(HW_Memory, "VirtualFree(MEM_DECOMMIT) failed at {:#x}, error {}", base, GetLastError());
|
||||||
|
}
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
// Linux's MADV_DONTNEED zeros out pages for us
|
if (madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED) != 0) {
|
||||||
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_DONTNEED);
|
LOG_WARNING(HW_Memory, "madvise(MADV_DONTNEED) failed at {:#x}: {}", base, std::strerror(errno));
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE);
|
if (madvise(reinterpret_cast<void*>(base), HostPageSize, MADV_FREE) != 0) {
|
||||||
|
LOG_WARNING(HW_Memory, "madvise(MADV_FREE) failed at {:#x}: {}", base, std::strerror(errno));
|
||||||
|
}
|
||||||
std::memset(reinterpret_cast<void*>(base), 0, HostPageSize);
|
std::memset(reinterpret_cast<void*>(base), 0, HostPageSize);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||||
if (auto page = HostPageSize; size % page != 0) {
|
if (size == 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto page = HostPageSize;
|
||||||
|
if (size % page != 0) {
|
||||||
LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page);
|
LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page);
|
||||||
|
if (size > SIZE_MAX - (page - 1)) {
|
||||||
|
LOG_CRITICAL(HW_Memory, "Size {:#x} would overflow page alignment", size);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
size = AlignUp(size, page);
|
size = AlignUp(size, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// We will never use this memory entirely so instead of committing it up front let's just reserve it and commit each page individually
|
|
||||||
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||||
|
|
||||||
if (base != nullptr) {
|
if (base != nullptr) {
|
||||||
std::lock_guard lock(vector_regions_mutex);
|
{
|
||||||
vector_regions.emplace_back(reinterpret_cast<u64>(base) >> HostPageBits, (reinterpret_cast<u64>(base) + size) >> HostPageBits);
|
std::lock_guard lock(GetVectorRegionsMutex());
|
||||||
|
GetVectorRegions().push_back({
|
||||||
|
reinterpret_cast<u64>(base) >> HostPageBits,
|
||||||
|
(reinterpret_cast<u64>(base) + size) >> HostPageBits,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static std::once_flag flag;
|
static std::once_flag flag;
|
||||||
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
||||||
} else {
|
} else {
|
||||||
// Try committing everything instead??
|
|
||||||
LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError());
|
LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError());
|
||||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||||
}
|
}
|
||||||
ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError());
|
ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError());
|
||||||
#else
|
#else
|
||||||
void* base = mmap(nullptr, size, PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0);
|
int flags = MAP_ANON | MAP_PRIVATE;
|
||||||
if (base == MAP_FAILED)
|
#ifdef MAP_NORESERVE
|
||||||
base = nullptr;
|
flags |= MAP_NORESERVE;
|
||||||
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, strerror(errno));
|
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(MAP_NOCORE)
|
||||||
|
flags |= MAP_NOCORE;
|
||||||
|
#endif
|
||||||
|
void* base = mmap(nullptr, size, PROT_READ, flags, -1, 0);
|
||||||
|
if (base == MAP_FAILED) {
|
||||||
|
base = nullptr;
|
||||||
|
}
|
||||||
|
#ifdef MADV_HUGEPAGE
|
||||||
|
if (base != nullptr) {
|
||||||
|
madvise(base, size, MADV_HUGEPAGE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, std::strerror(errno));
|
||||||
|
#endif
|
||||||
|
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||||
if (auto page = HostPageSize; size % page != 0) {
|
if (base == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto page = HostPageSize; size % page != 0) {
|
||||||
size = AlignUp(size, page);
|
size = AlignUp(size, page);
|
||||||
}
|
}
|
||||||
if (!base)
|
|
||||||
return;
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
std::lock_guard lock(vector_regions_mutex);
|
{
|
||||||
std::erase_if(vector_regions, [base](const auto& r) {return r.first == reinterpret_cast<u64>(base); });
|
std::lock_guard lock(GetVectorRegionsMutex());
|
||||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
auto& regions = GetVectorRegions();
|
||||||
|
const u64 base_page = reinterpret_cast<u64>(base) >> HostPageBits;
|
||||||
|
regions.erase(std::remove_if(regions.begin(), regions.end(),
|
||||||
|
[base_page](const VectorRegion& r) { return r.start_page == base_page; }), regions.end());
|
||||||
|
}
|
||||||
|
if (!VirtualFree(base, 0, MEM_RELEASE)) {
|
||||||
|
LOG_ERROR(HW_Memory, "VirtualFree failed, error {}", GetLastError());
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
ASSERT(munmap(base, size) == 0);
|
if (munmap(base, size) != 0) {
|
||||||
|
LOG_ERROR(HW_Memory, "munmap failed: {}", std::strerror(errno));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+138
-105
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
/* virtual_buffer.h */
|
/* virtual_buffer.h */
|
||||||
@@ -7,10 +7,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <bit>
|
#include <bit>
|
||||||
#include <utility>
|
#include <cerrno>
|
||||||
#include <vector>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -28,7 +32,7 @@ constexpr u64 HostPageBits = 12;
|
|||||||
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
||||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
||||||
#else
|
#else
|
||||||
inline const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
inline const u64 HostPageSize = static_cast<u64>(sysconf(_SC_PAGESIZE));
|
||||||
inline const u64 HostPageBits = std::countr_zero(HostPageSize);
|
inline const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||||
inline const u64 HostPageMask = ~(HostPageSize - 1);
|
inline const u64 HostPageMask = ~(HostPageSize - 1);
|
||||||
#endif
|
#endif
|
||||||
@@ -43,20 +47,18 @@ template <typename T>
|
|||||||
// requires std::is_trivially_copyable_v<T>
|
// requires std::is_trivially_copyable_v<T>
|
||||||
class SparseLargeVector final {
|
class SparseLargeVector final {
|
||||||
public:
|
public:
|
||||||
constexpr SparseLargeVector() = default;
|
SparseLargeVector() = default;
|
||||||
|
|
||||||
explicit SparseLargeVector(std::size_t count) noexcept
|
explicit SparseLargeVector(std::size_t count) noexcept {
|
||||||
: alloc_size{count * sizeof(T)}
|
if (count > SIZE_MAX / sizeof(T)) {
|
||||||
{
|
LOG_CRITICAL(Common_Memory, "SparseLargeVector size overflow: {} elements", count);
|
||||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
return;
|
||||||
|
}
|
||||||
// each item in vector holds information for 64 pages
|
Allocate(count * sizeof(T));
|
||||||
auto denom = HostPageSize * 64;
|
|
||||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~SparseLargeVector() noexcept {
|
~SparseLargeVector() noexcept {
|
||||||
FreeMemoryPages(base_ptr, alloc_size);
|
Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
SparseLargeVector(const SparseLargeVector&) = delete;
|
SparseLargeVector(const SparseLargeVector&) = delete;
|
||||||
@@ -65,150 +67,181 @@ public:
|
|||||||
SparseLargeVector& operator=(SparseLargeVector&& other) = delete;
|
SparseLargeVector& operator=(SparseLargeVector&& other) = delete;
|
||||||
|
|
||||||
void ResizeAndClear(std::size_t count) noexcept {
|
void ResizeAndClear(std::size_t count) noexcept {
|
||||||
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
if (count > SIZE_MAX / sizeof(T)) {
|
||||||
FreeMemoryPages(base_ptr, alloc_size);
|
LOG_CRITICAL(Common_Memory, "SparseLargeVector resize overflow: {} elements", count);
|
||||||
alloc_size = new_size;
|
return;
|
||||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
|
||||||
|
|
||||||
auto denom = HostPageSize * 64;
|
|
||||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
|
||||||
}
|
}
|
||||||
|
const std::size_t new_size = count * sizeof(T);
|
||||||
|
if (new_size == alloc_size) {
|
||||||
|
ZeroRegion(0, alloc_size / sizeof(T));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Release();
|
||||||
|
Allocate(new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the value of the requested index and allocates memory if needed.
|
|
||||||
T& GetAndFault(std::size_t index) noexcept {
|
T& GetAndFault(std::size_t index) noexcept {
|
||||||
if (index > alloc_size / sizeof(T)) {
|
if (base_ptr == nullptr || index >= size()) [[unlikely]] {
|
||||||
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
LOG_CRITICAL(Common_Memory, "SparseLargeVector RW access out of bounds @ {} (size {})", index, size());
|
||||||
|
std::abort();
|
||||||
}
|
}
|
||||||
|
const u64 byte_offset = static_cast<u64>(index) * sizeof(T);
|
||||||
if (!IsCommittedPage(index) && !CommitPage(index)) {
|
if (!CommitPage(byte_offset)) [[unlikely]] {
|
||||||
UNREACHABLE_MSG("Cannot access SparseLargeVector index {} with RW permission", index);
|
LOG_CRITICAL(Common_Memory, "SparseLargeVector commit failed @ {} (offset {:#x})", index, byte_offset);
|
||||||
|
std::abort();
|
||||||
}
|
}
|
||||||
return base_ptr[index];
|
return base_ptr[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the value of the requested index if initialized, or will otherwise return a zero-initialized object.
|
const T& GetOrDefault(std::size_t index) const noexcept {
|
||||||
const T& GetOrDefault(std::size_t index) const {
|
if (base_ptr == nullptr || index >= size()) [[unlikely]] {
|
||||||
|
LOG_CRITICAL(Common_Memory, "SparseLargeVector RO access out of bounds @ {}", index);
|
||||||
|
return DefaultValue();
|
||||||
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (!IsCommittedPage(index)) {
|
if (!IsPageCommitted(static_cast<u64>(index) * sizeof(T))) {
|
||||||
return *reinterpret_cast<const T*>(&default_val);
|
return DefaultValue();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// On non-Windows, OS page table should optimize this by pointing to a zero page if unallocated.
|
|
||||||
return base_ptr[index];
|
return base_ptr[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Set(std::size_t index, const T& value) noexcept {
|
void Set(std::size_t index, const T& value) noexcept {
|
||||||
if (index > alloc_size / sizeof(T)) {
|
if (base_ptr == nullptr || index >= size()) [[unlikely]] {
|
||||||
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
LOG_CRITICAL(Common_Memory, "SparseLargeVector write out of bounds @ {}", index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (IsCommittedPage(index) || CommitPage(index))
|
const u64 byte_offset = static_cast<u64>(index) * sizeof(T);
|
||||||
base_ptr[index] = value;
|
if (!CommitPage(byte_offset)) [[unlikely]] {
|
||||||
|
LOG_CRITICAL(Common_Memory, "SparseLargeVector commit failed for write @ {}", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
base_ptr[index] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
|
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
|
||||||
u64 base = reinterpret_cast<u64>(&base_ptr[start]);
|
if (base_ptr == nullptr || start >= end_) return;
|
||||||
const u64 end = reinterpret_cast<u64>(&base_ptr[end_]);
|
|
||||||
|
|
||||||
const u64 end_page = AlignUp(base, HostPageSize);
|
const u64 start_off = static_cast<u64>(start) * sizeof(T);
|
||||||
const u64 first_size = (std::min)(end_page, end) - base;
|
const u64 end_off = static_cast<u64>(end_) * sizeof(T);
|
||||||
|
const u64 first_page_end = (start_off + HostPageSize - 1) & HostPageMask;
|
||||||
|
|
||||||
if (IsCommittedPage(start)) {
|
if (start_off < first_page_end) {
|
||||||
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
const u64 chunk_end = (std::min)(first_page_end, end_off);
|
||||||
|
const u64 chunk_size = chunk_end - start_off;
|
||||||
|
if (chunk_size != 0 && IsPageCommitted(start_off)) {
|
||||||
|
std::memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_ptr) + start_off), 0, chunk_size);
|
||||||
|
}
|
||||||
|
if (end_off <= first_page_end) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end <= end_page)
|
for (u64 off = first_page_end; off < end_off; off += HostPageSize) {
|
||||||
return;
|
if (!IsPageCommitted(off)) continue;
|
||||||
|
const u64 remaining = end_off - off;
|
||||||
base = end_page;
|
if (remaining >= HostPageSize) {
|
||||||
|
DecommitPage(off);
|
||||||
for (u64 page = base; page < end; page += HostPageSize) {
|
|
||||||
auto index = (page - reinterpret_cast<u64>(base_ptr)) / sizeof(T);
|
|
||||||
if (!IsCommittedPage(index)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end - page >= HostPageSize) {
|
|
||||||
DecommitPage(index);
|
|
||||||
} else {
|
} else {
|
||||||
std::memset(reinterpret_cast<void*>(page), 0, end - page);
|
std::memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(base_ptr) + off), 0, remaining);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void CommitRegion(size_t index, size_t end_) {
|
void CommitRegion(std::size_t index, std::size_t end_) noexcept {
|
||||||
const u64 base = static_cast<u64>(index) * sizeof(T);
|
if (base_ptr == nullptr || index >= end_) return;
|
||||||
const u64 end = static_cast<u64>(end_) * sizeof(T);
|
const u64 start_off = static_cast<u64>(index) * sizeof(T);
|
||||||
|
const u64 end_off = static_cast<u64>(end_) * sizeof(T);
|
||||||
for (u64 page = AlignDown(base, HostPageSize); page < end; page += HostPageSize) {
|
const u64 start_page = start_off & HostPageMask;
|
||||||
if (!IsCommittedPage(page / sizeof(T))) {
|
for (u64 off = start_page; off < end_off; off += HostPageSize) {
|
||||||
CommitPage(page / sizeof(T));
|
if (!IsPageCommitted(off)) {
|
||||||
|
(void)CommitPage(off);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr T& GetUnchecked(size_t index) {
|
T& GetUnchecked(std::size_t index) noexcept { return base_ptr[index]; }
|
||||||
return base_ptr[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
[[nodiscard]] const T& operator[](std::size_t index) const noexcept { return GetOrDefault(index); }
|
||||||
return GetOrDefault(index);
|
[[nodiscard]] const T* data() const noexcept { return base_ptr; }
|
||||||
}
|
[[nodiscard]] std::size_t size() const noexcept { return alloc_size / sizeof(T); }
|
||||||
|
|
||||||
[[nodiscard]] constexpr const T* data() const noexcept {
|
|
||||||
return base_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
|
||||||
return alloc_size / sizeof(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] constexpr bool IsCommittedPage(std::size_t index) const noexcept {
|
void Allocate(std::size_t new_size) noexcept {
|
||||||
if (index > alloc_size / sizeof(T)) {
|
alloc_size = new_size;
|
||||||
LOG_CRITICAL(Common_Memory, "Out of bounds access on large vector @ {}", index);
|
if (alloc_size == 0) {
|
||||||
return false;
|
base_ptr = nullptr;
|
||||||
|
committed_pages.reset();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||||
auto page = (index * sizeof(T)) >> HostPageBits;
|
const std::size_t num_pages = NumPages();
|
||||||
auto val = committed_pages[page >> 6].load(std::memory_order_acquire);
|
const std::size_t num_words = (num_pages + 63) / 64;
|
||||||
return (val >> (page & 63)) & 1;
|
committed_pages = std::make_unique<std::atomic<u64>[]>(num_words);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr bool CommitPage(std::size_t index) noexcept {
|
void Release() noexcept {
|
||||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
if (base_ptr != nullptr) {
|
||||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
FreeMemoryPages(base_ptr, alloc_size);
|
||||||
#if defined(_WIN32)
|
base_ptr = nullptr;
|
||||||
if (!CommitVectorPage(page, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
committed_pages.reset();
|
||||||
|
alloc_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u64 NumPages() const noexcept {
|
||||||
|
return (alloc_size + HostPageSize - 1) >> HostPageBits;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsPageCommitted(u64 byte_offset) const noexcept {
|
||||||
|
const u64 page_index = byte_offset >> HostPageBits;
|
||||||
|
if (committed_pages == nullptr || page_index >= NumPages()) return false;
|
||||||
|
const auto val = committed_pages[page_index >> 6].load(std::memory_order_acquire);
|
||||||
|
return (val >> (page_index & 63)) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPageBit(u64 page_index, bool value) noexcept {
|
||||||
|
if (committed_pages == nullptr) return;
|
||||||
|
const u64 bit = 1ULL << (page_index & 63);
|
||||||
|
auto& atom = committed_pages[page_index >> 6];
|
||||||
|
if (value) {
|
||||||
|
atom.fetch_or(bit, std::memory_order_release);
|
||||||
|
} else {
|
||||||
|
atom.fetch_and(~bit, std::memory_order_release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CommitPage(u64 byte_offset) noexcept {
|
||||||
|
const u64 page_index = byte_offset >> HostPageBits;
|
||||||
|
const uintptr_t page_addr = (reinterpret_cast<uintptr_t>(base_ptr) + byte_offset) & HostPageMask;
|
||||||
|
|
||||||
|
if (IsPageCommitted(byte_offset)) return true;
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
if (!CommitVectorPage(page_addr, true)) return false;
|
||||||
#else
|
#else
|
||||||
if (mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE) != 0) {
|
if (mprotect(reinterpret_cast<void*>(page_addr), HostPageSize, PROT_READ | PROT_WRITE) != 0) {
|
||||||
LOG_ERROR(Common_Memory, "Failed to commit large buffer region at index {}, error {}", index, strerror(errno));
|
LOG_ERROR(Common_Memory, "mprotect failed at {:#x}: {}", page_addr, std::strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
SetPageBit(page_index, true);
|
||||||
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void DecommitPage(std::size_t index) noexcept {
|
void DecommitPage(u64 byte_offset) noexcept {
|
||||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
const u64 page_index = byte_offset >> HostPageBits;
|
||||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
const uintptr_t page_addr = (reinterpret_cast<uintptr_t>(base_ptr) + byte_offset) & HostPageMask;
|
||||||
|
DecommitVectorPage(page_addr);
|
||||||
|
SetPageBit(page_index, false);
|
||||||
|
}
|
||||||
|
|
||||||
committed_pages[page_index >> 6].fetch_and(~(1ULL << (page_index & 63)), std::memory_order_release);
|
[[nodiscard]] const T& DefaultValue() const noexcept {
|
||||||
DecommitVectorPage(page);
|
return *reinterpret_cast<const T*>(&default_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t alloc_size{};
|
std::size_t alloc_size{};
|
||||||
T* base_ptr{};
|
T* base_ptr{};
|
||||||
|
std::unique_ptr<std::atomic<u64>[]> committed_pages{};
|
||||||
std::vector<std::atomic<u64>> committed_pages{};
|
alignas(T) const std::array<u8, sizeof(T)> default_val{};
|
||||||
#ifdef _WIN32
|
|
||||||
const std::array<u8, sizeof(T)> default_val{};
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
@@ -1127,7 +1127,6 @@ add_library(core STATIC
|
|||||||
internal_network/network_interface.h
|
internal_network/network_interface.h
|
||||||
internal_network/socket_proxy.cpp
|
internal_network/socket_proxy.cpp
|
||||||
internal_network/socket_proxy.h
|
internal_network/socket_proxy.h
|
||||||
internal_network/socket_types.h
|
|
||||||
internal_network/sockets.h
|
internal_network/sockets.h
|
||||||
internal_network/wifi_scanner.h
|
internal_network/wifi_scanner.h
|
||||||
launch_timestamp_cache.cpp
|
launch_timestamp_cache.cpp
|
||||||
@@ -1166,12 +1165,6 @@ add_library(core STATIC
|
|||||||
tools/renderdoc.cpp
|
tools/renderdoc.cpp
|
||||||
tools/renderdoc.h)
|
tools/renderdoc.h)
|
||||||
|
|
||||||
if (UNIX AND NOT APPLE)
|
|
||||||
target_sources(core PRIVATE
|
|
||||||
internal_network/socket_icmp.cpp
|
|
||||||
internal_network/socket_icmp.h)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (ENABLE_WIFI_SCAN)
|
if (ENABLE_WIFI_SCAN)
|
||||||
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
|
target_sources(core PRIVATE internal_network/wifi_scanner.cpp)
|
||||||
if (LINUX)
|
if (LINUX)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#include "common/container/unordered_map.h"
|
#include "common/container/unordered_map.h"
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "core/internal_network/socket_types.h"
|
#include "common/socket_types.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
#include "core/hle/service/ldn/ldn_results.h"
|
#include "core/hle/service/ldn/ldn_results.h"
|
||||||
#include "core/hle/service/ldn/ldn_types.h"
|
#include "core/hle/service/ldn/ldn_types.h"
|
||||||
|
|||||||
@@ -129,13 +129,6 @@ ServerManager::~ServerManager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
|
|
||||||
for (size_t i = 0; i < num_threads; i++) {
|
|
||||||
auto thread_name = fmt::format("{}:{}", name, i + 1);
|
|
||||||
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(std::move(thread_name), [&] { this->LoopProcessImpl(); }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
|
void ServerManager::RunServer(std::unique_ptr<ServerManager>&& server_manager) {
|
||||||
server_manager->m_system.RunServer(std::move(server_manager));
|
server_manager->m_system.RunServer(std::move(server_manager));
|
||||||
}
|
}
|
||||||
@@ -252,6 +245,14 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
|
|||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_threads) {
|
||||||
|
for (size_t i = 0; i < num_threads; i++) {
|
||||||
|
auto thread_name = fmt::format("{}:{}", name, i + 1);
|
||||||
|
m_threads.emplace_back(m_system.Kernel().RunOnHostCoreThread(
|
||||||
|
std::move(thread_name), [&] { this->LoopProcessImpl(); }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Result ServerManager::LoopProcess() {
|
Result ServerManager::LoopProcess() {
|
||||||
SCOPE_EXIT {
|
SCOPE_EXIT {
|
||||||
m_stopped.Set();
|
m_stopped.Set();
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -51,8 +48,8 @@ public:
|
|||||||
Result ManageDeferral(Kernel::KEvent** out_event);
|
Result ManageDeferral(Kernel::KEvent** out_event);
|
||||||
|
|
||||||
Result LoopProcess();
|
Result LoopProcess();
|
||||||
|
|
||||||
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
void StartAdditionalHostThreads(const char* name, size_t num_threads);
|
||||||
|
|
||||||
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
static void RunServer(std::unique_ptr<ServerManager>&& server);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <fmt/ranges.h>
|
#include <fmt/ranges.h>
|
||||||
|
|
||||||
#include "common/logging.h"
|
#include "common/logging.h"
|
||||||
#include "core/internal_network/socket_types.h"
|
#include "common/socket_types.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/kernel/k_thread.h"
|
#include "core/hle/kernel/k_thread.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
@@ -20,9 +20,6 @@
|
|||||||
#include "core/hle/service/sockets/sockets_translate.h"
|
#include "core/hle/service/sockets/sockets_translate.h"
|
||||||
#include "core/internal_network/network.h"
|
#include "core/internal_network/network.h"
|
||||||
#include "core/internal_network/socket_proxy.h"
|
#include "core/internal_network/socket_proxy.h"
|
||||||
#if defined(__unix__) && !defined(__APPLE__)
|
|
||||||
#include "core/internal_network/socket_icmp.h"
|
|
||||||
#endif
|
|
||||||
#include "core/internal_network/sockets.h"
|
#include "core/internal_network/sockets.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
#include <common/settings.h>
|
#include <common/settings.h>
|
||||||
@@ -31,18 +28,15 @@ namespace Service::Sockets {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
[[nodiscard]] bool IsConnectionBased(Network::Type type) noexcept {
|
bool IsConnectionBased(Type type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Network::Type::STREAM:
|
case Type::STREAM:
|
||||||
case Network::Type::SEQPACKET:
|
|
||||||
return true;
|
return true;
|
||||||
case Network::Type::RAW:
|
case Type::DGRAM:
|
||||||
case Network::Type::DGRAM:
|
|
||||||
case Network::Type::RDM:
|
|
||||||
case Network::Type::Unspecified:
|
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +92,7 @@ void BSD_USA::ConnectWork::Execute(BSD_USA* bsd) {
|
|||||||
void BSD_USA::ConnectWork::Response(HLERequestContext& ctx) {
|
void BSD_USA::ConnectWork::Response(HLERequestContext& ctx) {
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
|
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,9 +170,10 @@ void BSD_USA::Socket(HLERequestContext& ctx) {
|
|||||||
const u32 domain = rp.Pop<u32>();
|
const u32 domain = rp.Pop<u32>();
|
||||||
const u32 type = rp.Pop<u32>();
|
const u32 type = rp.Pop<u32>();
|
||||||
const u32 protocol = rp.Pop<u32>();
|
const u32 protocol = rp.Pop<u32>();
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
||||||
|
|
||||||
const auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
|
const auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
@@ -194,8 +189,8 @@ void BSD_USA::SocketExempt(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
|
||||||
|
|
||||||
auto [fd, bsd_errno] = SocketImpl(Network::Domain(domain), Network::Type(type), Network::Protocol(protocol));
|
auto [fd, bsd_errno] = SocketImpl(Domain(domain), Type(type), Protocol(protocol));
|
||||||
if (bsd_errno == Network::Errno::E_SUCCESS) {
|
if (bsd_errno == Errno::SUCCESS) {
|
||||||
bsd_errno = ShutdownImpl(fd, 0);
|
bsd_errno = ShutdownImpl(fd, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,13 +264,13 @@ void BSD_USA::GetPeerName(HLERequestContext& ctx) {
|
|||||||
LOG_DEBUG(Service, "called. fd={}", fd);
|
LOG_DEBUG(Service, "called. fd={}", fd);
|
||||||
|
|
||||||
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
||||||
const Network::Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
|
const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
|
||||||
|
|
||||||
ctx.WriteBuffer(write_buffer);
|
ctx.WriteBuffer(write_buffer);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
|
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||||
}
|
}
|
||||||
@@ -287,13 +282,13 @@ void BSD_USA::GetSockName(HLERequestContext& ctx) {
|
|||||||
LOG_DEBUG(Service, "called. fd={}", fd);
|
LOG_DEBUG(Service, "called. fd={}", fd);
|
||||||
|
|
||||||
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
|
||||||
const Network::Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
|
const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
|
||||||
|
|
||||||
ctx.WriteBuffer(write_buffer);
|
ctx.WriteBuffer(write_buffer);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno != Network::Errno::E_SUCCESS ? -1 : 0);
|
rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
rb.Push<u32>(static_cast<u32>(write_buffer.size()));
|
||||||
}
|
}
|
||||||
@@ -301,19 +296,21 @@ void BSD_USA::GetSockName(HLERequestContext& ctx) {
|
|||||||
void BSD_USA::GetSockOpt(HLERequestContext& ctx) {
|
void BSD_USA::GetSockOpt(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const s32 fd = rp.Pop<s32>();
|
const s32 fd = rp.Pop<s32>();
|
||||||
const auto level = Network::SocketLevel(rp.Pop<u32>());
|
const u32 level = rp.Pop<u32>();
|
||||||
const auto optname = Network::OptName(rp.Pop<u32>());
|
const auto optname = static_cast<OptName>(rp.Pop<u32>());
|
||||||
|
|
||||||
std::vector<u8> optval(ctx.GetWriteBufferSize());
|
std::vector<u8> optval(ctx.GetWriteBufferSize());
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname, optval.size());
|
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} len={:#x}", fd, level, optname,
|
||||||
const Network::Errno err = GetSockOptImpl(fd, level, optname, optval);
|
optval.size());
|
||||||
|
|
||||||
|
const Errno err = GetSockOptImpl(fd, level, optname, optval);
|
||||||
|
|
||||||
ctx.WriteBuffer(optval);
|
ctx.WriteBuffer(optval);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
IPC::ResponseBuilder rb{ctx, 5};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(err == Network::Errno::E_SUCCESS ? 0 : -1);
|
rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1);
|
||||||
rb.PushEnum(err);
|
rb.PushEnum(err);
|
||||||
rb.Push<u32>(static_cast<u32>(optval.size()));
|
rb.Push<u32>(static_cast<u32>(optval.size()));
|
||||||
}
|
}
|
||||||
@@ -336,7 +333,7 @@ void BSD_USA::Fcntl(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
|
LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
|
||||||
|
|
||||||
const auto [ret, bsd_errno] = FcntlImpl(fd, Network::FcntlCmd(cmd), arg);
|
const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
@@ -348,11 +345,13 @@ void BSD_USA::SetSockOpt(HLERequestContext& ctx) {
|
|||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
|
|
||||||
const s32 fd = rp.Pop<s32>();
|
const s32 fd = rp.Pop<s32>();
|
||||||
const Network::SocketLevel level = Network::SocketLevel(rp.Pop<u32>());
|
const u32 level = rp.Pop<u32>();
|
||||||
const Network::OptName optname = Network::OptName(rp.Pop<u32>());
|
const OptName optname = static_cast<OptName>(rp.Pop<u32>());
|
||||||
const auto optval = ctx.ReadBuffer();
|
const auto optval = ctx.ReadBuffer();
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level, u32(optname), optval.size());
|
LOG_DEBUG(Service, "called. fd={} level={} optname={:#x} optlen={}", fd, level,
|
||||||
|
static_cast<u32>(optname), optval.size());
|
||||||
|
|
||||||
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
|
BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optval));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,7 +473,7 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
s32 ret;
|
s32 ret;
|
||||||
Network::Errno bsd_errno;
|
Errno bsd_errno;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0x8);
|
static_assert(sizeof(OutputParameters) == 0x8);
|
||||||
|
|
||||||
@@ -486,7 +485,7 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
|
|||||||
if (is_user) {
|
if (is_user) {
|
||||||
rb.PushRaw(OutputParameters{
|
rb.PushRaw(OutputParameters{
|
||||||
.ret = 0,
|
.ret = 0,
|
||||||
.bsd_errno = Network::Errno::E_INVAL,
|
.bsd_errno = Errno::INVAL,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -495,10 +494,10 @@ void BSD_USA::DuplicateSocket(HLERequestContext& ctx) {
|
|||||||
if (auto* res = std::get_if<s32>(&res_v)) {
|
if (auto* res = std::get_if<s32>(&res_v)) {
|
||||||
rb.PushRaw(OutputParameters{
|
rb.PushRaw(OutputParameters{
|
||||||
.ret = *res,
|
.ret = *res,
|
||||||
.bsd_errno = Network::Errno::E_SUCCESS,
|
.bsd_errno = Errno::SUCCESS,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
auto* err = std::get_if<Network::Errno>(&res_v);
|
auto* err = std::get_if<Errno>(&res_v);
|
||||||
rb.PushRaw(OutputParameters{
|
rb.PushRaw(OutputParameters{
|
||||||
.ret = 0,
|
.ret = 0,
|
||||||
.bsd_errno = *err,
|
.bsd_errno = *err,
|
||||||
@@ -513,7 +512,7 @@ void BSD_USA::EventFd(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
|
LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags);
|
||||||
|
|
||||||
BuildErrnoResponse(ctx, Network::Errno::E_SUCCESS);
|
BuildErrnoResponse(ctx, Errno::SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Work>
|
template <typename Work>
|
||||||
@@ -522,143 +521,132 @@ void BSD_USA::ExecuteWork(HLERequestContext& ctx, Work work) {
|
|||||||
work.Response(ctx);
|
work.Response(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Network::Errno> BSD_USA::SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol) {
|
std::pair<s32, Errno> BSD_USA::SocketImpl(Domain domain, Type type, Protocol protocol) {
|
||||||
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
|
// user bsd:u has restrictions on SOCK_SEQPACKET and SOCK_RAW
|
||||||
LOG_DEBUG(Network, "domain={},type={},protocol={}", u32(domain), u32(type), u32(protocol));
|
if (is_user && (type == Type::SEQPACKET || type == Type::RAW)) {
|
||||||
if (is_user && (type == Network::Type::SEQPACKET || type == Network::Type::RAW)) {
|
if (type == Type::RAW && domain == Domain::INET && protocol == Protocol::ICMP) {
|
||||||
if (type == Network::Type::RAW && domain == Network::Domain::INET && protocol == Network::Protocol::ICMP) {
|
|
||||||
// fine, can use on bsd:s and bsd:u
|
// fine, can use on bsd:s and bsd:u
|
||||||
} else {
|
} else {
|
||||||
return {-1, Network::Errno::E_INVAL};
|
return {-1, Errno::INVAL};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[maybe_unused]] const bool unk_flag = (u32(type) & 0x20000000) != 0;
|
[[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
|
||||||
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
|
UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
|
||||||
type = Network::Type(u32(type) & ~0x20000000);
|
type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
|
||||||
|
|
||||||
const s32 fd = FindFreeFileDescriptorHandle();
|
const s32 fd = FindFreeFileDescriptorHandle();
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
LOG_ERROR(Service, "No more file descriptors available");
|
LOG_ERROR(Service, "No more file descriptors available");
|
||||||
return {-1, Network::Errno::E_MFILE};
|
return {-1, Errno::MFILE};
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings::values.airplane_mode.GetValue() && IsConnectionBased(type)) {
|
|
||||||
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
|
|
||||||
file_descriptors[fd].reset();
|
|
||||||
return {-1, Network::Errno::E_NOTCONN};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
file_descriptors[fd] = FileDescriptor{};
|
file_descriptors[fd] = FileDescriptor{};
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
// ENONMEM might be thrown here
|
// ENONMEM might be thrown here
|
||||||
LOG_INFO(Service, "New socket fd={},domain={},type={},prot={}", fd, domain, type, protocol);
|
|
||||||
|
|
||||||
// While room is important -- we need to remember ICMP takes priority over **everything else**
|
LOG_INFO(Service, "New socket fd={}", fd);
|
||||||
// TODO: rework this so proxy sockets can be done transparently? -- like what if i need
|
|
||||||
// to browse the internet while playing LDN or something stupid like that?
|
|
||||||
auto room_member = Network::GetRoomMember().lock();
|
auto room_member = Network::GetRoomMember().lock();
|
||||||
if ((protocol != Network::Protocol::ICMP && protocol != Network::Protocol::ICMPV6)
|
if (room_member && room_member->IsConnected()) {
|
||||||
&& (room_member && room_member->IsConnected())) {
|
|
||||||
descriptor.socket = std::make_shared<Network::ProxySocket>();
|
descriptor.socket = std::make_shared<Network::ProxySocket>();
|
||||||
descriptor.socket->fd = fd;
|
|
||||||
} else {
|
} else {
|
||||||
descriptor.socket = std::make_shared<Network::Socket>();
|
descriptor.socket = std::make_shared<Network::Socket>();
|
||||||
}
|
}
|
||||||
auto const bsd_errno = descriptor.socket->Initialize(domain, type, protocol);
|
|
||||||
|
|
||||||
#if defined(__unix__) && !defined(__APPLE__)
|
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
|
||||||
// ...only unix has this issue it seems, ICMP works otherwise fine on win
|
|
||||||
if ((protocol == Network::Protocol::ICMP || protocol == Network::Protocol::ICMPV6)
|
|
||||||
&& bsd_errno != Network::Errno::E_SUCCESS) {
|
|
||||||
LOG_WARNING(Network, "Using ICMP emulated socket");
|
|
||||||
descriptor.socket = std::make_shared<Network::IcmpSocket>();
|
|
||||||
descriptor.socket->fd = fd;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
descriptor.is_connection_based = IsConnectionBased(type);
|
descriptor.is_connection_based = IsConnectionBased(type);
|
||||||
#ifdef _WIN32
|
|
||||||
if (descriptor.is_connection_based && descriptor.socket->fd == INVALID_SOCKET) {
|
if (Settings::values.airplane_mode.GetValue() && descriptor.is_connection_based) {
|
||||||
#else
|
LOG_ERROR(Service, "Airplane mode is enabled, cannot create socket");
|
||||||
if (descriptor.is_connection_based && descriptor.socket->fd == Network::Socket::INVALID_SOCKET) {
|
return {-1, Errno::NOTCONN};
|
||||||
#endif
|
|
||||||
file_descriptors[fd].reset();
|
|
||||||
return {-1, bsd_errno};
|
|
||||||
}
|
}
|
||||||
return {fd, Network::Errno::E_SUCCESS};
|
|
||||||
|
return {fd, Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Network::Errno> BSD_USA::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout) {
|
std::pair<s32, Errno> BSD_USA::PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer,
|
||||||
LOG_DEBUG(Network, "nfds={},timeout={}", nfds, timeout);
|
s32 nfds, s32 timeout) {
|
||||||
if (nfds <= 0) {
|
if (nfds <= 0) {
|
||||||
// When no entries are provided, -1 is returned with errno zero
|
// When no entries are provided, -1 is returned with errno zero
|
||||||
return {-1, Network::Errno::E_SUCCESS};
|
return {-1, Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
if (read_buffer.size() < nfds * sizeof(Network::PollFD)) {
|
if (read_buffer.size() < nfds * sizeof(PollFD)) {
|
||||||
return {-1, Network::Errno::E_INVAL};
|
return {-1, Errno::INVAL};
|
||||||
}
|
}
|
||||||
if (write_buffer.size() < nfds * sizeof(Network::PollFD)) {
|
if (write_buffer.size() < nfds * sizeof(PollFD)) {
|
||||||
return {-1, Network::Errno::E_INVAL};
|
return {-1, Errno::INVAL};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::span<const Network::PollFD> in_fds(reinterpret_cast<const Network::PollFD*>(read_buffer.data()), nfds);
|
std::vector<PollFD> fds(nfds);
|
||||||
std::span<Network::PollFD> out_fds(reinterpret_cast<Network::PollFD*>(write_buffer.data()), nfds);
|
std::memcpy(fds.data(), read_buffer.data(), nfds * sizeof(PollFD));
|
||||||
std::copy(in_fds.begin(), in_fds.end(), out_fds.begin());
|
|
||||||
|
|
||||||
if (timeout >= 0) {
|
if (timeout >= 0) {
|
||||||
const s64 seconds = timeout / 1000;
|
const s64 seconds = timeout / 1000;
|
||||||
const u64 nanoseconds = 1'000'000 * (u64(timeout) % 1000);
|
const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
|
||||||
|
|
||||||
if (seconds < 0) {
|
if (seconds < 0) {
|
||||||
return {-1, Network::Errno::E_INVAL};
|
return {-1, Errno::INVAL};
|
||||||
}
|
}
|
||||||
if (nanoseconds > 999'999'999) {
|
if (nanoseconds > 999'999'999) {
|
||||||
return {-1, Network::Errno::E_INVAL};
|
return {-1, Errno::INVAL};
|
||||||
}
|
}
|
||||||
} else if (timeout != -1) {
|
} else if (timeout != -1) {
|
||||||
return {-1, Network::Errno::E_INVAL};
|
return {-1, Errno::INVAL};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < in_fds.size(); ++i) {
|
for (PollFD& pollfd : fds) {
|
||||||
ASSERT(out_fds[i].fd == in_fds[i].fd && False(in_fds[i].revents));
|
ASSERT(False(pollfd.revents));
|
||||||
if (!IsFileDescriptorValid(in_fds[i].fd)) {
|
|
||||||
out_fds[i].revents = {};
|
if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
|
||||||
if (!file_descriptors[in_fds[i].fd])
|
LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
|
||||||
out_fds[i].revents = Network::PollEvents::NVAL;
|
pollfd.revents = PollEvents{};
|
||||||
return {0, Network::Errno::E_SUCCESS};
|
return {0, Errno::SUCCESS};
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
|
||||||
|
if (!descriptor) {
|
||||||
|
LOG_TRACE(Service, "File descriptor handle={} is not allocated", pollfd.fd);
|
||||||
|
pollfd.revents = PollEvents::Nval;
|
||||||
|
return {0, Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Network::HostPollFD> host_pollfds(in_fds.size());
|
std::vector<Network::PollFD> host_pollfds(fds.size());
|
||||||
std::transform(in_fds.begin(), in_fds.end(), host_pollfds.begin(), [](auto const e) {
|
std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [](PollFD pollfd) {
|
||||||
Network::HostPollFD result{};
|
Network::PollFD result;
|
||||||
result.socket = file_descriptors[e.fd]->socket.get();
|
result.socket = file_descriptors[pollfd.fd]->socket.get();
|
||||||
result.events = e.events;
|
result.events = Translate(pollfd.events);
|
||||||
result.revents = {};
|
result.revents = Network::PollEvents{};
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
auto const res = Network::Poll(host_pollfds, timeout);
|
|
||||||
for (size_t i = 0; i < in_fds.size(); ++i)
|
const auto result = Network::Poll(host_pollfds, timeout);
|
||||||
out_fds[i].revents = host_pollfds[i].revents;
|
|
||||||
return res;
|
const size_t num = host_pollfds.size();
|
||||||
|
for (size_t i = 0; i < num; ++i) {
|
||||||
|
fds[i].revents = Translate(host_pollfds[i].revents);
|
||||||
|
}
|
||||||
|
std::memcpy(write_buffer.data(), fds.data(), nfds * sizeof(PollFD));
|
||||||
|
|
||||||
|
return Translate(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Network::Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
std::pair<s32, Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||||
LOG_DEBUG(Network, "fd={}", fd);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Network::Errno::E_BADF};
|
return {-1, Errno::BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
const s32 new_fd = FindFreeFileDescriptorHandle();
|
const s32 new_fd = FindFreeFileDescriptorHandle();
|
||||||
if (new_fd < 0) {
|
if (new_fd < 0) {
|
||||||
LOG_ERROR(Service, "No more file descriptors available");
|
LOG_ERROR(Service, "No more file descriptors available");
|
||||||
return {-1, Network::Errno::E_MFILE};
|
return {-1, Errno::MFILE};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
auto [result, bsd_errno] = descriptor.socket->Accept();
|
auto [result, bsd_errno] = descriptor.socket->Accept();
|
||||||
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
if (bsd_errno != Network::Errno::SUCCESS) {
|
||||||
return {-1, bsd_errno};
|
return {-1, Translate(bsd_errno)};
|
||||||
}
|
}
|
||||||
|
|
||||||
file_descriptors[new_fd] = FileDescriptor{};
|
file_descriptors[new_fd] = FileDescriptor{};
|
||||||
@@ -666,222 +654,268 @@ std::pair<s32, Network::Errno> BSD_USA::AcceptImpl(s32 fd, std::vector<u8>& writ
|
|||||||
new_descriptor.socket = std::move(result.socket);
|
new_descriptor.socket = std::move(result.socket);
|
||||||
new_descriptor.is_connection_based = descriptor.is_connection_based;
|
new_descriptor.is_connection_based = descriptor.is_connection_based;
|
||||||
|
|
||||||
PutValue(write_buffer, result.sockaddr_in);
|
const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
|
||||||
return {new_fd, Network::Errno::E_SUCCESS};
|
PutValue(write_buffer, guest_addr_in);
|
||||||
|
|
||||||
|
return {new_fd, Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Errno BSD_USA::BindImpl(s32 fd, std::span<const u8> addr) {
|
Errno BSD_USA::BindImpl(s32 fd, std::span<const u8> addr) {
|
||||||
LOG_DEBUG(Network, "fd={}", fd);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addr_in = GetValue<Network::SockAddrIn>(addr);
|
auto addr_in = GetValue<SockAddrIn>(addr);
|
||||||
return file_descriptors[fd]->socket->Bind(addr_in);
|
|
||||||
|
return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Errno BSD_USA::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
Errno BSD_USA::ConnectImpl(s32 fd, std::span<const u8> addr) {
|
||||||
LOG_DEBUG(Network, "fd={}", fd);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto addr_in = GetValue<Network::SockAddrIn>(addr);
|
auto addr_in = GetValue<SockAddrIn>(addr);
|
||||||
const Network::Errno result = file_descriptors[fd]->socket->Connect(addr_in);
|
|
||||||
if (result == Network::Errno::E_ISCONN) {
|
const Errno result = Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
|
||||||
|
|
||||||
|
if (result == Errno::ISCONN) {
|
||||||
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
|
LOG_DEBUG(Service, "returned ISCONN - socket already connected");
|
||||||
return Network::Errno::E_SUCCESS;
|
return Errno::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
Errno BSD_USA::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||||
LOG_DEBUG(Network, "fd={}", fd);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
|
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
|
||||||
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
if (bsd_errno != Network::Errno::SUCCESS) {
|
||||||
return bsd_errno;
|
return Translate(bsd_errno);
|
||||||
}
|
}
|
||||||
ASSERT(write_buffer.size() >= addr_in.len);
|
const SockAddrIn guest_addrin = Translate(addr_in);
|
||||||
write_buffer.resize(addr_in.len);
|
|
||||||
PutValue(write_buffer, addr_in);
|
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
|
||||||
return bsd_errno;
|
write_buffer.resize(sizeof(guest_addrin));
|
||||||
|
PutValue(write_buffer, guest_addrin);
|
||||||
|
return Translate(bsd_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
Errno BSD_USA::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
|
||||||
LOG_DEBUG(Network, "fd={}", fd);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
|
const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
|
||||||
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
if (bsd_errno != Network::Errno::SUCCESS) {
|
||||||
return bsd_errno;
|
return Translate(bsd_errno);
|
||||||
}
|
}
|
||||||
ASSERT(write_buffer.size() >= addr_in.len);
|
const SockAddrIn guest_addrin = Translate(addr_in);
|
||||||
write_buffer.resize(addr_in.len);
|
|
||||||
PutValue(write_buffer, addr_in);
|
ASSERT(write_buffer.size() >= sizeof(guest_addrin));
|
||||||
return bsd_errno;
|
write_buffer.resize(sizeof(guest_addrin));
|
||||||
|
PutValue(write_buffer, guest_addrin);
|
||||||
|
return Translate(bsd_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Errno BSD_USA::ListenImpl(s32 fd, s32 backlog) {
|
Errno BSD_USA::ListenImpl(s32 fd, s32 backlog) {
|
||||||
LOG_DEBUG(Network, "fd={},backlog={}", fd, backlog);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
return file_descriptors[fd]->socket->Listen(backlog);
|
return Translate(file_descriptors[fd]->socket->Listen(backlog));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Network::Errno> BSD_USA::FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg) {
|
std::pair<s32, Errno> BSD_USA::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
|
||||||
LOG_DEBUG(Network, "fd={},cmd={},arg={}", fd, u32(cmd), arg);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Network::Errno::E_BADF};
|
return {-1, Errno::BADF};
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return {-1, Network::Errno::E_BADF};
|
return {-1, Errno::BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case Network::FcntlCmd::GETFL:
|
case FcntlCmd::GETFL:
|
||||||
ASSERT(arg == 0);
|
ASSERT(arg == 0);
|
||||||
return {descriptor.flags, Network::Errno::E_SUCCESS};
|
return {descriptor.flags, Errno::SUCCESS};
|
||||||
case Network::FcntlCmd::SETFL: {
|
case FcntlCmd::SETFL: {
|
||||||
const bool enable = (arg & u32(Network::FcntlFlags::NONBLOCK_NX)) != 0;
|
const bool enable = (arg & Network::FLAG_O_NONBLOCK) != 0;
|
||||||
const Network::Errno bsd_errno = descriptor.socket->SetNonBlock(enable);
|
const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
|
||||||
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
if (bsd_errno != Errno::SUCCESS) {
|
||||||
return {-1, bsd_errno};
|
return {-1, bsd_errno};
|
||||||
}
|
}
|
||||||
descriptor.flags = arg;
|
descriptor.flags = arg;
|
||||||
return {0, Network::Errno::E_SUCCESS};
|
return {0, Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
|
UNIMPLEMENTED_MSG("Unimplemented cmd={}", cmd);
|
||||||
return {-1, Network::Errno::E_SUCCESS};
|
return {-1, Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Errno BSD_USA::GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval) {
|
Errno BSD_USA::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) {
|
||||||
LOG_DEBUG(Network, "fd={},level={},optname={}", fd, u32(level), u32(optname));
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level != Network::SocketLevel::SOCKET) {
|
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
|
||||||
LOG_WARNING(Service, "(stubbed) level fd={}, level={}, optname={}", fd, level, optname);
|
UNIMPLEMENTED_MSG("Unknown getsockopt level");
|
||||||
|
return Errno::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
||||||
|
|
||||||
switch (optname) {
|
switch (optname) {
|
||||||
case Network::OptName::ERROR_: {
|
case OptName::ERROR_: {
|
||||||
auto [pending_err, getsockopt_err] = socket->GetPendingError();
|
auto [pending_err, getsockopt_err] = socket->GetPendingError();
|
||||||
if (getsockopt_err == Network::Errno::E_SUCCESS) {
|
if (getsockopt_err == Network::Errno::SUCCESS) {
|
||||||
|
Errno translated_pending_err = Translate(pending_err);
|
||||||
ASSERT_OR_EXECUTE_MSG(
|
ASSERT_OR_EXECUTE_MSG(
|
||||||
optval.size() == sizeof(Network::Errno), { return Network::Errno::E_INVAL; },
|
optval.size() == sizeof(Errno), { return Errno::INVAL; },
|
||||||
"Incorrect getsockopt option size");
|
"Incorrect getsockopt option size");
|
||||||
optval.resize(sizeof(Network::Errno));
|
optval.resize(sizeof(Errno));
|
||||||
PutValue(optval, pending_err);
|
PutValue(optval, translated_pending_err);
|
||||||
}
|
}
|
||||||
return getsockopt_err;
|
return Translate(getsockopt_err);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
||||||
return Network::Errno::E_SUCCESS;
|
return Errno::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Errno BSD_USA::SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
Errno BSD_USA::SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval) {
|
||||||
LOG_DEBUG(Service, "fd={},level={},optname={}", fd, level, optname);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level != static_cast<u32>(SocketLevel::SOCKET)) {
|
||||||
|
LOG_WARNING(Service, "(STUBBED) setsockopt with level={}, optname={}", level, optname);
|
||||||
|
return Errno::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
Network::SocketBase* const socket = file_descriptors[fd]->socket.get();
|
||||||
return socket->SetSockOpt(level, optname, optval);
|
|
||||||
|
if (optname == OptName::LINGER) {
|
||||||
|
ASSERT(optval.size() == sizeof(Linger));
|
||||||
|
auto linger = GetValue<Linger>(optval);
|
||||||
|
ASSERT(linger.onoff == 0 || linger.onoff == 1);
|
||||||
|
|
||||||
|
return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(optval.size() == sizeof(u32));
|
||||||
|
auto value = GetValue<u32>(optval);
|
||||||
|
|
||||||
|
switch (optname) {
|
||||||
|
case OptName::REUSEADDR:
|
||||||
|
ASSERT(value == 0 || value == 1);
|
||||||
|
return Translate(socket->SetReuseAddr(value != 0));
|
||||||
|
case OptName::KEEPALIVE:
|
||||||
|
ASSERT(value == 0 || value == 1);
|
||||||
|
return Translate(socket->SetKeepAlive(value != 0));
|
||||||
|
case OptName::BROADCAST:
|
||||||
|
ASSERT(value == 0 || value == 1);
|
||||||
|
return Translate(socket->SetBroadcast(value != 0));
|
||||||
|
case OptName::SNDBUF:
|
||||||
|
return Translate(socket->SetSndBuf(value));
|
||||||
|
case OptName::RCVBUF:
|
||||||
|
return Translate(socket->SetRcvBuf(value));
|
||||||
|
case OptName::SNDTIMEO:
|
||||||
|
return Translate(socket->SetSndTimeo(value));
|
||||||
|
case OptName::RCVTIMEO:
|
||||||
|
return Translate(socket->SetRcvTimeo(value));
|
||||||
|
case OptName::NOSIGPIPE:
|
||||||
|
LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value);
|
||||||
|
return Errno::SUCCESS;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented optname={}", optname);
|
||||||
|
return Errno::SUCCESS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Errno BSD_USA::ShutdownImpl(s32 fd, s32 how) {
|
Errno BSD_USA::ShutdownImpl(s32 fd, s32 how) {
|
||||||
LOG_DEBUG(Network, "fd={},how={}", fd, how);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
return file_descriptors[fd]->socket->Shutdown(Network::ShutdownHow(how));
|
const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
|
||||||
|
return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Network::Errno> BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
|
std::pair<s32, Errno> BSD_USA::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
|
||||||
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Network::Errno::E_BADF};
|
return {-1, Errno::BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
// Apply flags
|
// Apply flags
|
||||||
if ((flags & u32(Network::MsgOpt::DONTWAIT)) != 0) {
|
using Network::FLAG_MSG_DONTWAIT;
|
||||||
flags &= ~u32(Network::MsgOpt::DONTWAIT);
|
using Network::FLAG_O_NONBLOCK;
|
||||||
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0) {
|
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
|
||||||
|
flags &= ~FLAG_MSG_DONTWAIT;
|
||||||
|
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
||||||
descriptor.socket->SetNonBlock(true);
|
descriptor.socket->SetNonBlock(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto [ret, bsd_errno] = descriptor.socket->Recv(flags, message);
|
const auto [ret, bsd_errno] = Translate(descriptor.socket->Recv(flags, message));
|
||||||
|
|
||||||
// Restore original state
|
// Restore original state
|
||||||
if ((descriptor.flags & u32(Network::FcntlFlags::NONBLOCK_NX)) == 0)
|
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
||||||
descriptor.socket->SetNonBlock(false);
|
descriptor.socket->SetNonBlock(false);
|
||||||
|
}
|
||||||
|
|
||||||
return {ret, bsd_errno};
|
return {ret, bsd_errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Network::Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message, std::vector<u8>& addr) {
|
std::pair<s32, Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
||||||
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
std::vector<u8>& addr) {
|
||||||
if (!IsFileDescriptorValid(fd))
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Network::Errno::E_BADF};
|
return {-1, Errno::BADF};
|
||||||
if (message.size() == 0)
|
}
|
||||||
return {0, Network::Errno::E_SUCCESS};
|
|
||||||
if (!std::in_range<u32>(message.size()))
|
|
||||||
return {-1, Network::Errno::E_FAULT};
|
|
||||||
|
|
||||||
FileDescriptor& descriptor = *file_descriptors[fd];
|
FileDescriptor& descriptor = *file_descriptors[fd];
|
||||||
|
|
||||||
@@ -895,75 +929,79 @@ std::pair<s32, Network::Errno> BSD_USA::RecvFromImpl(s32 fd, u32 flags, std::vec
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply flags
|
// Apply flags
|
||||||
auto const is_nonblock = descriptor.socket->GetNonBlock();
|
using Network::FLAG_MSG_DONTWAIT;
|
||||||
auto const f_dontwait = (flags & u32(Network::MsgOpt::DONTWAIT)) != 0;
|
using Network::FLAG_O_NONBLOCK;
|
||||||
auto const f_waitall = (flags & u32(Network::MsgOpt::WAITALL)) != 0;
|
if ((flags & FLAG_MSG_DONTWAIT) != 0) {
|
||||||
// DONTWAIT set clears WAITALL, if socket is non-blocking it also clears WAITALL
|
flags &= ~FLAG_MSG_DONTWAIT;
|
||||||
if (f_dontwait || (f_waitall && is_nonblock))
|
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
||||||
flags &= ~u32(Network::MsgOpt::WAITALL);
|
descriptor.socket->SetNonBlock(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (f_dontwait) descriptor.socket->SetNonBlock(true); //set non-block
|
const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
|
||||||
const auto [ret, bsd_errno] = descriptor.socket->RecvFrom(flags, message, p_addr_in);
|
|
||||||
if (f_dontwait) descriptor.socket->SetNonBlock(is_nonblock); //restore
|
// Restore original state
|
||||||
|
if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
|
||||||
|
descriptor.socket->SetNonBlock(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (p_addr_in) {
|
if (p_addr_in) {
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
addr.clear();
|
addr.clear();
|
||||||
} else {
|
} else {
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
PutValue(addr, addr_in);
|
const SockAddrIn result = Translate(addr_in);
|
||||||
|
PutValue(addr, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {ret, bsd_errno};
|
return {ret, bsd_errno};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Network::Errno> BSD_USA::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
|
std::pair<s32, Errno> BSD_USA::SendImpl(s32 fd, u32 flags, std::span<const u8> message) {
|
||||||
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Network::Errno::E_BADF};
|
return {-1, Errno::BADF};
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return {-1, Network::Errno::E_BADF};
|
return {-1, Errno::BADF};
|
||||||
}
|
}
|
||||||
return file_descriptors[fd]->socket->Send(message, flags);
|
return Translate(file_descriptors[fd]->socket->Send(message, flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Network::Errno> BSD_USA::SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::span<const u8> addr) {
|
std::pair<s32, Errno> BSD_USA::SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
||||||
LOG_DEBUG(Network, "fd={},flags={}", fd, flags);
|
std::span<const u8> addr) {
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return {-1, Network::Errno::E_BADF};
|
return {-1, Errno::BADF};
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return {-1, Network::Errno::E_BADF};
|
return {-1, Errno::BADF};
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SockAddrIn addr_in{};
|
Network::SockAddrIn addr_in;
|
||||||
Network::SockAddrIn* p_addr_in = nullptr;
|
Network::SockAddrIn* p_addr_in = nullptr;
|
||||||
if (!addr.empty()) {
|
if (!addr.empty()) {
|
||||||
ASSERT(addr.size() >= 16);
|
ASSERT(addr.size() >= 16);
|
||||||
auto guest_addr_in = GetValue<Network::SockAddrIn>(addr);
|
auto guest_addr_in = GetValue<SockAddrIn>(addr);
|
||||||
addr_in = guest_addr_in;
|
addr_in = Translate(guest_addr_in);
|
||||||
p_addr_in = &addr_in;
|
p_addr_in = &addr_in;
|
||||||
}
|
}
|
||||||
|
|
||||||
return file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in);
|
return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::Errno BSD_USA::CloseImpl(s32 fd) {
|
Errno BSD_USA::CloseImpl(s32 fd) {
|
||||||
LOG_DEBUG(Network, "fd={}", fd);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]->socket) {
|
if (!file_descriptors[fd]->socket) {
|
||||||
LOG_WARNING(Service, "Uninitialized socket");
|
LOG_WARNING(Service, "Uninitialized socket");
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const bsd_errno = file_descriptors[fd]->socket->Close();
|
const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
|
||||||
if (bsd_errno != Network::Errno::E_SUCCESS) {
|
if (bsd_errno != Errno::SUCCESS) {
|
||||||
return bsd_errno;
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,27 +1011,26 @@ Network::Errno BSD_USA::CloseImpl(s32 fd) {
|
|||||||
return bsd_errno;
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::variant<s32, Network::Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
|
std::variant<s32, Errno> BSD_USA::DuplicateSocketImpl(s32 fd) {
|
||||||
LOG_DEBUG(Network, "fd={}", fd);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return Network::Errno::E_BADF;
|
return Errno::BADF;
|
||||||
}
|
}
|
||||||
// Don't use IsFileDescriptorValid as new_fd isn't a proper handle yet
|
|
||||||
if (s32 const new_fd = FindFreeFileDescriptorHandle(); new_fd >= 0) {
|
const s32 new_fd = FindFreeFileDescriptorHandle();
|
||||||
file_descriptors[new_fd] = FileDescriptor{
|
if (new_fd < 0) {
|
||||||
.socket = file_descriptors[fd]->socket,
|
|
||||||
.flags = file_descriptors[fd]->flags,
|
|
||||||
.is_connection_based = file_descriptors[fd]->is_connection_based,
|
|
||||||
};
|
|
||||||
return new_fd;
|
|
||||||
} else {
|
|
||||||
LOG_ERROR(Service, "No more file descriptors available");
|
LOG_ERROR(Service, "No more file descriptors available");
|
||||||
return Network::Errno::E_MFILE;
|
return Errno::MFILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
file_descriptors[new_fd] = FileDescriptor{
|
||||||
|
.socket = file_descriptors[fd]->socket,
|
||||||
|
.flags = file_descriptors[fd]->flags,
|
||||||
|
.is_connection_based = file_descriptors[fd]->is_connection_based,
|
||||||
|
};
|
||||||
|
return new_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
|
std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
|
||||||
LOG_DEBUG(Network, "fd={}", fd);
|
|
||||||
if (!IsFileDescriptorValid(fd)) {
|
if (!IsFileDescriptorValid(fd)) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -1005,40 +1042,41 @@ std::optional<std::shared_ptr<Network::SocketBase>> BSD_USA::GetSocket(s32 fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
s32 BSD_USA::FindFreeFileDescriptorHandle() noexcept {
|
s32 BSD_USA::FindFreeFileDescriptorHandle() noexcept {
|
||||||
// first three file descriptors are reserved for:
|
for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
|
||||||
// STDOUT_FILENO, STDIN_FILENO and STDERR_FILENO
|
if (!file_descriptors[fd]) {
|
||||||
for (s32 fd = 0; fd < s32(file_descriptors.size()); ++fd)
|
|
||||||
if (!file_descriptors[fd])
|
|
||||||
return fd;
|
return fd;
|
||||||
|
}
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BSD_USA::IsFileDescriptorValid(s32 fd) const noexcept {
|
bool BSD_USA::IsFileDescriptorValid(s32 fd) const noexcept {
|
||||||
if (fd < 0 || fd >= s32(file_descriptors.size())) {
|
if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
|
||||||
LOG_ERROR(Service, "Invalid handle={}", fd);
|
LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!file_descriptors[fd]) {
|
if (!file_descriptors[fd]) {
|
||||||
LOG_ERROR(Service, "handle={} is not allocated", fd);
|
LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept {
|
void BSD_USA::BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept {
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.Push<s32>(bsd_errno == Network::Errno::E_SUCCESS ? 0 : -1);
|
rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
|
void BSD_USA::OnProxyPacketReceived(const Network::ProxyPacket& packet) {
|
||||||
for (auto& optional_descriptor : file_descriptors) {
|
for (auto& optional_descriptor : file_descriptors) {
|
||||||
if (optional_descriptor.has_value()) {
|
if (!optional_descriptor.has_value()) {
|
||||||
FileDescriptor& descriptor = *optional_descriptor;
|
continue;
|
||||||
descriptor.socket.get()->HandleProxyPacket(packet);
|
|
||||||
}
|
}
|
||||||
|
FileDescriptor& descriptor = *optional_descriptor;
|
||||||
|
descriptor.socket.get()->HandleProxyPacket(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/internal_network/socket_types.h"
|
#include "common/socket_types.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
#include "core/hle/service/sockets/sockets.h"
|
#include "core/hle/service/sockets/sockets.h"
|
||||||
#include "network/network.h"
|
#include "network/network.h"
|
||||||
@@ -35,8 +35,8 @@ public:
|
|||||||
// These methods are called from SSL; the first two are also called from
|
// These methods are called from SSL; the first two are also called from
|
||||||
// this class for the corresponding IPC methods.
|
// this class for the corresponding IPC methods.
|
||||||
// On the real device, the SSL service makes IPC calls to this service.
|
// On the real device, the SSL service makes IPC calls to this service.
|
||||||
std::variant<s32, Network::Errno> DuplicateSocketImpl(s32 fd);
|
std::variant<s32, Errno> DuplicateSocketImpl(s32 fd);
|
||||||
Network::Errno CloseImpl(s32 fd);
|
Errno CloseImpl(s32 fd);
|
||||||
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
|
std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -58,7 +58,7 @@ private:
|
|||||||
std::span<const u8> read_buffer;
|
std::span<const u8> read_buffer;
|
||||||
std::vector<u8> write_buffer;
|
std::vector<u8> write_buffer;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Network::Errno bsd_errno{};
|
Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AcceptWork {
|
struct AcceptWork {
|
||||||
@@ -68,7 +68,7 @@ private:
|
|||||||
s32 fd;
|
s32 fd;
|
||||||
std::vector<u8> write_buffer;
|
std::vector<u8> write_buffer;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Network::Errno bsd_errno{};
|
Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ConnectWork {
|
struct ConnectWork {
|
||||||
@@ -77,7 +77,7 @@ private:
|
|||||||
|
|
||||||
s32 fd;
|
s32 fd;
|
||||||
std::span<const u8> addr;
|
std::span<const u8> addr;
|
||||||
Network::Errno bsd_errno{};
|
Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RecvWork {
|
struct RecvWork {
|
||||||
@@ -88,7 +88,7 @@ private:
|
|||||||
u32 flags;
|
u32 flags;
|
||||||
std::vector<u8> message;
|
std::vector<u8> message;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Network::Errno bsd_errno{};
|
Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RecvFromWork {
|
struct RecvFromWork {
|
||||||
@@ -100,7 +100,7 @@ private:
|
|||||||
std::vector<u8> message;
|
std::vector<u8> message;
|
||||||
std::vector<u8> addr;
|
std::vector<u8> addr;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Network::Errno bsd_errno{};
|
Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SendWork {
|
struct SendWork {
|
||||||
@@ -111,7 +111,7 @@ private:
|
|||||||
u32 flags;
|
u32 flags;
|
||||||
std::span<const u8> message;
|
std::span<const u8> message;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Network::Errno bsd_errno{};
|
Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SendToWork {
|
struct SendToWork {
|
||||||
@@ -123,7 +123,7 @@ private:
|
|||||||
std::span<const u8> message;
|
std::span<const u8> message;
|
||||||
std::span<const u8> addr;
|
std::span<const u8> addr;
|
||||||
s32 ret{};
|
s32 ret{};
|
||||||
Network::Errno bsd_errno{};
|
Errno bsd_errno{};
|
||||||
};
|
};
|
||||||
|
|
||||||
void RegisterClient(HLERequestContext& ctx);
|
void RegisterClient(HLERequestContext& ctx);
|
||||||
@@ -155,29 +155,29 @@ private:
|
|||||||
template <typename Work>
|
template <typename Work>
|
||||||
void ExecuteWork(HLERequestContext& ctx, Work work);
|
void ExecuteWork(HLERequestContext& ctx, Work work);
|
||||||
|
|
||||||
std::pair<s32, Network::Errno> SocketImpl(Network::Domain domain, Network::Type type, Network::Protocol protocol);
|
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
|
||||||
std::pair<s32, Network::Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
|
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::span<const u8> read_buffer, s32 nfds, s32 timeout);
|
||||||
std::pair<s32, Network::Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
|
std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||||
Network::Errno BindImpl(s32 fd, std::span<const u8> addr);
|
Errno BindImpl(s32 fd, std::span<const u8> addr);
|
||||||
Network::Errno ConnectImpl(s32 fd, std::span<const u8> addr);
|
Errno ConnectImpl(s32 fd, std::span<const u8> addr);
|
||||||
Network::Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||||
Network::Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
|
||||||
Network::Errno ListenImpl(s32 fd, s32 backlog);
|
Errno ListenImpl(s32 fd, s32 backlog);
|
||||||
std::pair<s32, Network::Errno> FcntlImpl(s32 fd, Network::FcntlCmd cmd, s32 arg);
|
std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
|
||||||
Network::Errno GetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::vector<u8>& optval);
|
Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval);
|
||||||
Network::Errno SetSockOptImpl(s32 fd, Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval);
|
Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, std::span<const u8> optval);
|
||||||
Network::Errno ShutdownImpl(s32 fd, s32 how);
|
Errno ShutdownImpl(s32 fd, s32 how);
|
||||||
std::pair<s32, Network::Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
|
std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
|
||||||
std::pair<s32, Network::Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
|
||||||
std::vector<u8>& addr);
|
std::vector<u8>& addr);
|
||||||
std::pair<s32, Network::Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
|
std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message);
|
||||||
std::pair<s32, Network::Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message,
|
||||||
std::span<const u8> addr);
|
std::span<const u8> addr);
|
||||||
|
|
||||||
s32 FindFreeFileDescriptorHandle() noexcept;
|
s32 FindFreeFileDescriptorHandle() noexcept;
|
||||||
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
||||||
|
|
||||||
void BuildErrnoResponse(HLERequestContext& ctx, Network::Errno bsd_errno) const noexcept;
|
void BuildErrnoResponse(HLERequestContext& ctx, Errno bsd_errno) const noexcept;
|
||||||
|
|
||||||
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
|
static inline std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors{};
|
||||||
|
|
||||||
|
|||||||
@@ -102,39 +102,39 @@ static bool IsBlockedHost(const std::string& host) {
|
|||||||
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
|
[&host](const std::string& domain) { return host.find(domain) != std::string::npos; });
|
||||||
}
|
}
|
||||||
|
|
||||||
static NetDbError GetAddrInfoErrorToNetDbError(Network::GetAddrInfoError result) {
|
static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) {
|
||||||
// These combinations have been verified on console (but are not
|
// These combinations have been verified on console (but are not
|
||||||
// exhaustive).
|
// exhaustive).
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case Network::GetAddrInfoError::SUCCESS:
|
case GetAddrInfoError::SUCCESS:
|
||||||
return NetDbError::Success;
|
return NetDbError::Success;
|
||||||
case Network::GetAddrInfoError::AGAIN:
|
case GetAddrInfoError::AGAIN:
|
||||||
return NetDbError::TryAgain;
|
return NetDbError::TryAgain;
|
||||||
case Network::GetAddrInfoError::NODATA:
|
case GetAddrInfoError::NODATA:
|
||||||
return NetDbError::HostNotFound;
|
return NetDbError::HostNotFound;
|
||||||
case Network::GetAddrInfoError::SERVICE:
|
case GetAddrInfoError::SERVICE:
|
||||||
return NetDbError::Success;
|
return NetDbError::Success;
|
||||||
default:
|
default:
|
||||||
return NetDbError::HostNotFound;
|
return NetDbError::HostNotFound;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Network::Errno GetAddrInfoErrorToErrno(Network::GetAddrInfoError result) {
|
static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) {
|
||||||
// These combinations have been verified on console (but are not
|
// These combinations have been verified on console (but are not
|
||||||
// exhaustive).
|
// exhaustive).
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case Network::GetAddrInfoError::SUCCESS:
|
case GetAddrInfoError::SUCCESS:
|
||||||
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
|
// Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for
|
||||||
// some reason, but that doesn't seem useful to implement.
|
// some reason, but that doesn't seem useful to implement.
|
||||||
return Network::Errno::E_SUCCESS;
|
return Errno::SUCCESS;
|
||||||
case Network::GetAddrInfoError::AGAIN:
|
case GetAddrInfoError::AGAIN:
|
||||||
return Network::Errno::E_SUCCESS;
|
return Errno::SUCCESS;
|
||||||
case Network::GetAddrInfoError::NODATA:
|
case GetAddrInfoError::NODATA:
|
||||||
return Network::Errno::E_SUCCESS;
|
return Errno::SUCCESS;
|
||||||
case Network::GetAddrInfoError::SERVICE:
|
case GetAddrInfoError::SERVICE:
|
||||||
return Network::Errno::E_INVAL;
|
return Errno::INVAL;
|
||||||
default:
|
default:
|
||||||
return Network::Errno::E_SUCCESS;
|
return Errno::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +155,9 @@ static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) {
|
|||||||
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
|
// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo
|
||||||
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
|
// behaves the same on Unix and Windows, unlike gethostbyname where Windows
|
||||||
// doesn't implement h_errno.
|
// doesn't implement h_errno.
|
||||||
static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrInfo> vec, std::string_view host) {
|
static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec,
|
||||||
|
std::string_view host) {
|
||||||
|
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
// h_name: use the input hostname (append nul-terminated)
|
// h_name: use the input hostname (append nul-terminated)
|
||||||
AppendNulTerminated(data, host);
|
AppendNulTerminated(data, host);
|
||||||
@@ -163,12 +165,12 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrI
|
|||||||
|
|
||||||
Append<u32_be>(data, 0); // count of h_aliases
|
Append<u32_be>(data, 0); // count of h_aliases
|
||||||
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
|
// (If the count were nonzero, the aliases would be appended as nul-terminated here.)
|
||||||
Append<u16_be>(data, u16(Network::Domain::INET)); // h_addrtype
|
Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype
|
||||||
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
|
Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length
|
||||||
// h_addr_list:
|
// h_addr_list:
|
||||||
size_t count = vec.size();
|
size_t count = vec.size();
|
||||||
ASSERT(count <= UINT32_MAX);
|
ASSERT(count <= UINT32_MAX);
|
||||||
Append<u32_be>(data, u32(count));
|
Append<u32_be>(data, static_cast<uint32_t>(count));
|
||||||
for (const Network::AddrInfo& addrinfo : vec) {
|
for (const Network::AddrInfo& addrinfo : vec) {
|
||||||
// On the Switch, this is passed through htonl despite already being
|
// On the Switch, this is passed through htonl despite already being
|
||||||
// big-endian, so it ends up as little-endian.
|
// big-endian, so it ends up as little-endian.
|
||||||
@@ -180,7 +182,7 @@ static std::vector<u8> SerializeAddrInfoAsHostEnt(std::span<const Network::AddrI
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
|
static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) {
|
||||||
struct InputParameters {
|
struct InputParameters {
|
||||||
u8 use_nsd_resolve;
|
u8 use_nsd_resolve;
|
||||||
u32 cancel_handle;
|
u32 cancel_handle;
|
||||||
@@ -203,7 +205,7 @@ static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLEReq
|
|||||||
// Prevent resolution of Nintendo servers
|
// Prevent resolution of Nintendo servers
|
||||||
if (IsBlockedHost(host)) {
|
if (IsBlockedHost(host)) {
|
||||||
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||||
return {0, Network::GetAddrInfoError::AGAIN};
|
return {0, GetAddrInfoError::AGAIN};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
auto res_v = Network::GetAddressInfo(host, /*service*/ std::nullopt);
|
||||||
@@ -211,10 +213,10 @@ static std::pair<u32, Network::GetAddrInfoError> GetHostByNameRequestImpl(HLEReq
|
|||||||
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
|
const std::vector<u8> data = SerializeAddrInfoAsHostEnt(*res, host);
|
||||||
const u32 data_size = u32(data.size());
|
const u32 data_size = u32(data.size());
|
||||||
ctx.WriteBuffer(data, 0);
|
ctx.WriteBuffer(data, 0);
|
||||||
return {data_size, Network::GetAddrInfoError::SUCCESS};
|
return {data_size, GetAddrInfoError::SUCCESS};
|
||||||
}
|
}
|
||||||
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
||||||
return {0, *err};
|
return {0, Translate(*err)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
||||||
@@ -222,7 +224,7 @@ void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
NetDbError netdb_error;
|
NetDbError netdb_error;
|
||||||
Network::Errno bsd_errno;
|
Errno bsd_errno;
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0xc);
|
static_assert(sizeof(OutputParameters) == 0xc);
|
||||||
@@ -242,7 +244,7 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
|
|||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
NetDbError netdb_error;
|
NetDbError netdb_error;
|
||||||
Network::Errno bsd_errno;
|
Errno bsd_errno;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0xc);
|
static_assert(sizeof(OutputParameters) == 0xc);
|
||||||
|
|
||||||
@@ -255,23 +257,24 @@ void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec, std::string_view host) {
|
static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec,
|
||||||
|
std::string_view host) {
|
||||||
// Adapted from
|
// Adapted from
|
||||||
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
|
// https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
|
|
||||||
for (const Network::AddrInfo& addrinfo : vec) {
|
for (const Network::AddrInfo& addrinfo : vec) {
|
||||||
// serialized addrinfo:
|
// serialized addrinfo:
|
||||||
Append<u32_be>(data, 0xBEEFCAFE); // magic
|
Append<u32_be>(data, 0xBEEFCAFE); // magic
|
||||||
Append<u32_be>(data, 0); // ai_flags
|
Append<u32_be>(data, 0); // ai_flags
|
||||||
Append<u32_be>(data, u32(addrinfo.family)); // ai_family
|
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family
|
||||||
Append<u32_be>(data, u32(addrinfo.socket_type)); // ai_socktype
|
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype
|
||||||
Append<u32_be>(data, u32(addrinfo.protocol)); // ai_protocol
|
Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol
|
||||||
Append<u32_be>(data, 16); // ai_addrlen
|
Append<u32_be>(data, 16); // ai_addrlen
|
||||||
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
|
// ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size
|
||||||
|
|
||||||
// ai_addr:
|
// ai_addr:
|
||||||
Append<u16_be>(data, u16(addrinfo.addr.family)); // sin_family
|
Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family
|
||||||
// On the Switch, the following fields are passed through htonl despite
|
// On the Switch, the following fields are passed through htonl despite
|
||||||
// already being big-endian, so they end up as little-endian.
|
// already being big-endian, so they end up as little-endian.
|
||||||
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
|
Append<u16_le>(data, addrinfo.addr.portno); // sin_port
|
||||||
@@ -293,7 +296,7 @@ static std::vector<u8> SerializeAddrInfo(std::span<const Network::AddrInfo> vec,
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
|
static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) {
|
||||||
struct InputParameters {
|
struct InputParameters {
|
||||||
u8 use_nsd_resolve;
|
u8 use_nsd_resolve;
|
||||||
u32 cancel_handle;
|
u32 cancel_handle;
|
||||||
@@ -318,7 +321,7 @@ static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLEReque
|
|||||||
// Prevent resolution of Nintendo servers
|
// Prevent resolution of Nintendo servers
|
||||||
if (IsBlockedHost(host)) {
|
if (IsBlockedHost(host)) {
|
||||||
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
LOG_WARNING(Network, "Resolution of hostname {} requested, returning EAI_AGAIN", host);
|
||||||
return {0, Network::GetAddrInfoError::AGAIN};
|
return {0, GetAddrInfoError::AGAIN};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> service = std::nullopt;
|
std::optional<std::string> service = std::nullopt;
|
||||||
@@ -328,23 +331,24 @@ static std::pair<u32, Network::GetAddrInfoError> GetAddrInfoRequestImpl(HLEReque
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Serialized hints are also passed in a buffer, but are ignored for now.
|
// Serialized hints are also passed in a buffer, but are ignored for now.
|
||||||
|
|
||||||
auto res_v = Network::GetAddressInfo(host, service);
|
auto res_v = Network::GetAddressInfo(host, service);
|
||||||
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
|
if (auto* res = std::get_if<std::vector<Network::AddrInfo>>(&res_v)) {
|
||||||
const std::vector<u8> data = SerializeAddrInfo(*res, host);
|
const std::vector<u8> data = SerializeAddrInfo(*res, host);
|
||||||
const u32 data_size = u32(data.size());
|
const u32 data_size = u32(data.size());
|
||||||
ctx.WriteBuffer(data, 0);
|
ctx.WriteBuffer(data, 0);
|
||||||
return {data_size, Network::GetAddrInfoError::SUCCESS};
|
return {data_size, GetAddrInfoError::SUCCESS};
|
||||||
}
|
}
|
||||||
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
auto* err = std::get_if<Network::GetAddrInfoError>(&res_v);
|
||||||
return {0, *err};
|
return {0, Translate(*err)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
||||||
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
|
auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx);
|
||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
Network::Errno bsd_errno;
|
Errno bsd_errno;
|
||||||
Network::GetAddrInfoError gai_error;
|
GetAddrInfoError gai_error;
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0xc);
|
static_assert(sizeof(OutputParameters) == 0xc);
|
||||||
@@ -360,7 +364,7 @@ void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
|
void SFDNSRES::GetGaiStringErrorRequest(HLERequestContext& ctx) {
|
||||||
struct InputParameters {
|
struct InputParameters {
|
||||||
Network::GetAddrInfoError gai_errno;
|
GetAddrInfoError gai_errno;
|
||||||
};
|
};
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
auto input = rp.PopRaw<InputParameters>();
|
auto input = rp.PopRaw<InputParameters>();
|
||||||
@@ -378,9 +382,9 @@ void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) {
|
|||||||
|
|
||||||
struct OutputParameters {
|
struct OutputParameters {
|
||||||
u32 data_size;
|
u32 data_size;
|
||||||
Network::GetAddrInfoError gai_error;
|
GetAddrInfoError gai_error;
|
||||||
NetDbError netdb_error;
|
NetDbError netdb_error;
|
||||||
Network::Errno bsd_errno;
|
Errno bsd_errno;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(OutputParameters) == 0x10);
|
static_assert(sizeof(OutputParameters) == 0x10);
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/internal_network/socket_types.h"
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
@@ -16,6 +15,242 @@ class System;
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
|
enum class Errno : u32 {
|
||||||
|
SUCCESS = 0,
|
||||||
|
BADF = 9,
|
||||||
|
AGAIN = 11,
|
||||||
|
INVAL = 22,
|
||||||
|
MFILE = 24,
|
||||||
|
PIPE = 32,
|
||||||
|
MSGSIZE = 90,
|
||||||
|
CONNABORTED = 103,
|
||||||
|
CONNRESET = 104,
|
||||||
|
NOTCONN = 107,
|
||||||
|
TIMEDOUT = 110,
|
||||||
|
CONNREFUSED = 111,
|
||||||
|
INPROGRESS = 115,
|
||||||
|
ISCONN = 106,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GetAddrInfoError : s32 {
|
||||||
|
SUCCESS = 0,
|
||||||
|
ADDRFAMILY = 1,
|
||||||
|
AGAIN = 2,
|
||||||
|
BADFLAGS = 3,
|
||||||
|
FAIL = 4,
|
||||||
|
FAMILY = 5,
|
||||||
|
MEMORY = 6,
|
||||||
|
NODATA = 7,
|
||||||
|
NONAME = 8,
|
||||||
|
SERVICE = 9,
|
||||||
|
SOCKTYPE = 10,
|
||||||
|
SYSTEM = 11,
|
||||||
|
BADHINTS = 12,
|
||||||
|
PROTOCOL = 13,
|
||||||
|
OVERFLOW_ = 14, // avoid name collision with Windows macro
|
||||||
|
OTHER = 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Domain : u32 {
|
||||||
|
Unspecified = 0,
|
||||||
|
INET = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Type : u32 {
|
||||||
|
Unspecified = 0,
|
||||||
|
STREAM = 1,
|
||||||
|
DGRAM = 2,
|
||||||
|
RAW = 3,
|
||||||
|
SEQPACKET = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Protocol : u32 {
|
||||||
|
IP = 0,
|
||||||
|
ICMP = 1,
|
||||||
|
TCP = 6,
|
||||||
|
UDP = 17,
|
||||||
|
//
|
||||||
|
IPV6 = 41,
|
||||||
|
RAW = 255,
|
||||||
|
//
|
||||||
|
HOPOPTS = 0,
|
||||||
|
IGMP = 2,
|
||||||
|
GGP = 3,
|
||||||
|
IPV4 = 4,
|
||||||
|
ST = 7,
|
||||||
|
EGP = 8,
|
||||||
|
PIGP = 9,
|
||||||
|
RCCMON = 10,
|
||||||
|
NVPII = 11,
|
||||||
|
PUP = 12,
|
||||||
|
ARGUS = 13,
|
||||||
|
EMCON = 14,
|
||||||
|
XNET = 15,
|
||||||
|
CHAOS = 16,
|
||||||
|
MUX = 18,
|
||||||
|
MEAS = 19,
|
||||||
|
HMP = 20,
|
||||||
|
PRM = 21,
|
||||||
|
IDP = 22,
|
||||||
|
TRUNK1 = 23,
|
||||||
|
TRUNK2 = 24,
|
||||||
|
LEAF1 = 25,
|
||||||
|
LEAF2 = 26,
|
||||||
|
RDP = 27,
|
||||||
|
IRTP = 28,
|
||||||
|
TP = 29,
|
||||||
|
BLT = 30,
|
||||||
|
NSP = 31,
|
||||||
|
INP = 32,
|
||||||
|
DCCP = 33,
|
||||||
|
//3PC = 34,
|
||||||
|
IDPR = 35,
|
||||||
|
XTP = 36,
|
||||||
|
DDP = 37,
|
||||||
|
CMTP = 38,
|
||||||
|
TPXX = 39,
|
||||||
|
IL = 40,
|
||||||
|
SDRP = 42,
|
||||||
|
ROUTING = 43,
|
||||||
|
FRAGMENT = 44,
|
||||||
|
IDRP = 45,
|
||||||
|
RSVP = 46,
|
||||||
|
GRE = 47,
|
||||||
|
MHRP = 48,
|
||||||
|
BHA = 49,
|
||||||
|
ESP = 50,
|
||||||
|
AH = 51,
|
||||||
|
INLSP = 52,
|
||||||
|
SWIPE = 53,
|
||||||
|
NHRP = 54,
|
||||||
|
MOBILE = 55,
|
||||||
|
TLSP = 56,
|
||||||
|
SKIP = 57,
|
||||||
|
ICMPV6 = 58,
|
||||||
|
NONE = 59,
|
||||||
|
DSTOPTS = 60,
|
||||||
|
AHIP = 61,
|
||||||
|
CFTP = 62,
|
||||||
|
HELLO = 63,
|
||||||
|
SATEXPAK = 64,
|
||||||
|
KRYPTOLAN = 65,
|
||||||
|
RVD = 66,
|
||||||
|
IPPC = 67,
|
||||||
|
ADFS = 68,
|
||||||
|
SATMON = 69,
|
||||||
|
VISA = 70,
|
||||||
|
IPCV = 71,
|
||||||
|
CPNX = 72,
|
||||||
|
CPHB = 73,
|
||||||
|
WSN = 74,
|
||||||
|
PVP = 75,
|
||||||
|
BRSATMON = 76,
|
||||||
|
ND = 77,
|
||||||
|
WBMON = 78,
|
||||||
|
WBEXPAK = 79,
|
||||||
|
EON = 80,
|
||||||
|
VMTP = 81,
|
||||||
|
SVMTP = 82,
|
||||||
|
VINES = 83,
|
||||||
|
TTP = 84,
|
||||||
|
IGP = 85,
|
||||||
|
DGP = 86,
|
||||||
|
TCF = 87,
|
||||||
|
IGRP = 88,
|
||||||
|
OSPFIGP = 89,
|
||||||
|
SRPC = 90,
|
||||||
|
LARP = 91,
|
||||||
|
MTP = 92,
|
||||||
|
AX25 = 93,
|
||||||
|
IPEIP = 94,
|
||||||
|
MICP = 95,
|
||||||
|
SCCSP = 96,
|
||||||
|
ETHERIP = 97,
|
||||||
|
ENCAP = 98,
|
||||||
|
APES = 99,
|
||||||
|
GMTP = 100,
|
||||||
|
IPCOMP = 108,
|
||||||
|
SCTP = 132,
|
||||||
|
MH = 135,
|
||||||
|
UDPLITE = 136,
|
||||||
|
HIP = 139,
|
||||||
|
SHIM6 = 140,
|
||||||
|
PIM = 103,
|
||||||
|
CARP = 112,
|
||||||
|
PGM = 113,
|
||||||
|
MPLS = 137,
|
||||||
|
PFSYNC = 240,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SocketLevel : u32 {
|
||||||
|
IP = 0,
|
||||||
|
TCP = 6,
|
||||||
|
SOCKET = 0xffff, // i.e. SOL_SOCKET
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class OptName : u32 {
|
||||||
|
REUSEADDR = 0x4,
|
||||||
|
KEEPALIVE = 0x8,
|
||||||
|
BROADCAST = 0x20,
|
||||||
|
LINGER = 0x80,
|
||||||
|
SNDBUF = 0x1001,
|
||||||
|
RCVBUF = 0x1002,
|
||||||
|
SNDTIMEO = 0x1005,
|
||||||
|
RCVTIMEO = 0x1006,
|
||||||
|
ERROR_ = 0x1007, // avoid name collision with Windows macro
|
||||||
|
NOSIGPIPE = 0x800, // at least according to libnx
|
||||||
|
ACCEPTFILTER = 0x1000,
|
||||||
|
BINTIME = 0x2000,
|
||||||
|
NO_OFFLOAD = 0x4000,
|
||||||
|
NO_DDP = 0x8000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ShutdownHow : s32 {
|
||||||
|
RD = 0,
|
||||||
|
WR = 1,
|
||||||
|
RDWR = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FcntlCmd : s32 {
|
||||||
|
GETFL = 3,
|
||||||
|
SETFL = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SockAddrIn {
|
||||||
|
u8 len;
|
||||||
|
u8 family;
|
||||||
|
u16 portno;
|
||||||
|
std::array<u8, 4> ip;
|
||||||
|
std::array<u8, 248> zeroes;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(SockAddrIn) == 0x100);
|
||||||
|
|
||||||
|
enum class PollEvents : u16 {
|
||||||
|
// Using Pascal case because IN is a macro on Windows.
|
||||||
|
In = 1 << 0,
|
||||||
|
Pri = 1 << 1,
|
||||||
|
Out = 1 << 2,
|
||||||
|
Err = 1 << 3,
|
||||||
|
Hup = 1 << 4,
|
||||||
|
Nval = 1 << 5,
|
||||||
|
RdNorm = 1 << 6,
|
||||||
|
RdBand = 1 << 7,
|
||||||
|
WrBand = 1 << 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
||||||
|
|
||||||
|
struct PollFD {
|
||||||
|
s32 fd;
|
||||||
|
PollEvents events;
|
||||||
|
PollEvents revents;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Linger {
|
||||||
|
u32 onoff;
|
||||||
|
u32 linger;
|
||||||
|
};
|
||||||
|
|
||||||
void LoopProcess(Core::System& system);
|
void LoopProcess(Core::System& system);
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
} // namespace Service::Sockets
|
||||||
|
|||||||
@@ -15,42 +15,388 @@
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
const char* Translate(Network::GetAddrInfoError error) {
|
Errno Translate(Network::Errno value) {
|
||||||
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
|
switch (value) {
|
||||||
|
case Network::Errno::SUCCESS:
|
||||||
|
return Errno::SUCCESS;
|
||||||
|
case Network::Errno::BADF:
|
||||||
|
return Errno::BADF;
|
||||||
|
case Network::Errno::AGAIN:
|
||||||
|
return Errno::AGAIN;
|
||||||
|
case Network::Errno::INVAL:
|
||||||
|
return Errno::INVAL;
|
||||||
|
case Network::Errno::MFILE:
|
||||||
|
return Errno::MFILE;
|
||||||
|
case Network::Errno::PIPE:
|
||||||
|
return Errno::PIPE;
|
||||||
|
case Network::Errno::CONNREFUSED:
|
||||||
|
return Errno::CONNREFUSED;
|
||||||
|
case Network::Errno::NOTCONN:
|
||||||
|
return Errno::NOTCONN;
|
||||||
|
case Network::Errno::TIMEDOUT:
|
||||||
|
return Errno::TIMEDOUT;
|
||||||
|
case Network::Errno::CONNABORTED:
|
||||||
|
return Errno::CONNABORTED;
|
||||||
|
case Network::Errno::CONNRESET:
|
||||||
|
return Errno::CONNRESET;
|
||||||
|
case Network::Errno::INPROGRESS:
|
||||||
|
return Errno::INPROGRESS;
|
||||||
|
case Network::Errno::ISCONN:
|
||||||
|
return Errno::ISCONN;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented errno={}", value);
|
||||||
|
return Errno::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
|
||||||
|
return {value.first, Translate(value.second)};
|
||||||
|
}
|
||||||
|
|
||||||
|
GetAddrInfoError Translate(Network::GetAddrInfoError error) {
|
||||||
switch (error) {
|
switch (error) {
|
||||||
case Network::GetAddrInfoError::SUCCESS:
|
case Network::GetAddrInfoError::SUCCESS:
|
||||||
return "Success";
|
return GetAddrInfoError::SUCCESS;
|
||||||
case Network::GetAddrInfoError::ADDRFAMILY:
|
case Network::GetAddrInfoError::ADDRFAMILY:
|
||||||
return "Address family for hostname not supported";
|
return GetAddrInfoError::ADDRFAMILY;
|
||||||
case Network::GetAddrInfoError::AGAIN:
|
case Network::GetAddrInfoError::AGAIN:
|
||||||
return "Temporary failure in name resolution";
|
return GetAddrInfoError::AGAIN;
|
||||||
case Network::GetAddrInfoError::BADFLAGS:
|
case Network::GetAddrInfoError::BADFLAGS:
|
||||||
return "Invalid value for ai_flags";
|
return GetAddrInfoError::BADFLAGS;
|
||||||
case Network::GetAddrInfoError::FAIL:
|
case Network::GetAddrInfoError::FAIL:
|
||||||
return "Non-recoverable failure in name resolution";
|
return GetAddrInfoError::FAIL;
|
||||||
case Network::GetAddrInfoError::FAMILY:
|
case Network::GetAddrInfoError::FAMILY:
|
||||||
return "ai_family not supported";
|
return GetAddrInfoError::FAMILY;
|
||||||
case Network::GetAddrInfoError::MEMORY:
|
case Network::GetAddrInfoError::MEMORY:
|
||||||
return "Memory allocation failure";
|
return GetAddrInfoError::MEMORY;
|
||||||
case Network::GetAddrInfoError::NODATA:
|
case Network::GetAddrInfoError::NODATA:
|
||||||
return "No address associated with hostname";
|
return GetAddrInfoError::NODATA;
|
||||||
case Network::GetAddrInfoError::NONAME:
|
case Network::GetAddrInfoError::NONAME:
|
||||||
return "hostname nor servname provided, or not known";
|
return GetAddrInfoError::NONAME;
|
||||||
case Network::GetAddrInfoError::SERVICE:
|
case Network::GetAddrInfoError::SERVICE:
|
||||||
return "servname not supported for ai_socktype";
|
return GetAddrInfoError::SERVICE;
|
||||||
case Network::GetAddrInfoError::SOCKTYPE:
|
case Network::GetAddrInfoError::SOCKTYPE:
|
||||||
return "ai_socktype not supported";
|
return GetAddrInfoError::SOCKTYPE;
|
||||||
case Network::GetAddrInfoError::SYSTEM:
|
case Network::GetAddrInfoError::SYSTEM:
|
||||||
return "System error returned in errno";
|
return GetAddrInfoError::SYSTEM;
|
||||||
case Network::GetAddrInfoError::BADHINTS:
|
case Network::GetAddrInfoError::BADHINTS:
|
||||||
return "Invalid value for hints";
|
return GetAddrInfoError::BADHINTS;
|
||||||
case Network::GetAddrInfoError::PROTOCOL:
|
case Network::GetAddrInfoError::PROTOCOL:
|
||||||
return "Resolved protocol is unknown";
|
return GetAddrInfoError::PROTOCOL;
|
||||||
case Network::GetAddrInfoError::OVERFLOW_:
|
case Network::GetAddrInfoError::OVERFLOW_:
|
||||||
|
return GetAddrInfoError::OVERFLOW_;
|
||||||
|
case Network::GetAddrInfoError::OTHER:
|
||||||
|
return GetAddrInfoError::OTHER;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error);
|
||||||
|
return GetAddrInfoError::OTHER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Translate(GetAddrInfoError error) {
|
||||||
|
// https://android.googlesource.com/platform/bionic/+/085543106/libc/dns/net/getaddrinfo.c#254
|
||||||
|
switch (error) {
|
||||||
|
case GetAddrInfoError::SUCCESS:
|
||||||
|
return "Success";
|
||||||
|
case GetAddrInfoError::ADDRFAMILY:
|
||||||
|
return "Address family for hostname not supported";
|
||||||
|
case GetAddrInfoError::AGAIN:
|
||||||
|
return "Temporary failure in name resolution";
|
||||||
|
case GetAddrInfoError::BADFLAGS:
|
||||||
|
return "Invalid value for ai_flags";
|
||||||
|
case GetAddrInfoError::FAIL:
|
||||||
|
return "Non-recoverable failure in name resolution";
|
||||||
|
case GetAddrInfoError::FAMILY:
|
||||||
|
return "ai_family not supported";
|
||||||
|
case GetAddrInfoError::MEMORY:
|
||||||
|
return "Memory allocation failure";
|
||||||
|
case GetAddrInfoError::NODATA:
|
||||||
|
return "No address associated with hostname";
|
||||||
|
case GetAddrInfoError::NONAME:
|
||||||
|
return "hostname nor servname provided, or not known";
|
||||||
|
case GetAddrInfoError::SERVICE:
|
||||||
|
return "servname not supported for ai_socktype";
|
||||||
|
case GetAddrInfoError::SOCKTYPE:
|
||||||
|
return "ai_socktype not supported";
|
||||||
|
case GetAddrInfoError::SYSTEM:
|
||||||
|
return "System error returned in errno";
|
||||||
|
case GetAddrInfoError::BADHINTS:
|
||||||
|
return "Invalid value for hints";
|
||||||
|
case GetAddrInfoError::PROTOCOL:
|
||||||
|
return "Resolved protocol is unknown";
|
||||||
|
case GetAddrInfoError::OVERFLOW_:
|
||||||
return "Argument buffer overflow";
|
return "Argument buffer overflow";
|
||||||
default:
|
default:
|
||||||
return "Unknown error";
|
return "Unknown error";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Network::Domain Translate(Domain domain) {
|
||||||
|
switch (domain) {
|
||||||
|
case Domain::Unspecified:
|
||||||
|
return Network::Domain::Unspecified;
|
||||||
|
case Domain::INET:
|
||||||
|
return Network::Domain::INET;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Domain Translate(Network::Domain domain) {
|
||||||
|
switch (domain) {
|
||||||
|
case Network::Domain::Unspecified:
|
||||||
|
return Domain::Unspecified;
|
||||||
|
case Network::Domain::INET:
|
||||||
|
return Domain::INET;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented domain={}", domain);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Network::Type Translate(Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case Type::Unspecified:
|
||||||
|
return Network::Type::Unspecified;
|
||||||
|
case Type::STREAM:
|
||||||
|
return Network::Type::STREAM;
|
||||||
|
case Type::DGRAM:
|
||||||
|
return Network::Type::DGRAM;
|
||||||
|
case Type::RAW:
|
||||||
|
return Network::Type::RAW;
|
||||||
|
case Type::SEQPACKET:
|
||||||
|
return Network::Type::SEQPACKET;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
||||||
|
return Network::Type{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Type Translate(Network::Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case Network::Type::Unspecified: return Type::Unspecified;
|
||||||
|
case Network::Type::STREAM: return Type::STREAM;
|
||||||
|
case Network::Type::DGRAM: return Type::DGRAM;
|
||||||
|
case Network::Type::RAW: return Type::RAW;
|
||||||
|
case Network::Type::SEQPACKET: return Type::SEQPACKET;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented type={}", type);
|
||||||
|
return Type{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NETWORK_PROTOCOL_TRANSLATE_LIST \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV6) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(RAW) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGMP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(GGP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPV4) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(ST) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(EGP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIGP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(RCCMON) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(NVPII) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(PUP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(ARGUS) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(EMCON) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(XNET) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(CHAOS) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(MUX) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(MEAS) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(HMP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(PRM) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK1) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(TRUNK2) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF1) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(LEAF2) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(RDP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IRTP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(TP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(BLT) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(NSP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(INP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(DCCP) \
|
||||||
|
/*NETWORK_PROTOCOL_TRANSLATE_ELEM(3PC)*/ \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDPR) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(XTP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(DDP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(CMTP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(TPXX) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IL) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(SDRP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(ROUTING) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(FRAGMENT) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IDRP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(RSVP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(GRE) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(MHRP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(BHA) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(ESP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(AH) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(INLSP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(SWIPE) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(NHRP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(MOBILE) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(TLSP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(SKIP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(ICMPV6) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(NONE) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(DSTOPTS) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(AHIP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(CFTP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(HELLO) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATEXPAK) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(KRYPTOLAN) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(RVD) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPPC) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(ADFS) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(SATMON) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(VISA) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCV) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPNX) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(CPHB) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(WSN) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(PVP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(BRSATMON) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(ND) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBMON) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(WBEXPAK) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(EON) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(VMTP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(SVMTP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(VINES) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(TTP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(DGP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(TCF) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IGRP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(OSPFIGP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(SRPC) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(LARP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(MTP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(AX25) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPEIP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(MICP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCCSP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(ETHERIP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(ENCAP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(APES) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(GMTP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(IPCOMP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(SCTP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(MH) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(UDPLITE) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(HIP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(SHIM6) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(PIM) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(CARP) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(PGM) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(MPLS) \
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_ELEM(PFSYNC)
|
||||||
|
[[nodiscard]] Network::Protocol Translate(Protocol protocol) {
|
||||||
|
switch (protocol) {
|
||||||
|
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Protocol::name: return Network::Protocol::name;
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_LIST
|
||||||
|
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[[nodiscard]] Protocol Translate(Network::Protocol protocol) {
|
||||||
|
switch (protocol) {
|
||||||
|
#define NETWORK_PROTOCOL_TRANSLATE_ELEM(name) case Network::Protocol::name: return Protocol::name;
|
||||||
|
NETWORK_PROTOCOL_TRANSLATE_LIST
|
||||||
|
#undef NETWORK_PROTOCOL_TRANSLATE_ELEM
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef NETWORK_PROTOCOL_TRANSLATE_LIST
|
||||||
|
|
||||||
|
Network::PollEvents Translate(PollEvents flags) {
|
||||||
|
Network::PollEvents result{};
|
||||||
|
const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) {
|
||||||
|
if (True(flags & from)) {
|
||||||
|
flags &= ~from;
|
||||||
|
result |= to;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
translate(PollEvents::In, Network::PollEvents::In);
|
||||||
|
translate(PollEvents::Pri, Network::PollEvents::Pri);
|
||||||
|
translate(PollEvents::Out, Network::PollEvents::Out);
|
||||||
|
translate(PollEvents::Err, Network::PollEvents::Err);
|
||||||
|
translate(PollEvents::Hup, Network::PollEvents::Hup);
|
||||||
|
translate(PollEvents::Nval, Network::PollEvents::Nval);
|
||||||
|
translate(PollEvents::RdNorm, Network::PollEvents::RdNorm);
|
||||||
|
translate(PollEvents::RdBand, Network::PollEvents::RdBand);
|
||||||
|
translate(PollEvents::WrBand, Network::PollEvents::WrBand);
|
||||||
|
|
||||||
|
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PollEvents Translate(Network::PollEvents flags) {
|
||||||
|
PollEvents result{};
|
||||||
|
const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) {
|
||||||
|
if (True(flags & from)) {
|
||||||
|
flags &= ~from;
|
||||||
|
result |= to;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
translate(Network::PollEvents::In, PollEvents::In);
|
||||||
|
translate(Network::PollEvents::Pri, PollEvents::Pri);
|
||||||
|
translate(Network::PollEvents::Out, PollEvents::Out);
|
||||||
|
translate(Network::PollEvents::Err, PollEvents::Err);
|
||||||
|
translate(Network::PollEvents::Hup, PollEvents::Hup);
|
||||||
|
translate(Network::PollEvents::Nval, PollEvents::Nval);
|
||||||
|
translate(Network::PollEvents::RdNorm, PollEvents::RdNorm);
|
||||||
|
translate(Network::PollEvents::RdBand, PollEvents::RdBand);
|
||||||
|
translate(Network::PollEvents::WrBand, PollEvents::WrBand);
|
||||||
|
|
||||||
|
UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Network::SockAddrIn Translate(SockAddrIn value) {
|
||||||
|
// All lengths are valid, from [0 upto 256]
|
||||||
|
return {
|
||||||
|
.family = Translate(Domain(value.family)),
|
||||||
|
.ip = value.ip,
|
||||||
|
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
SockAddrIn Translate(Network::SockAddrIn value) {
|
||||||
|
return {
|
||||||
|
.len = 16,
|
||||||
|
.family = static_cast<u8>(Translate(value.family)),
|
||||||
|
.portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
|
||||||
|
.ip = value.ip,
|
||||||
|
.zeroes = {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Network::ShutdownHow Translate(ShutdownHow how) {
|
||||||
|
switch (how) {
|
||||||
|
case ShutdownHow::RD:
|
||||||
|
return Network::ShutdownHow::RD;
|
||||||
|
case ShutdownHow::WR:
|
||||||
|
return Network::ShutdownHow::WR;
|
||||||
|
case ShutdownHow::RDWR:
|
||||||
|
return Network::ShutdownHow::RDWR;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented how={}", how);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
} // namespace Service::Sockets
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -14,7 +11,49 @@
|
|||||||
|
|
||||||
namespace Service::Sockets {
|
namespace Service::Sockets {
|
||||||
|
|
||||||
|
/// Translate abstract errno to guest errno
|
||||||
|
Errno Translate(Network::Errno value);
|
||||||
|
|
||||||
|
/// Translate abstract return value errno pair to guest return value errno pair
|
||||||
|
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
|
||||||
|
|
||||||
|
/// Translate abstract getaddrinfo error to guest getaddrinfo error
|
||||||
|
GetAddrInfoError Translate(Network::GetAddrInfoError value);
|
||||||
|
|
||||||
/// Translate guest error to string
|
/// Translate guest error to string
|
||||||
const char* Translate(Network::GetAddrInfoError value);
|
const char* Translate(GetAddrInfoError value);
|
||||||
|
|
||||||
|
/// Translate guest domain to abstract domain
|
||||||
|
Network::Domain Translate(Domain domain);
|
||||||
|
|
||||||
|
/// Translate abstract domain to guest domain
|
||||||
|
Domain Translate(Network::Domain domain);
|
||||||
|
|
||||||
|
/// Translate guest type to abstract type
|
||||||
|
Network::Type Translate(Type type);
|
||||||
|
|
||||||
|
/// Translate abstract type to guest type
|
||||||
|
Type Translate(Network::Type type);
|
||||||
|
|
||||||
|
/// Translate guest protocol to abstract protocol
|
||||||
|
Network::Protocol Translate(Protocol protocol);
|
||||||
|
|
||||||
|
/// Translate abstract protocol to guest protocol
|
||||||
|
Protocol Translate(Network::Protocol protocol);
|
||||||
|
|
||||||
|
/// Translate guest poll event flags to abstract poll event flags
|
||||||
|
Network::PollEvents Translate(PollEvents flags);
|
||||||
|
|
||||||
|
/// Translate abstract poll event flags to guest poll event flags
|
||||||
|
PollEvents Translate(Network::PollEvents flags);
|
||||||
|
|
||||||
|
/// Translate guest socket address structure to abstract socket address structure
|
||||||
|
Network::SockAddrIn Translate(SockAddrIn value);
|
||||||
|
|
||||||
|
/// Translate abstract socket address structure to guest socket address structure
|
||||||
|
SockAddrIn Translate(Network::SockAddrIn value);
|
||||||
|
|
||||||
|
/// Translate guest shutdown mode to abstract shutdown mode
|
||||||
|
Network::ShutdownHow Translate(ShutdownHow how);
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
} // namespace Service::Sockets
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ public:
|
|||||||
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
|
auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD_USA>("bsd:u");
|
||||||
if (bsd) {
|
if (bsd) {
|
||||||
auto err = bsd->CloseImpl(fd);
|
auto err = bsd->CloseImpl(fd);
|
||||||
if (err != Network::Errno::E_SUCCESS) {
|
if (err != Service::Sockets::Errno::SUCCESS) {
|
||||||
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
|
LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,7 +230,7 @@ private:
|
|||||||
R_UNLESS(socket, ResultNoSocket);
|
R_UNLESS(socket, ResultNoSocket);
|
||||||
const bool non_block = mode == IoMode::NonBlocking;
|
const bool non_block = mode == IoMode::NonBlocking;
|
||||||
const Network::Errno error = socket->SetNonBlock(non_block);
|
const Network::Errno error = socket->SetNonBlock(non_block);
|
||||||
if (error != Network::Errno::E_SUCCESS) {
|
if (error != Network::Errno::SUCCESS) {
|
||||||
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block);
|
||||||
}
|
}
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
|||||||
@@ -327,10 +327,10 @@ public:
|
|||||||
BIO_clear_retry_flags(bio);
|
BIO_clear_retry_flags(bio);
|
||||||
auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0);
|
auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0);
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case Network::Errno::E_SUCCESS:
|
case Network::Errno::SUCCESS:
|
||||||
*actual_p = actual;
|
*actual_p = actual;
|
||||||
return 1;
|
return 1;
|
||||||
case Network::Errno::E_AGAIN:
|
case Network::Errno::AGAIN:
|
||||||
BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
|
BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
@@ -346,13 +346,13 @@ public:
|
|||||||
BIO_clear_retry_flags(bio);
|
BIO_clear_retry_flags(bio);
|
||||||
auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len});
|
auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len});
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case Network::Errno::E_SUCCESS:
|
case Network::Errno::SUCCESS:
|
||||||
*actual_p = actual;
|
*actual_p = actual;
|
||||||
if (actual == 0) {
|
if (actual == 0) {
|
||||||
self->got_read_eof = true;
|
self->got_read_eof = true;
|
||||||
}
|
}
|
||||||
return actual ? 1 : 0;
|
return actual ? 1 : 0;
|
||||||
case Network::Errno::E_AGAIN:
|
case Network::Errno::AGAIN:
|
||||||
BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
|
BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <common/socket_types.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include "core/internal_network/socket_types.h"
|
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/internal_network/socket_types.h"
|
#include "common/socket_types.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
@@ -31,10 +31,68 @@ namespace Network {
|
|||||||
class SocketBase;
|
class SocketBase;
|
||||||
class Socket;
|
class Socket;
|
||||||
|
|
||||||
struct HostPollFD {
|
/// Error code for network functions
|
||||||
SocketBase* socket = nullptr;
|
enum class Errno {
|
||||||
Network::PollEvents events = {};
|
SUCCESS,
|
||||||
Network::PollEvents revents = {};
|
BADF,
|
||||||
|
INVAL,
|
||||||
|
MFILE,
|
||||||
|
PIPE,
|
||||||
|
NOTCONN,
|
||||||
|
AGAIN,
|
||||||
|
CONNREFUSED,
|
||||||
|
CONNRESET,
|
||||||
|
CONNABORTED,
|
||||||
|
HOSTUNREACH,
|
||||||
|
NETDOWN,
|
||||||
|
NETUNREACH,
|
||||||
|
TIMEDOUT,
|
||||||
|
MSGSIZE,
|
||||||
|
INPROGRESS,
|
||||||
|
ISCONN,
|
||||||
|
OTHER,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GetAddrInfoError {
|
||||||
|
SUCCESS,
|
||||||
|
ADDRFAMILY,
|
||||||
|
AGAIN,
|
||||||
|
BADFLAGS,
|
||||||
|
FAIL,
|
||||||
|
FAMILY,
|
||||||
|
MEMORY,
|
||||||
|
NODATA,
|
||||||
|
NONAME,
|
||||||
|
SERVICE,
|
||||||
|
SOCKTYPE,
|
||||||
|
SYSTEM,
|
||||||
|
BADHINTS,
|
||||||
|
PROTOCOL,
|
||||||
|
OVERFLOW_,
|
||||||
|
OTHER,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Cross-platform poll fd structure
|
||||||
|
|
||||||
|
enum class PollEvents : u16 {
|
||||||
|
// Using Pascal case because IN is a macro on Windows.
|
||||||
|
In = 1 << 0,
|
||||||
|
Pri = 1 << 1,
|
||||||
|
Out = 1 << 2,
|
||||||
|
Err = 1 << 3,
|
||||||
|
Hup = 1 << 4,
|
||||||
|
Nval = 1 << 5,
|
||||||
|
RdNorm = 1 << 6,
|
||||||
|
RdBand = 1 << 7,
|
||||||
|
WrBand = 1 << 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
||||||
|
|
||||||
|
struct PollFD {
|
||||||
|
SocketBase* socket;
|
||||||
|
PollEvents events;
|
||||||
|
PollEvents revents;
|
||||||
};
|
};
|
||||||
|
|
||||||
class NetworkInstance {
|
class NetworkInstance {
|
||||||
@@ -43,10 +101,6 @@ public:
|
|||||||
~NetworkInstance();
|
~NetworkInstance();
|
||||||
};
|
};
|
||||||
|
|
||||||
sockaddr_in TranslateFromSockAddrIn(Network::SockAddrIn input);
|
|
||||||
Network::SockAddrIn TranslateToSockAddrIn(sockaddr_in input);
|
|
||||||
s32 TranslateMsgOptToNative(s32 flags);
|
|
||||||
|
|
||||||
void CancelPendingSocketOperations();
|
void CancelPendingSocketOperations();
|
||||||
void RestartSocketOperations();
|
void RestartSocketOperations();
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace Network {
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||||
|
|
||||||
ULONG buf_size = 0;
|
ULONG buf_size = 0;
|
||||||
if (GetAdaptersAddresses(
|
if (GetAdaptersAddresses(
|
||||||
@@ -66,7 +66,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<NetworkInterface> result;
|
std::vector<Network::NetworkInterface> result;
|
||||||
|
|
||||||
for (auto* a = addrs; a; a = a->Next) {
|
for (auto* a = addrs; a; a = a->Next) {
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr)
|
gw = reinterpret_cast<sockaddr_in*>(a->FirstGatewayAddress->Address.lpSockaddr)
|
||||||
->sin_addr;
|
->sin_addr;
|
||||||
|
|
||||||
result.emplace_back(NetworkInterface{
|
result.emplace_back(Network::NetworkInterface{
|
||||||
.name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}),
|
.name = Common::UTF16ToUTF8(std::wstring{a->FriendlyName}),
|
||||||
.ip_address = ip,
|
.ip_address = ip,
|
||||||
.subnet_mask = mask,
|
.subnet_mask = mask,
|
||||||
@@ -103,7 +103,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
std::vector<Network::NetworkInterface> GetAvailableNetworkInterfaces() {
|
||||||
#if defined(__ANDROID__) || defined(__linux__)
|
#if defined(__ANDROID__) || defined(__linux__)
|
||||||
struct ifaddrs* ifaddr = nullptr;
|
struct ifaddrs* ifaddr = nullptr;
|
||||||
if (getifaddrs(&ifaddr) != 0) {
|
if (getifaddrs(&ifaddr) != 0) {
|
||||||
@@ -135,7 +135,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0");
|
LOG_WARNING(Network, "\"/proc/net/route\" not found - using gateway 0");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
std::vector<NetworkInterface> ifaces;
|
std::vector<Network::NetworkInterface> ifaces;
|
||||||
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
|
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
|
||||||
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */
|
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr /* Have a netmask and address */
|
||||||
|| ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */
|
|| ifa->ifa_addr->sa_family != AF_INET /* Must be of kind AF_INET */
|
||||||
@@ -149,7 +149,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
});
|
});
|
||||||
in_addr gw; // Solaris defines s_addr as a macro, can't use special C++ shenanigans here
|
in_addr gw; // Solaris defines s_addr as a macro, can't use special C++ shenanigans here
|
||||||
gw.s_addr = it != routes.end() ? it->gateway : 0;
|
gw.s_addr = it != routes.end() ? it->gateway : 0;
|
||||||
ifaces.emplace_back(NetworkInterface{
|
ifaces.emplace_back(Network::NetworkInterface{
|
||||||
.name = ifa->ifa_name,
|
.name = ifa->ifa_name,
|
||||||
.ip_address = std::bit_cast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr,
|
.ip_address = std::bit_cast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr,
|
||||||
.subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr,
|
.subnet_mask = std::bit_cast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr,
|
||||||
@@ -159,7 +159,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
freeifaddrs(ifaddr);
|
freeifaddrs(ifaddr);
|
||||||
return ifaces;
|
return ifaces;
|
||||||
#elif defined(__FreeBSD__)
|
#elif defined(__FreeBSD__)
|
||||||
std::vector<NetworkInterface> ifaces;
|
std::vector<Network::NetworkInterface> ifaces;
|
||||||
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
|
int fd = ::socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
LOG_ERROR(Network, "socket: {}", std::strerror(errno));
|
LOG_ERROR(Network, "socket: {}", std::strerror(errno));
|
||||||
@@ -191,7 +191,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
|
size_t msglen = rtm->rtm_msglen - sizeof(*ifm);
|
||||||
char const* p = (char const*)(ifm + 1);
|
char const* p = (char const*)(ifm + 1);
|
||||||
|
|
||||||
NetworkInterface iface{};
|
Network::NetworkInterface iface{};
|
||||||
for (size_t i = 0; i < RTAX_MAX; i++)
|
for (size_t i = 0; i < RTAX_MAX; i++)
|
||||||
if ((ifm->ifm_addrs & (1 << i)) != 0) {
|
if ((ifm->ifm_addrs & (1 << i)) != 0) {
|
||||||
struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
|
struct sockaddr const* sa = reinterpret_cast<struct sockaddr const*>(p);
|
||||||
@@ -220,7 +220,7 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
|
|||||||
|
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
|
std::optional<Network::NetworkInterface> GetSelectedNetworkInterface() {
|
||||||
auto const& sel_if = Settings::values.network_interface.GetValue();
|
auto const& sel_if = Settings::values.network_interface.GetValue();
|
||||||
if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) {
|
if (auto const ifaces = Network::GetAvailableNetworkInterfaces(); ifaces.size() > 0) {
|
||||||
if (sel_if.empty())
|
if (sel_if.empty())
|
||||||
|
|||||||
@@ -1,281 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <chrono>
|
|
||||||
#include <mutex>
|
|
||||||
#include <thread>
|
|
||||||
#ifdef __unix__
|
|
||||||
#include <spawn.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/logging.h"
|
|
||||||
#include "core/internal_network/socket_icmp.h"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
extern char **environ;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Network {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
u16 ComputeChecksum(std::span<const u8> data) {
|
|
||||||
u32 sum = 0;
|
|
||||||
for (size_t i = 0; i < data.size(); i += 2) {
|
|
||||||
u32 value = (u32(data[i + 0]) << 8ull) | u32(data[i + 1]); //big endian
|
|
||||||
sum += value;
|
|
||||||
}
|
|
||||||
if (data.size() % 2 != 0){
|
|
||||||
sum += u16(data[data.size() - 1]) << 8;
|
|
||||||
}
|
|
||||||
while ((sum >> 16) != 0)
|
|
||||||
sum = (sum & 0xffff) + (sum >> 16);
|
|
||||||
return (~sum) & 0xffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
IcmpSocket::~IcmpSocket() {
|
|
||||||
if (fd == INVALID_SOCKET) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fd = INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno IcmpSocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
|
||||||
LOG_WARNING(Network, "(stubbed) level={},optname={},optval={}", level, optname, optval.size());
|
|
||||||
if (optname == Network::OptName::RCVTIMEO) {
|
|
||||||
if (optval.size() < sizeof(Network::Timeval))
|
|
||||||
return Errno::E_INVAL;
|
|
||||||
std::memcpy(&rcv_timeo, optval.data(), sizeof(rcv_timeo));
|
|
||||||
}
|
|
||||||
return Errno::E_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno IcmpSocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
|
|
||||||
return Errno::E_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<IcmpSocket::AcceptResult, Errno> IcmpSocket::Accept() {
|
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
|
||||||
return {AcceptResult{}, Errno::E_SUCCESS};
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno IcmpSocket::Connect(Network::SockAddrIn addr_in) {
|
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
|
||||||
connected_addr = addr_in;
|
|
||||||
return Errno::E_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetPeerName() {
|
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
|
||||||
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<Network::SockAddrIn, Errno> IcmpSocket::GetSockName() {
|
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
|
||||||
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno IcmpSocket::Bind(Network::SockAddrIn addr) {
|
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
|
||||||
return Errno::E_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno IcmpSocket::Listen(s32 backlog) {
|
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
|
||||||
return Errno::E_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno IcmpSocket::Shutdown(ShutdownHow how) {
|
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
|
||||||
return Errno::E_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<s32, Errno> IcmpSocket::Recv(int flags, std::span<u8> message) {
|
|
||||||
LOG_DEBUG(Network, "(stubbed) called");
|
|
||||||
return connected_addr.has_value()
|
|
||||||
? RecvFrom(flags, message, nullptr)
|
|
||||||
: std::make_pair(s32(0), Errno::E_NOTCONN);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<s32, Errno> IcmpSocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
|
|
||||||
LOG_DEBUG(Network, "(stubbed) called");
|
|
||||||
ASSERT(flags == 0);
|
|
||||||
ASSERT(message.size() < std::size_t((std::numeric_limits<int>::max)()));
|
|
||||||
|
|
||||||
#if !defined(__OPENORBIS__) && (defined(__FreeBSD__) || defined(__linux__))
|
|
||||||
const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
|
|
||||||
const auto timestamp = std::chrono::steady_clock::now();
|
|
||||||
while (true) {
|
|
||||||
{
|
|
||||||
std::lock_guard guard(pings_mutex);
|
|
||||||
// find ping process that is finished running
|
|
||||||
for (auto it = pings.begin(); it != pings.end();) {
|
|
||||||
pid_t result = waitpid(it->ping_pid, &it->ping_status, WNOHANG);
|
|
||||||
// ping process is still running, go to next
|
|
||||||
if (result != it->ping_pid) {
|
|
||||||
++it;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// ping process is finished, remove and handle it
|
|
||||||
it = pings.erase(it);
|
|
||||||
if (it->ping_status == 0) {
|
|
||||||
if (addr) {
|
|
||||||
addr->family = it->family;
|
|
||||||
addr->ip = it->ip;
|
|
||||||
addr->portno = it->portno;
|
|
||||||
addr->len = 16;
|
|
||||||
addr->zeroes = {};
|
|
||||||
}
|
|
||||||
std::array<u8, 8> data{
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0, //checksum
|
|
||||||
0,
|
|
||||||
it->seq_ident[0],
|
|
||||||
it->seq_ident[1],
|
|
||||||
it->seq_ident[2],
|
|
||||||
it->seq_ident[3]
|
|
||||||
};
|
|
||||||
auto const csum = ComputeChecksum(std::span<const u8>{data.begin(), data.end()});
|
|
||||||
data[2] = u8(csum >> 8); //hi
|
|
||||||
data[3] = u8(csum); //lo
|
|
||||||
auto const n = std::min(data.size(), message.size());
|
|
||||||
std::copy(data.begin(), data.begin() + n, message.begin());
|
|
||||||
return {s32(n), Errno::E_SUCCESS};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!blocking)
|
|
||||||
return {-1, Errno::E_AGAIN};
|
|
||||||
|
|
||||||
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
|
|
||||||
const auto time_diff_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
|
|
||||||
if (time_diff_ms > rcv_timeout_ms)
|
|
||||||
return {-1, Errno::E_TIMEDOUT};
|
|
||||||
|
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return {-1, Errno::E_INVAL};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<s32, Errno> IcmpSocket::Send(std::span<const u8> message, int flags) {
|
|
||||||
LOG_DEBUG(Network, "(stubbed) called");
|
|
||||||
if (connected_addr.has_value())
|
|
||||||
return SendTo(flags, message, std::addressof(connected_addr.value()));
|
|
||||||
return {s32(0), Errno::E_NOTCONN};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<s32, Errno> IcmpSocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
|
|
||||||
LOG_DEBUG(Network, "(stubbed) called");
|
|
||||||
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
|
|
||||||
// 0 -> 8 (IPv4), 128 (IPv6)
|
|
||||||
// 1 -> 0
|
|
||||||
// 2..4 -> checksum
|
|
||||||
// 4..6 -> ident
|
|
||||||
// 6..8 -> seq
|
|
||||||
|
|
||||||
// PLEASE DON'T KILL ME, I SWEAR THIS IS LEGITIMATELY THE BEST WAY TO DO IT
|
|
||||||
// IF YOU OPEN socket() GOOGLE WILL STRAIGHT UP IP BAN YOU AFTER 2 HOURS
|
|
||||||
#if !(defined(__OPENORBIS__) || defined(__ANDROID__)) && (defined(__FreeBSD__) || defined(__linux__))
|
|
||||||
const auto rcv_timeout_ms = (s64(rcv_timeo.tv_sec) * 1000) + (s64(rcv_timeo.tv_usec) / 1000);
|
|
||||||
if (!addr)
|
|
||||||
return {-1, Errno::E_DESTADDRREQ};
|
|
||||||
|
|
||||||
if (message.size() >= 8) {
|
|
||||||
std::string ip_str = fmt::format(
|
|
||||||
"{}.{}.{}.{}",
|
|
||||||
addr->ip[0],
|
|
||||||
addr->ip[1],
|
|
||||||
addr->ip[2],
|
|
||||||
addr->ip[3]
|
|
||||||
);
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
// ping -W option is a nonfractional int (milliseconds)
|
|
||||||
std::string timeout_str = fmt::format("{}", rcv_timeout_ms);
|
|
||||||
std::vector<char*> argv = {
|
|
||||||
const_cast<char*>("ping"),
|
|
||||||
const_cast<char*>("-c"),
|
|
||||||
const_cast<char*>("1"),
|
|
||||||
const_cast<char*>("-W"),
|
|
||||||
timeout_str.data(),
|
|
||||||
ip_str.data(),
|
|
||||||
nullptr
|
|
||||||
};
|
|
||||||
#elif defined(__linux__)
|
|
||||||
// ping -W option is a fractional float (seconds)
|
|
||||||
auto const rcv_timeout_s = f64(rcv_timeout_ms) / 1000.0;
|
|
||||||
std::string timeout_str = fmt::format("{}", rcv_timeout_s);
|
|
||||||
std::vector<char*> argv = {
|
|
||||||
const_cast<char*>("ping"),
|
|
||||||
const_cast<char*>("-c"),
|
|
||||||
const_cast<char*>("1"),
|
|
||||||
const_cast<char*>("-W"),
|
|
||||||
timeout_str.data(),
|
|
||||||
ip_str.data(),
|
|
||||||
nullptr
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
pid_t ping_pid;
|
|
||||||
// we should pass in attributes to stop stdout spam, but im too lazy to figure that out
|
|
||||||
if (posix_spawnp(&ping_pid, "ping", nullptr, nullptr, argv.data(), environ) != 0) {
|
|
||||||
LOG_ERROR(Network, "Unable to start ping process for emulated ICMP socket");
|
|
||||||
return {-1, Errno::E_INVAL};
|
|
||||||
}
|
|
||||||
std::lock_guard guard(pings_mutex);
|
|
||||||
if (pings.size() >= pings.max_size())
|
|
||||||
pings.erase(pings.begin());
|
|
||||||
pings.push_back(PingProcessData{
|
|
||||||
.ip = addr->ip,
|
|
||||||
.portno = addr->portno,
|
|
||||||
.ping_pid = ping_pid,
|
|
||||||
.ping_status = 0,
|
|
||||||
.seq_ident = {
|
|
||||||
message[4],
|
|
||||||
message[5],
|
|
||||||
message[6],
|
|
||||||
message[7]
|
|
||||||
},
|
|
||||||
.family = addr->family,
|
|
||||||
});
|
|
||||||
return {s32(message.size()), Errno::E_SUCCESS};
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return {-1, Errno::E_INVAL};
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno IcmpSocket::Close() {
|
|
||||||
LOG_DEBUG(Network, "called");
|
|
||||||
fd = INVALID_SOCKET;
|
|
||||||
return Errno::E_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<Errno, Errno> IcmpSocket::GetPendingError() {
|
|
||||||
LOG_DEBUG(Network, "called");
|
|
||||||
return {Errno::E_SUCCESS, Errno::E_SUCCESS};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IcmpSocket::IsOpened() const {
|
|
||||||
return fd != INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IcmpSocket::HandleProxyPacket(const ProxyPacket& packet) {
|
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IcmpSocket::GetNonBlock() {
|
|
||||||
return !blocking;
|
|
||||||
}
|
|
||||||
Errno IcmpSocket::SetNonBlock(bool enable) {
|
|
||||||
blocking = !enable;
|
|
||||||
return Errno::E_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Network
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <span>
|
|
||||||
#include <utility>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <mutex>
|
|
||||||
#include <boost/container/static_vector.hpp>
|
|
||||||
#include "core/internal_network/socket_types.h"
|
|
||||||
#include "core/internal_network/sockets.h"
|
|
||||||
|
|
||||||
namespace Network {
|
|
||||||
|
|
||||||
struct PingProcessData {
|
|
||||||
IPv4Address ip;
|
|
||||||
u16 portno;
|
|
||||||
pid_t ping_pid;
|
|
||||||
pid_t ping_status;
|
|
||||||
std::array<u8, 4> seq_ident;
|
|
||||||
u8 family;
|
|
||||||
};
|
|
||||||
|
|
||||||
class IcmpSocket : public Network::SocketBase {
|
|
||||||
public:
|
|
||||||
explicit IcmpSocket() noexcept = default;
|
|
||||||
~IcmpSocket() override;
|
|
||||||
Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
|
|
||||||
Errno Close() override;
|
|
||||||
std::pair<AcceptResult, Errno> Accept() override;
|
|
||||||
Errno Connect(Network::SockAddrIn addr_in) override;
|
|
||||||
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
|
|
||||||
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
|
|
||||||
Errno Bind(Network::SockAddrIn addr) override;
|
|
||||||
Errno Listen(s32 backlog) override;
|
|
||||||
Errno Shutdown(ShutdownHow how) override;
|
|
||||||
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
|
||||||
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
|
|
||||||
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
|
||||||
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
|
|
||||||
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
|
|
||||||
std::pair<Errno, Errno> GetPendingError() override;
|
|
||||||
bool IsOpened() const override;
|
|
||||||
void HandleProxyPacket(const ProxyPacket& packet) override;
|
|
||||||
bool GetNonBlock() override;
|
|
||||||
Errno SetNonBlock(bool enable) override;
|
|
||||||
|
|
||||||
boost::container::static_vector<PingProcessData, 128> pings;
|
|
||||||
std::optional<SockAddrIn> connected_addr;
|
|
||||||
std::mutex pings_mutex;
|
|
||||||
Network::Timeval rcv_timeo;
|
|
||||||
bool blocking = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Network
|
|
||||||
@@ -47,147 +47,169 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
|
|||||||
received_packets.push(decompressed);
|
received_packets.push(decompressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProxySocket::GetNonBlock() {
|
template <typename T>
|
||||||
return blocking;
|
Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) {
|
||||||
}
|
LOG_DEBUG(Network, "(STUBBED) called");
|
||||||
|
return Errno::SUCCESS;
|
||||||
Errno ProxySocket::SetNonBlock(bool enable) {
|
|
||||||
blocking = !enable;
|
|
||||||
return Errno::E_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
Errno ProxySocket::SetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<const u8> optval) {
|
|
||||||
LOG_DEBUG(Network, "level={},optname={},optval={}", level, optname, optval.size());
|
|
||||||
// numeric values?
|
|
||||||
if (optval.size() >= sizeof(u32)) {
|
|
||||||
u32 value;
|
|
||||||
std::memcpy(&value, optval.data(), sizeof(value));
|
|
||||||
if (optname == Network::OptName::BROADCAST)
|
|
||||||
broadcast = bool(value);
|
|
||||||
if (optname == Network::OptName::SNDTIMEO)
|
|
||||||
send_timeout = value;
|
|
||||||
if (optname == Network::OptName::RCVTIMEO)
|
|
||||||
receive_timeout = value;
|
|
||||||
}
|
|
||||||
return Errno::E_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
|
Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) {
|
||||||
protocol = socket_protocol;
|
protocol = socket_protocol;
|
||||||
return Errno::E_SUCCESS;
|
SetSockOpt(fd, SO_TYPE, type);
|
||||||
|
|
||||||
|
return Errno::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
|
std::pair<ProxySocket::AcceptResult, Errno> ProxySocket::Accept() {
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
LOG_WARNING(Network, "(STUBBED) called");
|
||||||
return {AcceptResult{}, Errno::E_SUCCESS};
|
return {AcceptResult{}, Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Connect(Network::SockAddrIn addr_in) {
|
Errno ProxySocket::Connect(SockAddrIn addr_in) {
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
LOG_WARNING(Network, "(STUBBED) called");
|
||||||
return Errno::E_SUCCESS;
|
return Errno::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetPeerName() {
|
std::pair<SockAddrIn, Errno> ProxySocket::GetPeerName() {
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
LOG_WARNING(Network, "(STUBBED) called");
|
||||||
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
return {SockAddrIn{}, Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Network::SockAddrIn, Errno> ProxySocket::GetSockName() {
|
std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() {
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
LOG_WARNING(Network, "(STUBBED) called");
|
||||||
return {Network::SockAddrIn{}, Errno::E_SUCCESS};
|
return {SockAddrIn{}, Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Bind(Network::SockAddrIn addr) {
|
Errno ProxySocket::Bind(SockAddrIn addr) {
|
||||||
if (is_bound) {
|
if (is_bound) {
|
||||||
LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
|
LOG_WARNING(Network, "Rebinding Socket is unimplemented!");
|
||||||
return Errno::E_SUCCESS;
|
return Errno::SUCCESS;
|
||||||
}
|
}
|
||||||
local_endpoint = addr;
|
local_endpoint = addr;
|
||||||
is_bound = true;
|
is_bound = true;
|
||||||
return Errno::E_SUCCESS;
|
|
||||||
|
return Errno::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Listen(s32 backlog) {
|
Errno ProxySocket::Listen(s32 backlog) {
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
LOG_WARNING(Network, "(STUBBED) called");
|
||||||
return Errno::E_SUCCESS;
|
return Errno::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Shutdown(ShutdownHow how) {
|
Errno ProxySocket::Shutdown(ShutdownHow how) {
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
LOG_WARNING(Network, "(STUBBED) called");
|
||||||
return Errno::E_SUCCESS;
|
return Errno::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) {
|
std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) {
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
LOG_WARNING(Network, "(STUBBED) called");
|
||||||
ASSERT(flags == 0 && message.size() < std::size_t((std::numeric_limits<int>::max)()));
|
ASSERT(flags == 0);
|
||||||
return {s32(0), Errno::E_SUCCESS};
|
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
|
||||||
|
|
||||||
|
return {static_cast<s32>(0), Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) {
|
std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) {
|
||||||
LOG_DEBUG(Network, "called");
|
ASSERT(flags == 0);
|
||||||
ASSERT(flags == 0 && message.size() < std::size_t((std::numeric_limits<int>::max)()));
|
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
|
||||||
do {
|
|
||||||
std::unique_lock lk{packets_mutex};
|
// TODO (flTobi): Verify the timeout behavior and break when connection is lost
|
||||||
if (received_packets.size() > 0)
|
const auto timestamp = std::chrono::steady_clock::now();
|
||||||
return ReceivePacket(flags, message, addr, message.size());
|
// When receive_timeout is set to zero, the socket is supposed to wait indefinitely until a
|
||||||
} while (blocking);
|
// packet arrives. In order to prevent lost packets from hanging the emulation thread, we set
|
||||||
return {-1, Errno::E_AGAIN};
|
// the timeout to 5s instead
|
||||||
|
const auto timeout = receive_timeout == 0 ? 5000 : receive_timeout;
|
||||||
|
while (true) {
|
||||||
|
{
|
||||||
|
std::lock_guard guard(packets_mutex);
|
||||||
|
if (received_packets.size() > 0) {
|
||||||
|
return ReceivePacket(flags, message, addr, message.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blocking) {
|
||||||
|
return {-1, Errno::AGAIN};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::yield();
|
||||||
|
|
||||||
|
const auto time_diff = std::chrono::steady_clock::now() - timestamp;
|
||||||
|
const auto time_diff_ms =
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(time_diff).count();
|
||||||
|
|
||||||
|
if (time_diff_ms > timeout) {
|
||||||
|
return {-1, Errno::TIMEDOUT};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr, std::size_t max_length) {
|
std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
|
||||||
LOG_DEBUG(Network, "called");
|
std::size_t max_length) {
|
||||||
ProxyPacket& packet = received_packets.front();
|
ProxyPacket& packet = received_packets.front();
|
||||||
if (addr) {
|
if (addr) {
|
||||||
addr->len = 16;
|
addr->family = Domain::INET;
|
||||||
addr->family = u8(Network::Domain::INET);
|
|
||||||
addr->ip = packet.local_endpoint.ip; // The senders ip address
|
addr->ip = packet.local_endpoint.ip; // The senders ip address
|
||||||
addr->portno = packet.local_endpoint.portno; // The senders port number
|
addr->portno = packet.local_endpoint.portno; // The senders port number
|
||||||
addr->zeroes = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool peek = (flags & u32(Network::MsgOpt::PEEK)) != 0;
|
bool peek = (flags & FLAG_MSG_PEEK) != 0;
|
||||||
std::size_t read_bytes = (std::min)(max_length, packet.data.size());
|
std::size_t read_bytes;
|
||||||
std::memcpy(message.data(), packet.data.data(), read_bytes);
|
if (packet.data.size() > max_length) {
|
||||||
if (!peek) {
|
read_bytes = max_length;
|
||||||
packet.data.erase(packet.data.begin(), packet.data.begin() + read_bytes);
|
memcpy(message.data(), packet.data.data(), max_length);
|
||||||
if (packet.data.empty())
|
|
||||||
|
if (protocol == Protocol::UDP) {
|
||||||
|
if (!peek) {
|
||||||
|
received_packets.pop();
|
||||||
|
}
|
||||||
|
return {-1, Errno::MSGSIZE};
|
||||||
|
} else if (protocol == Protocol::TCP) {
|
||||||
|
std::vector<u8> numArray(packet.data.size() - max_length);
|
||||||
|
std::copy(packet.data.begin() + max_length, packet.data.end(),
|
||||||
|
std::back_inserter(numArray));
|
||||||
|
packet.data = numArray;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
read_bytes = packet.data.size();
|
||||||
|
memcpy(message.data(), packet.data.data(), read_bytes);
|
||||||
|
if (!peek) {
|
||||||
received_packets.pop();
|
received_packets.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (packet.data.size() > max_length && protocol == Protocol::UDP) {
|
|
||||||
LOG_ERROR(Network, "Packet size");
|
return {static_cast<u32>(read_bytes), Errno::SUCCESS};
|
||||||
return {-1, Errno::E_MSGSIZE};
|
|
||||||
}
|
|
||||||
return {u32(read_bytes), Errno::E_SUCCESS};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
|
std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags) {
|
||||||
LOG_WARNING(Network, "(stubbed) called");
|
LOG_WARNING(Network, "(STUBBED) called");
|
||||||
ASSERT(message.size() < size_t((std::numeric_limits<int>::max)()));
|
ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)()));
|
||||||
ASSERT(flags == 0);
|
ASSERT(flags == 0);
|
||||||
return {s32(0), Errno::E_SUCCESS};
|
|
||||||
|
return {static_cast<s32>(0), Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxySocket::SendPacket(ProxyPacket& packet) {
|
void ProxySocket::SendPacket(ProxyPacket& packet) {
|
||||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||||
if (room_member->IsConnected()) {
|
if (room_member->IsConnected()) {
|
||||||
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(), packet.data.size());
|
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
|
||||||
|
packet.data.size());
|
||||||
room_member->SendProxyPacket(packet);
|
room_member->SendProxyPacket(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) {
|
std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message,
|
||||||
LOG_DEBUG(Network, "called");
|
const SockAddrIn* addr) {
|
||||||
ASSERT(flags == 0);
|
ASSERT(flags == 0);
|
||||||
|
|
||||||
if (!is_bound) {
|
if (!is_bound) {
|
||||||
LOG_ERROR(Network, "ProxySocket is not bound!");
|
LOG_ERROR(Network, "ProxySocket is not bound!");
|
||||||
return {s32(message.size()), Errno::E_SUCCESS};
|
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto room_member = Network::GetRoomMember().lock()) {
|
if (auto room_member = Network::GetRoomMember().lock()) {
|
||||||
if (!room_member->IsConnected()) {
|
if (!room_member->IsConnected()) {
|
||||||
return {s32(message.size()), Errno::E_SUCCESS};
|
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,20 +234,66 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message
|
|||||||
|
|
||||||
SendPacket(packet);
|
SendPacket(packet);
|
||||||
|
|
||||||
return {s32(message.size()), Errno::E_SUCCESS};
|
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno ProxySocket::Close() {
|
Errno ProxySocket::Close() {
|
||||||
LOG_DEBUG(Network, "called");
|
|
||||||
fd = INVALID_SOCKET;
|
fd = INVALID_SOCKET;
|
||||||
closed = true;
|
closed = true;
|
||||||
|
|
||||||
return Errno::E_SUCCESS;
|
return Errno::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno ProxySocket::SetLinger(bool enable, u32 linger) {
|
||||||
|
struct Linger {
|
||||||
|
u16 linger_enable;
|
||||||
|
u16 linger_time;
|
||||||
|
} values;
|
||||||
|
values.linger_enable = enable ? 1 : 0;
|
||||||
|
values.linger_time = static_cast<u16>(linger);
|
||||||
|
|
||||||
|
return SetSockOpt(fd, SO_LINGER, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno ProxySocket::SetReuseAddr(bool enable) {
|
||||||
|
return SetSockOpt<u32>(fd, SO_REUSEADDR, enable ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno ProxySocket::SetBroadcast(bool enable) {
|
||||||
|
broadcast = enable;
|
||||||
|
return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno ProxySocket::SetSndBuf(u32 value) {
|
||||||
|
return SetSockOpt(fd, SO_SNDBUF, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno ProxySocket::SetKeepAlive(bool enable) {
|
||||||
|
return Errno::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno ProxySocket::SetRcvBuf(u32 value) {
|
||||||
|
return SetSockOpt(fd, SO_RCVBUF, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno ProxySocket::SetSndTimeo(u32 value) {
|
||||||
|
send_timeout = value;
|
||||||
|
return SetSockOpt(fd, SO_SNDTIMEO, static_cast<int>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno ProxySocket::SetRcvTimeo(u32 value) {
|
||||||
|
receive_timeout = value;
|
||||||
|
return SetSockOpt(fd, SO_RCVTIMEO, static_cast<int>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
Errno ProxySocket::SetNonBlock(bool enable) {
|
||||||
|
blocking = !enable;
|
||||||
|
return Errno::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Errno, Errno> ProxySocket::GetPendingError() {
|
std::pair<Errno, Errno> ProxySocket::GetPendingError() {
|
||||||
LOG_DEBUG(Network, "called");
|
LOG_DEBUG(Network, "(STUBBED) called");
|
||||||
return {Errno::E_SUCCESS, Errno::E_SUCCESS};
|
return {Errno::SUCCESS, Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProxySocket::IsOpened() const {
|
bool ProxySocket::IsOpened() const {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
@@ -22,25 +22,62 @@ public:
|
|||||||
~ProxySocket() override;
|
~ProxySocket() override;
|
||||||
|
|
||||||
void HandleProxyPacket(const ProxyPacket& packet) override;
|
void HandleProxyPacket(const ProxyPacket& packet) override;
|
||||||
|
|
||||||
Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
|
Errno Initialize(Domain domain, Type type, Protocol socket_protocol) override;
|
||||||
|
|
||||||
Errno Close() override;
|
Errno Close() override;
|
||||||
|
|
||||||
std::pair<AcceptResult, Errno> Accept() override;
|
std::pair<AcceptResult, Errno> Accept() override;
|
||||||
Errno Connect(Network::SockAddrIn addr_in) override;
|
|
||||||
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
|
Errno Connect(SockAddrIn addr_in) override;
|
||||||
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
|
|
||||||
Errno Bind(Network::SockAddrIn addr) override;
|
std::pair<SockAddrIn, Errno> GetPeerName() override;
|
||||||
|
|
||||||
|
std::pair<SockAddrIn, Errno> GetSockName() override;
|
||||||
|
|
||||||
|
Errno Bind(SockAddrIn addr) override;
|
||||||
|
|
||||||
Errno Listen(s32 backlog) override;
|
Errno Listen(s32 backlog) override;
|
||||||
|
|
||||||
Errno Shutdown(ShutdownHow how) override;
|
Errno Shutdown(ShutdownHow how) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
||||||
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
|
|
||||||
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, Network::SockAddrIn* addr, std::size_t max_length);
|
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
|
||||||
|
|
||||||
|
std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr,
|
||||||
|
std::size_t max_length);
|
||||||
|
|
||||||
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
||||||
|
|
||||||
void SendPacket(ProxyPacket& packet);
|
void SendPacket(ProxyPacket& packet);
|
||||||
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
|
|
||||||
bool GetNonBlock() override;
|
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
||||||
|
const SockAddrIn* addr) override;
|
||||||
|
|
||||||
|
Errno SetLinger(bool enable, u32 linger) override;
|
||||||
|
|
||||||
|
Errno SetReuseAddr(bool enable) override;
|
||||||
|
|
||||||
|
Errno SetBroadcast(bool enable) override;
|
||||||
|
|
||||||
|
Errno SetKeepAlive(bool enable) override;
|
||||||
|
|
||||||
|
Errno SetSndBuf(u32 value) override;
|
||||||
|
|
||||||
|
Errno SetRcvBuf(u32 value) override;
|
||||||
|
|
||||||
|
Errno SetSndTimeo(u32 value) override;
|
||||||
|
|
||||||
|
Errno SetRcvTimeo(u32 value) override;
|
||||||
|
|
||||||
Errno SetNonBlock(bool enable) override;
|
Errno SetNonBlock(bool enable) override;
|
||||||
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
|
|
||||||
|
template <typename T>
|
||||||
|
Errno SetSockOpt(SOCKET fd, int option, T value);
|
||||||
|
|
||||||
std::pair<Errno, Errno> GetPendingError() override;
|
std::pair<Errno, Errno> GetPendingError() override;
|
||||||
|
|
||||||
bool IsOpened() const override;
|
bool IsOpened() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -49,7 +86,7 @@ private:
|
|||||||
u32 send_timeout = 0;
|
u32 send_timeout = 0;
|
||||||
u32 receive_timeout = 0;
|
u32 receive_timeout = 0;
|
||||||
bool is_bound = false;
|
bool is_bound = false;
|
||||||
Network::SockAddrIn local_endpoint{};
|
SockAddrIn local_endpoint{};
|
||||||
bool blocking = true;
|
bool blocking = true;
|
||||||
std::queue<ProxyPacket> received_packets;
|
std::queue<ProxyPacket> received_packets;
|
||||||
Protocol protocol;
|
Protocol protocol;
|
||||||
|
|||||||
@@ -1,470 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "common/common_funcs.h"
|
|
||||||
|
|
||||||
// Most of these structures are direct mappings of guest's
|
|
||||||
// expectations for these values, in other words, they're the
|
|
||||||
// values that HOS is expected to use AND handle.
|
|
||||||
|
|
||||||
namespace Network {
|
|
||||||
|
|
||||||
enum class Errno : u32 {
|
|
||||||
E_SUCCESS = 0,
|
|
||||||
E_PERM = 1,
|
|
||||||
E_NOENT = 2,
|
|
||||||
E_SRCH = 3,
|
|
||||||
E_INTR = 4,
|
|
||||||
E_IO = 5,
|
|
||||||
E_NXIO = 6,
|
|
||||||
E_2BIG = 7,
|
|
||||||
E_NOEXEC = 8,
|
|
||||||
E_BADF = 9,
|
|
||||||
E_CHILD = 10,
|
|
||||||
E_AGAIN = 11,
|
|
||||||
E_NOMEM = 12,
|
|
||||||
E_ACCES = 13,
|
|
||||||
E_FAULT = 14,
|
|
||||||
E_NOTBLK = 15,
|
|
||||||
E_BUSY = 16,
|
|
||||||
E_EXIST = 17,
|
|
||||||
E_XDEV = 18,
|
|
||||||
E_NODEV = 19,
|
|
||||||
E_NOTDIR = 20,
|
|
||||||
E_ISDIR = 21,
|
|
||||||
E_INVAL = 22,
|
|
||||||
E_NFILE = 23,
|
|
||||||
E_MFILE = 24,
|
|
||||||
E_NOTTY = 25,
|
|
||||||
E_TXTBSY = 26,
|
|
||||||
E_FBIG = 27,
|
|
||||||
E_NOSPC = 28,
|
|
||||||
E_SPIPE = 29,
|
|
||||||
E_ROFS = 30,
|
|
||||||
E_MLINK = 31,
|
|
||||||
E_PIPE = 32,
|
|
||||||
E_DOM = 33,
|
|
||||||
E_RANGE = 34,
|
|
||||||
E_DEADLK = 35,
|
|
||||||
E_NAMETOOLONG = 36,
|
|
||||||
E_NOLCK = 37,
|
|
||||||
E_NOSYS = 38,
|
|
||||||
E_NOTEMPTY = 39,
|
|
||||||
E_LOOP = 40,
|
|
||||||
E_NOMSG = 42,
|
|
||||||
E_IDRM = 43,
|
|
||||||
E_CHRNG = 44,
|
|
||||||
E_L2NSYNC = 45,
|
|
||||||
E_L3HLT = 46,
|
|
||||||
E_L3RST = 47,
|
|
||||||
E_LNRNG = 48,
|
|
||||||
E_UNATCH = 49,
|
|
||||||
E_NOCSI = 50,
|
|
||||||
E_L2HLT = 51,
|
|
||||||
E_BADE = 52,
|
|
||||||
E_BADR = 53,
|
|
||||||
E_XFULL = 54,
|
|
||||||
E_NOANO = 55,
|
|
||||||
E_BADRQC = 56,
|
|
||||||
E_BADSSL = 57,
|
|
||||||
E_BFONT = 59,
|
|
||||||
E_NOSTR = 60,
|
|
||||||
E_NODATA = 61,
|
|
||||||
E_TIME = 62,
|
|
||||||
E_NOSR = 63,
|
|
||||||
E_NONET = 64,
|
|
||||||
E_NOPKG = 65,
|
|
||||||
E_REMOTE = 66,
|
|
||||||
E_NOLINK = 67,
|
|
||||||
E_ADV = 68,
|
|
||||||
E_SRMNT = 69,
|
|
||||||
E_COMM = 70,
|
|
||||||
E_PROTO = 71,
|
|
||||||
E_MULTIHOP = 72,
|
|
||||||
E_DOTDOT = 73,
|
|
||||||
E_BADMSG = 74,
|
|
||||||
E_OVERFLOW = 75,
|
|
||||||
E_NOTUNUQ = 76,
|
|
||||||
E_BADFD = 77,
|
|
||||||
E_REMCHG = 78,
|
|
||||||
E_LIBACC = 79,
|
|
||||||
E_LIBBAD = 80,
|
|
||||||
E_LIBSCN = 81,
|
|
||||||
E_LIBMAX = 82,
|
|
||||||
E_LIBEXEC = 83,
|
|
||||||
E_ILSEQ = 84,
|
|
||||||
E_RESTART = 85,
|
|
||||||
E_STRPIPE = 86,
|
|
||||||
E_USERS = 87,
|
|
||||||
E_NOTSOCK = 88,
|
|
||||||
E_DESTADDRREQ = 89,
|
|
||||||
E_MSGSIZE = 90,
|
|
||||||
E_PROTOTYPE = 91,
|
|
||||||
E_NOPROTOOPT = 92,
|
|
||||||
E_PROTONOSUPPORT = 93,
|
|
||||||
E_SOCKTNOSUPPORT = 94,
|
|
||||||
E_OPNOTSUPP = 95,
|
|
||||||
E_PFNOSUPPORT = 96,
|
|
||||||
E_AFNOSUPPORT = 97,
|
|
||||||
E_ADDRINUSE = 98,
|
|
||||||
E_ADDRNOTAVAIL = 99,
|
|
||||||
E_NETDOWN = 100,
|
|
||||||
E_NETUNREACH = 101,
|
|
||||||
E_NETRESET = 102,
|
|
||||||
E_CONNABORTED = 103,
|
|
||||||
E_CONNRESET = 104,
|
|
||||||
E_NOBUFS = 105,
|
|
||||||
E_ISCONN = 106,
|
|
||||||
E_NOTCONN = 107,
|
|
||||||
E_SHUTDOWN = 108,
|
|
||||||
E_TOOMANYREFS = 109,
|
|
||||||
E_TIMEDOUT = 110,
|
|
||||||
E_CONNREFUSED = 111,
|
|
||||||
E_HOSTDOWN = 112,
|
|
||||||
E_HOSTUNREACH = 113,
|
|
||||||
E_ALREADY = 114,
|
|
||||||
E_INPROGRESS = 115,
|
|
||||||
E_STALE = 116,
|
|
||||||
E_UCLEAN = 117,
|
|
||||||
E_NOTNAM = 118,
|
|
||||||
E_NAVAIL = 119,
|
|
||||||
E_ISNAM = 120,
|
|
||||||
E_REMOTEIO = 121,
|
|
||||||
E_DQUOT = 122,
|
|
||||||
E_NOMEDIUM = 123,
|
|
||||||
E_MEDIUMTYPE = 124,
|
|
||||||
E_CANCELED = 125,
|
|
||||||
E_NOKEY = 126,
|
|
||||||
E_KEYEXPIRED = 127,
|
|
||||||
E_KEYREVOKED = 128,
|
|
||||||
E_KEYREJECTED = 129,
|
|
||||||
E_OWNERDEAD = 130,
|
|
||||||
E_NOTRECOVERABLE = 131,
|
|
||||||
E_RFKILL = 132,
|
|
||||||
E_HWPOISON = 133,
|
|
||||||
E_PROCLIM = 156,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class GetAddrInfoError : s32 {
|
|
||||||
SUCCESS = 0,
|
|
||||||
ADDRFAMILY = 1,
|
|
||||||
AGAIN = 2,
|
|
||||||
BADFLAGS = 3,
|
|
||||||
FAIL = 4,
|
|
||||||
FAMILY = 5,
|
|
||||||
MEMORY = 6,
|
|
||||||
NODATA = 7,
|
|
||||||
NONAME = 8,
|
|
||||||
SERVICE = 9,
|
|
||||||
SOCKTYPE = 10,
|
|
||||||
SYSTEM = 11,
|
|
||||||
BADHINTS = 12,
|
|
||||||
PROTOCOL = 13,
|
|
||||||
OVERFLOW_ = 14, // avoid name collision with Windows macro
|
|
||||||
OTHER = 15,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Domain : u32 {
|
|
||||||
Unspecified = 0,
|
|
||||||
UNIX = 1,
|
|
||||||
INET = 2,
|
|
||||||
IMPLINK = 3,
|
|
||||||
PUP = 4,
|
|
||||||
CHAOS = 5,
|
|
||||||
NETBIOS = 6,
|
|
||||||
ISO = 7,
|
|
||||||
ECMA = 8,
|
|
||||||
DATAKIT = 9,
|
|
||||||
CCITT = 10,
|
|
||||||
SNA = 11,
|
|
||||||
DECnet = 12,
|
|
||||||
DLI = 13,
|
|
||||||
LAT = 14,
|
|
||||||
HYLINK = 15,
|
|
||||||
APPLETALK = 16,
|
|
||||||
ROUTE = 17,
|
|
||||||
LINK = 18,
|
|
||||||
COIP = 20,
|
|
||||||
CNT = 21,
|
|
||||||
IPX = 23,
|
|
||||||
SIP = 24,
|
|
||||||
ISDN = 26,
|
|
||||||
INET6 = 28,
|
|
||||||
NATM = 29,
|
|
||||||
ATM = 30,
|
|
||||||
NETGRAPH = 32,
|
|
||||||
SLOW = 33,
|
|
||||||
SCLUSTER = 34,
|
|
||||||
ARP = 35,
|
|
||||||
BLUETOOTH = 36,
|
|
||||||
IEEE80211 = 37,
|
|
||||||
NETLINK = 38,
|
|
||||||
INET_SDP = 40,
|
|
||||||
INET6_SDP = 42,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Type : u32 {
|
|
||||||
Unspecified = 0,
|
|
||||||
STREAM = 1,
|
|
||||||
DGRAM = 2,
|
|
||||||
RAW = 3,
|
|
||||||
RDM = 4,
|
|
||||||
SEQPACKET = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Protocol : u32 {
|
|
||||||
IP = 0,
|
|
||||||
ICMP = 1,
|
|
||||||
TCP = 6,
|
|
||||||
UDP = 17,
|
|
||||||
//
|
|
||||||
IPV6 = 41,
|
|
||||||
RAW = 255,
|
|
||||||
//
|
|
||||||
HOPOPTS = 0,
|
|
||||||
IGMP = 2,
|
|
||||||
GGP = 3,
|
|
||||||
IPV4 = 4,
|
|
||||||
ST = 7,
|
|
||||||
EGP = 8,
|
|
||||||
PIGP = 9,
|
|
||||||
RCCMON = 10,
|
|
||||||
NVPII = 11,
|
|
||||||
PUP = 12,
|
|
||||||
ARGUS = 13,
|
|
||||||
EMCON = 14,
|
|
||||||
XNET = 15,
|
|
||||||
CHAOS = 16,
|
|
||||||
MUX = 18,
|
|
||||||
MEAS = 19,
|
|
||||||
HMP = 20,
|
|
||||||
PRM = 21,
|
|
||||||
IDP = 22,
|
|
||||||
TRUNK1 = 23,
|
|
||||||
TRUNK2 = 24,
|
|
||||||
LEAF1 = 25,
|
|
||||||
LEAF2 = 26,
|
|
||||||
RDP = 27,
|
|
||||||
IRTP = 28,
|
|
||||||
TP = 29,
|
|
||||||
BLT = 30,
|
|
||||||
NSP = 31,
|
|
||||||
INP = 32,
|
|
||||||
DCCP = 33,
|
|
||||||
//3PC = 34,
|
|
||||||
IDPR = 35,
|
|
||||||
XTP = 36,
|
|
||||||
DDP = 37,
|
|
||||||
CMTP = 38,
|
|
||||||
TPXX = 39,
|
|
||||||
IL = 40,
|
|
||||||
SDRP = 42,
|
|
||||||
ROUTING = 43,
|
|
||||||
FRAGMENT = 44,
|
|
||||||
IDRP = 45,
|
|
||||||
RSVP = 46,
|
|
||||||
GRE = 47,
|
|
||||||
MHRP = 48,
|
|
||||||
BHA = 49,
|
|
||||||
ESP = 50,
|
|
||||||
AH = 51,
|
|
||||||
INLSP = 52,
|
|
||||||
SWIPE = 53,
|
|
||||||
NHRP = 54,
|
|
||||||
MOBILE = 55,
|
|
||||||
TLSP = 56,
|
|
||||||
SKIP = 57,
|
|
||||||
ICMPV6 = 58,
|
|
||||||
NONE = 59,
|
|
||||||
DSTOPTS = 60,
|
|
||||||
AHIP = 61,
|
|
||||||
CFTP = 62,
|
|
||||||
HELLO = 63,
|
|
||||||
SATEXPAK = 64,
|
|
||||||
KRYPTOLAN = 65,
|
|
||||||
RVD = 66,
|
|
||||||
IPPC = 67,
|
|
||||||
ADFS = 68,
|
|
||||||
SATMON = 69,
|
|
||||||
VISA = 70,
|
|
||||||
IPCV = 71,
|
|
||||||
CPNX = 72,
|
|
||||||
CPHB = 73,
|
|
||||||
WSN = 74,
|
|
||||||
PVP = 75,
|
|
||||||
BRSATMON = 76,
|
|
||||||
ND = 77,
|
|
||||||
WBMON = 78,
|
|
||||||
WBEXPAK = 79,
|
|
||||||
EON = 80,
|
|
||||||
VMTP = 81,
|
|
||||||
SVMTP = 82,
|
|
||||||
VINES = 83,
|
|
||||||
TTP = 84,
|
|
||||||
IGP = 85,
|
|
||||||
DGP = 86,
|
|
||||||
TCF = 87,
|
|
||||||
IGRP = 88,
|
|
||||||
OSPFIGP = 89,
|
|
||||||
SRPC = 90,
|
|
||||||
LARP = 91,
|
|
||||||
MTP = 92,
|
|
||||||
AX25 = 93,
|
|
||||||
IPEIP = 94,
|
|
||||||
MICP = 95,
|
|
||||||
SCCSP = 96,
|
|
||||||
ETHERIP = 97,
|
|
||||||
ENCAP = 98,
|
|
||||||
APES = 99,
|
|
||||||
GMTP = 100,
|
|
||||||
IPCOMP = 108,
|
|
||||||
SCTP = 132,
|
|
||||||
MH = 135,
|
|
||||||
UDPLITE = 136,
|
|
||||||
HIP = 139,
|
|
||||||
SHIM6 = 140,
|
|
||||||
PIM = 103,
|
|
||||||
CARP = 112,
|
|
||||||
PGM = 113,
|
|
||||||
MPLS = 137,
|
|
||||||
PFSYNC = 240,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class SocketLevel : u32 {
|
|
||||||
IP = 0,
|
|
||||||
ICMP = 1,
|
|
||||||
TCP = 6,
|
|
||||||
UDP = 17,
|
|
||||||
CONFIG = 0xfffe,
|
|
||||||
SOCKET = 0xffff, // i.e. SOL_SOCKET
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class MsgOpt : u32 {
|
|
||||||
OOB = 0x00001,
|
|
||||||
PEEK = 0x00002,
|
|
||||||
DONTROUTE = 0x00004,
|
|
||||||
EOR_ = 0x00008,
|
|
||||||
TRUNC = 0x00010,
|
|
||||||
CTRUNC = 0x00020,
|
|
||||||
WAITALL = 0x00040,
|
|
||||||
DONTWAIT = 0x00080,
|
|
||||||
EOF_ = 0x00100,
|
|
||||||
NOSIGNAL = 0x20000,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class OptName : u32 {
|
|
||||||
DEBUG = 0x0001,
|
|
||||||
ACCEPTCONN = 0x0002,
|
|
||||||
REUSEADDR = 0x0004,
|
|
||||||
KEEPALIVE = 0x0008,
|
|
||||||
DONTROUTE = 0x0010,
|
|
||||||
BROADCAST = 0x0020,
|
|
||||||
USELOOPBACK = 0x0040,
|
|
||||||
LINGER = 0x0080,
|
|
||||||
OOBINLINE = 0x0100,
|
|
||||||
REUSEPORT = 0x0200,
|
|
||||||
TIMESTAMP = 0x0400,
|
|
||||||
NOSIGPIPE = 0x0800, // at least according to libnx
|
|
||||||
ACCEPTFILER = 0x1000,
|
|
||||||
SNDBUF = 0x1001,
|
|
||||||
RCVBUF = 0x1002,
|
|
||||||
SNDTIMEO = 0x1005,
|
|
||||||
RCVTIMEO = 0x1006,
|
|
||||||
ERROR_ = 0x1007, // avoid name collision with Windows macro
|
|
||||||
ACCEPTFILTER = 0x1000,
|
|
||||||
BINTIME = 0x2000,
|
|
||||||
NO_OFFLOAD = 0x4000,
|
|
||||||
NO_DDP = 0x8000,
|
|
||||||
};
|
|
||||||
enum class TcpOptName : u32 {
|
|
||||||
NODELAY = 0x0001,
|
|
||||||
MAXSEG = 0x0002,
|
|
||||||
NOPUSH = 0x0004,
|
|
||||||
NOOPT = 0x0008,
|
|
||||||
MS5SIG = 0x0010,
|
|
||||||
INFO = 0x0020
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ShutdownHow : s32 {
|
|
||||||
RD = 0,
|
|
||||||
WR = 1,
|
|
||||||
RDWR = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class FcntlCmd : s32 {
|
|
||||||
GETFL = 3,
|
|
||||||
SETFL = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class FcntlFlags : u32 {
|
|
||||||
NONBLOCK = 0x004,
|
|
||||||
NONBLOCK_NX = 0x800,
|
|
||||||
// Provided for convenience
|
|
||||||
NONBLOCK_ANY = u32(NONBLOCK) | u32(NONBLOCK_NX),
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Array of IPv4 address
|
|
||||||
using IPv4Address = std::array<u8, 4>;
|
|
||||||
|
|
||||||
struct SockAddrIn {
|
|
||||||
u8 len;
|
|
||||||
u8 family;
|
|
||||||
u16 portno;
|
|
||||||
IPv4Address ip;
|
|
||||||
std::array<u8, 248> zeroes;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(SockAddrIn) == 0x100);
|
|
||||||
|
|
||||||
enum class PollEvents : u16 {
|
|
||||||
// Using Pascal case because IN is a macro on Windows.
|
|
||||||
IN_ = 0x0001,
|
|
||||||
PRI_ = 0x0002,
|
|
||||||
OUT_ = 0x0004,
|
|
||||||
ERR_ = 0x0008,
|
|
||||||
HUP_ = 0x0010,
|
|
||||||
NVAL = 0x0020,
|
|
||||||
RDNORM = 0x0040,
|
|
||||||
RDBAND = 0x0080,
|
|
||||||
WRBAND = 0x0100,
|
|
||||||
IGNEOF = 0x2000,
|
|
||||||
};
|
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(PollEvents);
|
|
||||||
|
|
||||||
struct PollFD {
|
|
||||||
s32 fd;
|
|
||||||
Network::PollEvents events;
|
|
||||||
Network::PollEvents revents;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PollFD) == 8);
|
|
||||||
|
|
||||||
struct Linger {
|
|
||||||
s32 onoff;
|
|
||||||
s32 linger;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(Linger) == 8);
|
|
||||||
|
|
||||||
struct Timeval {
|
|
||||||
u64 tv_sec;
|
|
||||||
u64 tv_usec;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(Timeval) == 16);
|
|
||||||
|
|
||||||
/// @brief Cross-platform addrinfo structure (not guest)
|
|
||||||
struct AddrInfo {
|
|
||||||
Domain family;
|
|
||||||
Type socket_type;
|
|
||||||
Protocol protocol;
|
|
||||||
SockAddrIn addr;
|
|
||||||
std::optional<std::string> canon_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Network
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
|
|
||||||
struct AcceptResult {
|
struct AcceptResult {
|
||||||
std::unique_ptr<SocketBase> socket;
|
std::unique_ptr<SocketBase> socket;
|
||||||
Network::SockAddrIn sockaddr_in;
|
SockAddrIn sockaddr_in;
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketBase() = default;
|
SocketBase() = default;
|
||||||
@@ -41,29 +41,61 @@ public:
|
|||||||
YUZU_NON_MOVEABLE(SocketBase);
|
YUZU_NON_MOVEABLE(SocketBase);
|
||||||
|
|
||||||
virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0;
|
virtual Errno Initialize(Domain domain, Type type, Protocol protocol) = 0;
|
||||||
|
|
||||||
virtual Errno Close() = 0;
|
virtual Errno Close() = 0;
|
||||||
|
|
||||||
virtual std::pair<AcceptResult, Errno> Accept() = 0;
|
virtual std::pair<AcceptResult, Errno> Accept() = 0;
|
||||||
virtual Errno Connect(Network::SockAddrIn addr_in) = 0;
|
|
||||||
virtual std::pair<Network::SockAddrIn, Errno> GetPeerName() = 0;
|
virtual Errno Connect(SockAddrIn addr_in) = 0;
|
||||||
virtual std::pair<Network::SockAddrIn, Errno> GetSockName() = 0;
|
|
||||||
virtual Errno Bind(Network::SockAddrIn addr) = 0;
|
virtual std::pair<SockAddrIn, Errno> GetPeerName() = 0;
|
||||||
|
|
||||||
|
virtual std::pair<SockAddrIn, Errno> GetSockName() = 0;
|
||||||
|
|
||||||
|
virtual Errno Bind(SockAddrIn addr) = 0;
|
||||||
|
|
||||||
virtual Errno Listen(s32 backlog) = 0;
|
virtual Errno Listen(s32 backlog) = 0;
|
||||||
|
|
||||||
virtual Errno Shutdown(ShutdownHow how) = 0;
|
virtual Errno Shutdown(ShutdownHow how) = 0;
|
||||||
|
|
||||||
virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0;
|
virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0;
|
||||||
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) = 0;
|
|
||||||
|
virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) = 0;
|
||||||
|
|
||||||
virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
|
virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0;
|
||||||
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) = 0;
|
|
||||||
virtual bool GetNonBlock() = 0;
|
virtual std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
||||||
|
const SockAddrIn* addr) = 0;
|
||||||
|
|
||||||
|
virtual Errno SetLinger(bool enable, u32 linger) = 0;
|
||||||
|
|
||||||
|
virtual Errno SetReuseAddr(bool enable) = 0;
|
||||||
|
|
||||||
|
virtual Errno SetKeepAlive(bool enable) = 0;
|
||||||
|
|
||||||
|
virtual Errno SetBroadcast(bool enable) = 0;
|
||||||
|
|
||||||
|
virtual Errno SetSndBuf(u32 value) = 0;
|
||||||
|
|
||||||
|
virtual Errno SetRcvBuf(u32 value) = 0;
|
||||||
|
|
||||||
|
virtual Errno SetSndTimeo(u32 value) = 0;
|
||||||
|
|
||||||
|
virtual Errno SetRcvTimeo(u32 value) = 0;
|
||||||
|
|
||||||
virtual Errno SetNonBlock(bool enable) = 0;
|
virtual Errno SetNonBlock(bool enable) = 0;
|
||||||
virtual Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) = 0;
|
|
||||||
virtual std::pair<Errno, Errno> GetPendingError() = 0;
|
virtual std::pair<Errno, Errno> GetPendingError() = 0;
|
||||||
|
|
||||||
virtual bool IsOpened() const = 0;
|
virtual bool IsOpened() const = 0;
|
||||||
|
|
||||||
virtual void HandleProxyPacket(const ProxyPacket& packet) = 0;
|
virtual void HandleProxyPacket(const ProxyPacket& packet) = 0;
|
||||||
|
|
||||||
[[nodiscard]] SOCKET GetFD() const {
|
[[nodiscard]] SOCKET GetFD() const {
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
SOCKET fd = INVALID_SOCKET;
|
SOCKET fd = INVALID_SOCKET;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -77,30 +109,66 @@ public:
|
|||||||
Socket(Socket&& rhs) noexcept;
|
Socket(Socket&& rhs) noexcept;
|
||||||
|
|
||||||
Errno Initialize(Domain domain, Type type, Protocol protocol) override;
|
Errno Initialize(Domain domain, Type type, Protocol protocol) override;
|
||||||
|
|
||||||
Errno Close() override;
|
Errno Close() override;
|
||||||
|
|
||||||
std::pair<AcceptResult, Errno> Accept() override;
|
std::pair<AcceptResult, Errno> Accept() override;
|
||||||
Errno Connect(Network::SockAddrIn addr_in) override;
|
|
||||||
std::pair<Network::SockAddrIn, Errno> GetPeerName() override;
|
Errno Connect(SockAddrIn addr_in) override;
|
||||||
std::pair<Network::SockAddrIn, Errno> GetSockName() override;
|
|
||||||
Errno Bind(Network::SockAddrIn addr) override;
|
std::pair<SockAddrIn, Errno> GetPeerName() override;
|
||||||
|
|
||||||
|
std::pair<SockAddrIn, Errno> GetSockName() override;
|
||||||
|
|
||||||
|
Errno Bind(SockAddrIn addr) override;
|
||||||
|
|
||||||
Errno Listen(s32 backlog) override;
|
Errno Listen(s32 backlog) override;
|
||||||
|
|
||||||
Errno Shutdown(ShutdownHow how) override;
|
Errno Shutdown(ShutdownHow how) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override;
|
||||||
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, Network::SockAddrIn* addr) override;
|
|
||||||
|
std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override;
|
||||||
|
|
||||||
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override;
|
||||||
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message, const Network::SockAddrIn* addr) override;
|
|
||||||
bool GetNonBlock() override;
|
std::pair<s32, Errno> SendTo(u32 flags, std::span<const u8> message,
|
||||||
|
const SockAddrIn* addr) override;
|
||||||
|
|
||||||
|
Errno SetLinger(bool enable, u32 linger) override;
|
||||||
|
|
||||||
|
Errno SetReuseAddr(bool enable) override;
|
||||||
|
|
||||||
|
Errno SetKeepAlive(bool enable) override;
|
||||||
|
|
||||||
|
Errno SetBroadcast(bool enable) override;
|
||||||
|
|
||||||
|
Errno SetSndBuf(u32 value) override;
|
||||||
|
|
||||||
|
Errno SetRcvBuf(u32 value) override;
|
||||||
|
|
||||||
|
Errno SetSndTimeo(u32 value) override;
|
||||||
|
|
||||||
|
Errno SetRcvTimeo(u32 value) override;
|
||||||
|
|
||||||
Errno SetNonBlock(bool enable) override;
|
Errno SetNonBlock(bool enable) override;
|
||||||
Errno SetSockOpt(Network::SocketLevel level, Network::OptName option, std::span<const u8> value) override;
|
|
||||||
|
template <typename T>
|
||||||
|
Errno SetSockOpt(SOCKET fd, int option, T value);
|
||||||
|
|
||||||
std::pair<Errno, Errno> GetPendingError() override;
|
std::pair<Errno, Errno> GetPendingError() override;
|
||||||
Errno GetSockOpt(Network::SocketLevel level, Network::OptName optname, std::span<u8> value);
|
|
||||||
|
template <typename T>
|
||||||
|
std::pair<T, Errno> GetSockOpt(SOCKET fd, int option);
|
||||||
|
|
||||||
bool IsOpened() const override;
|
bool IsOpened() const override;
|
||||||
|
|
||||||
void HandleProxyPacket(const ProxyPacket& packet) override;
|
void HandleProxyPacket(const ProxyPacket& packet) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_non_blocking = false;
|
bool is_non_blocking = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::pair<s32, Errno> Poll(std::span<Network::HostPollFD> poll_fds, s32 timeout);
|
std::pair<s32, Errno> Poll(std::vector<PollFD>& poll_fds, s32 timeout);
|
||||||
|
|
||||||
} // namespace Network
|
} // namespace Network
|
||||||
|
|||||||
@@ -761,6 +761,9 @@ void EmulatedController::StartMotionCalibration() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, Common::UUID uuid) {
|
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, Common::UUID uuid) {
|
||||||
|
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||||
|
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||||
|
|
||||||
if (index >= controller.button_values.size()) {
|
if (index >= controller.button_values.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -913,10 +916,21 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
if (!is_connected) {
|
||||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
if (npad_type == NpadStyleIndex::Handheld) {
|
||||||
if (player.connected) {
|
if (npad_id_type == NpadIdType::Handheld) {
|
||||||
Connect();
|
Connect();
|
||||||
|
controller_connected[player_index] = true;
|
||||||
|
}
|
||||||
|
} else if (npad_type != NpadStyleIndex::Handheld) {
|
||||||
|
if (npad_id_type == NpadIdType::Player1) {
|
||||||
|
Connect();
|
||||||
|
controller_connected[player_index] = true;
|
||||||
|
} else if (player.connected && !controller_connected[player_index]) {
|
||||||
|
Connect();
|
||||||
|
controller_connected[player_index] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TriggerOnChange(ControllerTriggerType::Button, true);
|
TriggerOnChange(ControllerTriggerType::Button, true);
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/vector_math.h"
|
#include "common/vector_math.h"
|
||||||
#include "hid_core/frontend/motion_input.h"
|
#include "hid_core/frontend/motion_input.h"
|
||||||
|
#include "hid_core/hid_core.h"
|
||||||
#include "hid_core/hid_types.h"
|
#include "hid_core/hid_types.h"
|
||||||
#include "hid_core/irsensor/irs_types.h"
|
#include "hid_core/irsensor/irs_types.h"
|
||||||
|
|
||||||
@@ -584,6 +585,7 @@ private:
|
|||||||
std::array<VibrationValue, 2> last_vibration_value{DEFAULT_VIBRATION_VALUE,
|
std::array<VibrationValue, 2> last_vibration_value{DEFAULT_VIBRATION_VALUE,
|
||||||
DEFAULT_VIBRATION_VALUE};
|
DEFAULT_VIBRATION_VALUE};
|
||||||
std::array<std::chrono::steady_clock::time_point, 2> last_vibration_timepoint{};
|
std::array<std::chrono::steady_clock::time_point, 2> last_vibration_timepoint{};
|
||||||
|
std::array<bool, HIDCore::available_controllers> controller_connected{};
|
||||||
|
|
||||||
// Atomically synched values
|
// Atomically synched values
|
||||||
std::atomic<HID::NpadStyleIndex> npad_type{HID::NpadStyleIndex::None};
|
std::atomic<HID::NpadStyleIndex> npad_type{HID::NpadStyleIndex::None};
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/announce_multiplayer_room.h"
|
#include "common/announce_multiplayer_room.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/internal_network/socket_types.h"
|
#include "common/socket_types.h"
|
||||||
#include "network/verify_user.h"
|
#include "network/verify_user.h"
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
#include "core/internal_network/socket_types.h"
|
#include "common/socket_types.h"
|
||||||
#include "enet/enet.h"
|
#include "enet/enet.h"
|
||||||
#include "network/packet.h"
|
#include "network/packet.h"
|
||||||
#include "network/room_member.h"
|
#include "network/room_member.h"
|
||||||
@@ -358,23 +358,19 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
|
|||||||
// Parse the ProxyPacket from the packet
|
// Parse the ProxyPacket from the packet
|
||||||
u8 local_family;
|
u8 local_family;
|
||||||
packet.Read(local_family);
|
packet.Read(local_family);
|
||||||
proxy_packet.local_endpoint.len = 16;
|
proxy_packet.local_endpoint.family = static_cast<Domain>(local_family);
|
||||||
proxy_packet.local_endpoint.family = u8(Network::Domain(local_family));
|
|
||||||
packet.Read(proxy_packet.local_endpoint.ip);
|
packet.Read(proxy_packet.local_endpoint.ip);
|
||||||
packet.Read(proxy_packet.local_endpoint.portno);
|
packet.Read(proxy_packet.local_endpoint.portno);
|
||||||
proxy_packet.local_endpoint.zeroes = {};
|
|
||||||
|
|
||||||
u8 remote_family;
|
u8 remote_family;
|
||||||
packet.Read(remote_family);
|
packet.Read(remote_family);
|
||||||
proxy_packet.remote_endpoint.len = 16;
|
proxy_packet.remote_endpoint.family = static_cast<Domain>(remote_family);
|
||||||
proxy_packet.remote_endpoint.family = u8(Network::Domain(remote_family));
|
|
||||||
packet.Read(proxy_packet.remote_endpoint.ip);
|
packet.Read(proxy_packet.remote_endpoint.ip);
|
||||||
packet.Read(proxy_packet.remote_endpoint.portno);
|
packet.Read(proxy_packet.remote_endpoint.portno);
|
||||||
proxy_packet.local_endpoint.zeroes = {};
|
|
||||||
|
|
||||||
u8 protocol_type;
|
u8 protocol_type;
|
||||||
packet.Read(protocol_type);
|
packet.Read(protocol_type);
|
||||||
proxy_packet.protocol = Network::Protocol(protocol_type);
|
proxy_packet.protocol = static_cast<Protocol>(protocol_type);
|
||||||
|
|
||||||
packet.Read(proxy_packet.broadcast);
|
packet.Read(proxy_packet.broadcast);
|
||||||
packet.Read(proxy_packet.data);
|
packet.Read(proxy_packet.data);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/announce_multiplayer_room.h"
|
#include "common/announce_multiplayer_room.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/internal_network/socket_types.h"
|
#include "common/socket_types.h"
|
||||||
#include "network/room.h"
|
#include "network/room.h"
|
||||||
|
|
||||||
namespace Network {
|
namespace Network {
|
||||||
@@ -39,8 +39,8 @@ struct LDNPacket {
|
|||||||
|
|
||||||
/// Information about the received proxy packets.
|
/// Information about the received proxy packets.
|
||||||
struct ProxyPacket {
|
struct ProxyPacket {
|
||||||
Network::SockAddrIn local_endpoint;
|
SockAddrIn local_endpoint;
|
||||||
Network::SockAddrIn remote_endpoint;
|
SockAddrIn remote_endpoint;
|
||||||
Protocol protocol;
|
Protocol protocol;
|
||||||
bool broadcast;
|
bool broadcast;
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
|
|||||||
@@ -226,7 +226,9 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
|||||||
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"),
|
INSERT(Settings, dma_accuracy, tr("DMA Accuracy:"),
|
||||||
tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting."));
|
tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting."));
|
||||||
INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"),
|
INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"),
|
||||||
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nDefault follows the GPU Mode setting."));
|
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nStrict is the slowest option, but can fix issues that require stricter synchronization.\nDefault follows the GPU Accuracy setting."));
|
||||||
|
INSERT(Settings, enable_gpu_buffer_readback, tr("Enable GPU buffer readback"),
|
||||||
|
tr("Preserves GPU-modified data by reading it back before uploading.\nSome games require this to render certain effects properly."));
|
||||||
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
||||||
tr("May reduce shader stutter."));
|
tr("May reduce shader stutter."));
|
||||||
INSERT(Settings, gpu_clock, tr("GPU Clocks"),
|
INSERT(Settings, gpu_clock, tr("GPU Clocks"),
|
||||||
@@ -440,6 +442,7 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
|
|||||||
PAIR(GpuFenceBehavior, Immediate, tr("Immediate")),
|
PAIR(GpuFenceBehavior, Immediate, tr("Immediate")),
|
||||||
PAIR(GpuFenceBehavior, Balanced, tr("Balanced")),
|
PAIR(GpuFenceBehavior, Balanced, tr("Balanced")),
|
||||||
PAIR(GpuFenceBehavior, Accurate, tr("Accurate")),
|
PAIR(GpuFenceBehavior, Accurate, tr("Accurate")),
|
||||||
|
PAIR(GpuFenceBehavior, Strict, tr("Strict")),
|
||||||
}});
|
}});
|
||||||
translations->insert(
|
translations->insert(
|
||||||
{Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
|
{Settings::EnumMetadata<Settings::CpuAccuracy>::Index(),
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -15,18 +12,16 @@ TEST_CASE("Network::Errors", "[core]") {
|
|||||||
Network::Socket socks[2];
|
Network::Socket socks[2];
|
||||||
for (Network::Socket& sock : socks) {
|
for (Network::Socket& sock : socks) {
|
||||||
REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM,
|
REQUIRE(sock.Initialize(Network::Domain::INET, Network::Type::STREAM,
|
||||||
Network::Protocol::TCP) == Network::Errno::E_SUCCESS);
|
Network::Protocol::TCP) == Network::Errno::SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
Network::SockAddrIn addr{
|
Network::SockAddrIn addr{
|
||||||
16,
|
Network::Domain::INET,
|
||||||
u8(Network::Domain::INET),
|
|
||||||
1, // hopefully nobody running this test has something listening on port 1
|
|
||||||
{127, 0, 0, 1},
|
{127, 0, 0, 1},
|
||||||
{},
|
1, // hopefully nobody running this test has something listening on port 1
|
||||||
};
|
};
|
||||||
REQUIRE(socks[0].Connect(addr) == Network::Errno::E_CONNREFUSED);
|
REQUIRE(socks[0].Connect(addr) == Network::Errno::CONNREFUSED);
|
||||||
|
|
||||||
std::vector<u8> message{1, 2, 3, 4};
|
std::vector<u8> message{1, 2, 3, 4};
|
||||||
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::E_NOTCONN);
|
REQUIRE(socks[1].Recv(0, message).second == Network::Errno::NOTCONN);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,10 +249,6 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am
|
|||||||
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
|
runtime.CopyBuffer(dest_buffer, src_buffer, copies, true);
|
||||||
if (has_new_downloads) {
|
if (has_new_downloads) {
|
||||||
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
|
memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount);
|
||||||
const bool should_sync = Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate();
|
|
||||||
if (should_sync) {
|
|
||||||
runtime.Finish();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tegra::Memory::DeviceGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::UnsafeReadWrite>
|
Tegra::Memory::DeviceGuestMemoryScoped<u8, Tegra::Memory::GuestMemoryFlags::UnsafeReadWrite>
|
||||||
@@ -1234,7 +1230,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
|
|||||||
buffer.MarkUsage(offset, size);
|
buffer.MarkUsage(offset, size);
|
||||||
|
|
||||||
if (is_written) {
|
if (is_written) {
|
||||||
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size, true);
|
MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
|
if constexpr (NEEDS_BIND_STORAGE_INDEX) {
|
||||||
@@ -1520,12 +1516,10 @@ void BufferCache<P>::UpdateComputeTextureBuffers() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, bool needs_sync) {
|
void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size) {
|
||||||
if constexpr (!IS_OPENGL) {
|
if constexpr (!IS_OPENGL) {
|
||||||
if (needs_sync) {
|
Buffer& buffer = slot_buffers[buffer_id];
|
||||||
Buffer& buffer = slot_buffers[buffer_id];
|
buffer.setWriteTick(runtime.CurrentTick());
|
||||||
buffer.setWriteTick(runtime.CurrentTick());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
|
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
|
||||||
gpu_modified_ranges.Add(device_addr, size);
|
gpu_modified_ranges.Add(device_addr, size);
|
||||||
@@ -1541,11 +1535,8 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_com
|
|||||||
const BufferId buffer_id = page_table[page];
|
const BufferId buffer_id = page_table[page];
|
||||||
if (buffer_id) {
|
if (buffer_id) {
|
||||||
Buffer& buffer = slot_buffers[buffer_id];
|
Buffer& buffer = slot_buffers[buffer_id];
|
||||||
|
WaitForGpuFenceIfNeeded(buffer);
|
||||||
if (buffer.IsInBounds(device_addr, size)) {
|
if (buffer.IsInBounds(device_addr, size)) {
|
||||||
const bool should_sync = Settings::IsGPUFenceBehaviorAccurate();
|
|
||||||
if (should_sync) {
|
|
||||||
SynchronizeBufferWrites(buffer);
|
|
||||||
}
|
|
||||||
bool usable = true;
|
bool usable = true;
|
||||||
if constexpr (requires { buffer.IsSparseCompatible(); }) {
|
if constexpr (requires { buffer.IsSparseCompatible(); }) {
|
||||||
if (sparse_compatible && !buffer.IsSparseCompatible()) {
|
if (sparse_compatible && !buffer.IsSparseCompatible()) {
|
||||||
@@ -1561,11 +1552,17 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size, bool sparse_com
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::SynchronizeBufferWrites(Buffer& buffer) {
|
void BufferCache<P>::WaitForGpuFenceIfNeeded(Buffer& buffer) {
|
||||||
if constexpr (!IS_OPENGL) {
|
if constexpr (!IS_OPENGL) {
|
||||||
const u64 buffer_tick = buffer.getWriteTick();
|
const bool gpu_fence_accurate = Settings::IsGPUFenceBehaviorAccurate();
|
||||||
if (!runtime.IsFree(buffer_tick)) {
|
const bool gpu_fence_strict = Settings::IsGPUFenceBehaviorStrict();
|
||||||
runtime.Wait(buffer_tick);
|
if (gpu_fence_accurate || gpu_fence_strict) {
|
||||||
|
const u64 gpu_tick_delay = gpu_fence_strict ? 0 : 3;
|
||||||
|
const u64 buffer_tick = buffer.getWriteTick();
|
||||||
|
const u64 gpu_tick = runtime.KnownGpuTick();
|
||||||
|
if (buffer_tick > gpu_tick + gpu_tick_delay) {
|
||||||
|
runtime.Wait(buffer_tick);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1798,6 +1795,9 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
|
|||||||
if (immediate_buffer.empty()) {
|
if (immediate_buffer.empty()) {
|
||||||
immediate_buffer = ImmediateBuffer(largest_copy);
|
immediate_buffer = ImmediateBuffer(largest_copy);
|
||||||
}
|
}
|
||||||
|
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
||||||
|
DownloadBufferMemory(buffer, device_addr, copy.size);
|
||||||
|
}
|
||||||
device_memory.ReadBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
|
device_memory.ReadBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
|
||||||
upload_span = immediate_buffer.subspan(0, copy.size);
|
upload_span = immediate_buffer.subspan(0, copy.size);
|
||||||
}
|
}
|
||||||
@@ -1816,6 +1816,9 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
|
|||||||
for (BufferCopy& copy : copies) {
|
for (BufferCopy& copy : copies) {
|
||||||
u8* const src_pointer = staging_pointer.data() + copy.src_offset;
|
u8* const src_pointer = staging_pointer.data() + copy.src_offset;
|
||||||
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
||||||
|
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
||||||
|
DownloadBufferMemory(buffer, device_addr, copy.size);
|
||||||
|
}
|
||||||
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
|
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
|
||||||
// Apply the staging offset
|
// Apply the staging offset
|
||||||
copy.src_offset += upload_staging.offset;
|
copy.src_offset += upload_staging.offset;
|
||||||
|
|||||||
@@ -430,11 +430,11 @@ private:
|
|||||||
|
|
||||||
void UpdateComputeTextureBuffers();
|
void UpdateComputeTextureBuffers();
|
||||||
|
|
||||||
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size, bool needs_sync = false);
|
void MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u32 size);
|
||||||
|
|
||||||
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible);
|
[[nodiscard]] BufferId FindBuffer(DAddr device_addr, u32 size, bool sparse_compatible);
|
||||||
|
|
||||||
void SynchronizeBufferWrites(Buffer& buffer);
|
void WaitForGpuFenceIfNeeded(Buffer& buffer);
|
||||||
|
|
||||||
[[nodiscard]] OverlapResult ResolveOverlaps(DAddr device_addr, u32 wanted_size);
|
[[nodiscard]] OverlapResult ResolveOverlaps(DAddr device_addr, u32 wanted_size);
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SignalFence(std::function<void()>&& func) {
|
void SignalFence(std::function<void()>&& func) {
|
||||||
const bool delay_fence = Settings::IsGPUFenceBehaviorDefault() ? Settings::IsGPULevelHigh() : Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate();
|
const bool delay_fence = Settings::IsGPUFenceBehaviorDefault() ? Settings::IsGPULevelHigh() : Settings::IsGPUFenceBehaviorBalanced() || Settings::IsGPUFenceBehaviorAccurate() || Settings::IsGPUFenceBehaviorStrict();
|
||||||
const bool should_flush = ShouldFlush();
|
const bool should_flush = ShouldFlush();
|
||||||
if constexpr (!can_async_check) {
|
if constexpr (!can_async_check) {
|
||||||
TryReleasePendingFences<false>();
|
TryReleasePendingFences<false>();
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ void QueryCacheBase<Traits>::CounterReport(GPUVAddr addr, QueryType counter_type
|
|||||||
};
|
};
|
||||||
u8* pointer = impl->device_memory.template GetPointer<u8>(cpu_addr);
|
u8* pointer = impl->device_memory.template GetPointer<u8>(cpu_addr);
|
||||||
u8* pointer_timestamp = impl->device_memory.template GetPointer<u8>(cpu_addr + 8);
|
u8* pointer_timestamp = impl->device_memory.template GetPointer<u8>(cpu_addr + 8);
|
||||||
bool is_synced = (Settings::IsGPUFenceBehaviorDefault() ? !Settings::IsGPULevelHigh() : !Settings::IsGPUFenceBehaviorBalanced() && !Settings::IsGPUFenceBehaviorAccurate()) && is_fence;
|
bool is_synced = (Settings::IsGPUFenceBehaviorDefault() ? !Settings::IsGPULevelHigh() : !Settings::IsGPUFenceBehaviorBalanced() && !Settings::IsGPUFenceBehaviorAccurate() && !Settings::IsGPUFenceBehaviorStrict()) && is_fence;
|
||||||
std::function<void()> operation([this, is_synced, streamer, query_base = query, query_location,
|
std::function<void()> operation([this, is_synced, streamer, query_base = query, query_location,
|
||||||
pointer, pointer_timestamp] {
|
pointer, pointer_timestamp] {
|
||||||
if (True(query_base->flags & QueryFlagBits::IsInvalidated)) {
|
if (True(query_base->flags & QueryFlagBits::IsInvalidated)) {
|
||||||
|
|||||||
@@ -419,15 +419,15 @@ void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noe
|
|||||||
}
|
}
|
||||||
|
|
||||||
u64 BufferCacheRuntime::CurrentTick() {
|
u64 BufferCacheRuntime::CurrentTick() {
|
||||||
return scheduler.CurrentTick();
|
return scheduler.GetMasterSemaphore().CurrentTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BufferCacheRuntime::IsFree(u64 tick) {
|
u64 BufferCacheRuntime::KnownGpuTick() {
|
||||||
return scheduler.IsFree(tick);
|
return scheduler.GetMasterSemaphore().KnownGpuTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::Wait(u64 tick) {
|
void BufferCacheRuntime::Wait(u64 buffer_tick) {
|
||||||
scheduler.Wait(tick);
|
scheduler.Wait(buffer_tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::Finish() {
|
void BufferCacheRuntime::Finish() {
|
||||||
|
|||||||
@@ -112,9 +112,9 @@ public:
|
|||||||
|
|
||||||
u64 CurrentTick();
|
u64 CurrentTick();
|
||||||
|
|
||||||
bool IsFree(u64 tick);
|
u64 KnownGpuTick();
|
||||||
|
|
||||||
void Wait(u64 tick);
|
void Wait(u64 buffer_tick);
|
||||||
|
|
||||||
void Finish();
|
void Finish();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user