mirror of
https://github.com/vanilla-wiiu/vanilla.git
synced 2025-01-22 08:11:47 -05:00
shifted all connection functionality into pipe
This commit is contained in:
parent
ce98f9d923
commit
8dc7aef66d
21 changed files with 985 additions and 1059 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,3 +1,3 @@
|
|||
[submodule "lib/hostap"]
|
||||
path = lib/hostap
|
||||
path = pipe/linux/hostap
|
||||
url = https://github.com/rolandoislas/drc-hostap
|
||||
|
|
|
@ -11,5 +11,4 @@ SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
|||
|
||||
add_subdirectory(lib)
|
||||
add_subdirectory(pipe)
|
||||
add_subdirectory(cli)
|
||||
add_subdirectory(app)
|
||||
|
|
|
@ -62,7 +62,10 @@ void BackendViaLocalRoot::connectToConsole()
|
|||
int BackendViaLocalRoot::connectInternal(BackendViaLocalRoot *instance, const QString &intf)
|
||||
{
|
||||
QByteArray wirelessInterfaceC = intf.toUtf8();
|
||||
return vanilla_connect_to_console(wirelessInterfaceC.constData(), vanillaEventHandler, instance);
|
||||
return vanilla_start(vanillaEventHandler, instance);
|
||||
//return vanilla_connect_to_console(wirelessInterfaceC.constData(), vanillaEventHandler, instance);
|
||||
// printf("TEMPORARILY STUBBED\n");
|
||||
// return 0;
|
||||
}
|
||||
|
||||
void BackendViaLocalRoot::updateTouch(int x, int y)
|
||||
|
@ -95,7 +98,9 @@ void BackendViaLocalRoot::sync(uint16_t code)
|
|||
int BackendViaLocalRoot::syncInternal(const QString &intf, uint16_t code)
|
||||
{
|
||||
QByteArray wirelessInterfaceC = intf.toUtf8();
|
||||
return vanilla_sync_with_console(wirelessInterfaceC.constData(), code);
|
||||
//return vanilla_sync_with_console(wirelessInterfaceC.constData(), code);
|
||||
printf("TEMPORARILY STUBBED\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BackendViaLocalRoot::syncFutureCompleted()
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
add_executable(vanilla-cli
|
||||
main.c
|
||||
)
|
||||
|
||||
target_link_libraries(vanilla-cli PRIVATE
|
||||
vanilla
|
||||
)
|
||||
|
||||
target_include_directories(vanilla-cli PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/lib"
|
||||
)
|
||||
|
||||
install(TARGETS vanilla-cli)
|
104
cli/main.c
104
cli/main.c
|
@ -1,104 +0,0 @@
|
|||
/**
|
||||
* This is a simple test app that can be used to inspect base functionality.
|
||||
*
|
||||
* Valid commands are:
|
||||
* "SYNC <wireless-interface> <code>"
|
||||
* "CONNECT <wireless-interface>"
|
||||
* "EXIT"
|
||||
*
|
||||
* The sync code is a 4-digit number derived from the card suits shown by the console:
|
||||
*
|
||||
* ♠ (spade) = 0 ♥ (heart) = 1 ♦ (diamond) = 2 ♣ (clover) = 3
|
||||
*
|
||||
* Example: ♣♠♥♦ (clover, spade, heart, diamond) would equal 3012
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "status.h"
|
||||
#include "vanilla.h"
|
||||
|
||||
void console_event_handler(void *context, int type, const char *data, size_t data_size)
|
||||
{
|
||||
switch (type) {
|
||||
case VANILLA_EVENT_VIDEO:
|
||||
print_info("RECEIVED VIDEO PACKET OF SIZE %llu", data_size);
|
||||
break;
|
||||
case VANILLA_EVENT_AUDIO:
|
||||
print_info("RECEIVED AUDIO PACKET OF SIZE %llu", data_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
print_status(VANILLA_READY);
|
||||
|
||||
char *line = NULL;
|
||||
size_t size;
|
||||
|
||||
while (1) {
|
||||
ssize_t sz = getline(&line, &size, stdin);
|
||||
if (sz == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
static const int cmd_max_args = 10;
|
||||
static const int cmd_max_arg_length = 20;
|
||||
int cmd_nb_arg = 0;
|
||||
char cmd_args[cmd_max_args][cmd_max_arg_length];
|
||||
|
||||
static const char *delim = " \n";
|
||||
char *token = strtok(line, delim);
|
||||
while (token && cmd_nb_arg < cmd_max_args) {
|
||||
strncpy(cmd_args[cmd_nb_arg], token, cmd_max_arg_length);
|
||||
token = strtok(NULL, delim);
|
||||
cmd_nb_arg++;
|
||||
}
|
||||
|
||||
if (cmd_nb_arg == 0) {
|
||||
print_status(VANILLA_UNKNOWN_COMMAND);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp("SYNC", cmd_args[0])) {
|
||||
if (cmd_nb_arg != 3) {
|
||||
print_status(VANILLA_INVALID_ARGUMENT);
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *wireless_interface = cmd_args[1];
|
||||
const char *code = cmd_args[2];
|
||||
|
||||
int sync_status = vanilla_sync_with_console(wireless_interface, atoi(code));
|
||||
if (sync_status == VANILLA_SUCCESS) {
|
||||
print_status(VANILLA_SUCCESS);
|
||||
} else {
|
||||
print_status(VANILLA_ERROR);
|
||||
}
|
||||
} else if (!strcmp("CONNECT", cmd_args[0])) {
|
||||
if (cmd_nb_arg != 2) {
|
||||
print_status(VANILLA_INVALID_ARGUMENT);
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *wireless_interface = cmd_args[1];
|
||||
|
||||
int status = vanilla_connect_to_console(wireless_interface, console_event_handler, NULL);
|
||||
if (status == VANILLA_SUCCESS) {
|
||||
print_status(VANILLA_SUCCESS);
|
||||
} else {
|
||||
print_status(VANILLA_ERROR);
|
||||
}
|
||||
} else if (!strcmp("EXIT", cmd_args[0])) {
|
||||
break;
|
||||
} else {
|
||||
print_status(VANILLA_UNKNOWN_COMMAND);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -5,68 +5,16 @@ add_library(vanilla SHARED
|
|||
gamepad/input.c
|
||||
gamepad/video.c
|
||||
status.c
|
||||
sync.c
|
||||
util.c
|
||||
vanilla.c
|
||||
wpa.c
|
||||
)
|
||||
|
||||
target_include_directories(vanilla PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hostap/src/common
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hostap/src/utils
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
# Build WPA supplicant
|
||||
add_custom_target(
|
||||
wpa_supplicant_configure
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/hostap/conf/wpa_supplicant.config" "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/.config"
|
||||
)
|
||||
add_custom_target(
|
||||
wpa_supplicant_build
|
||||
COMMAND make -j$$(nproc) && cp wpa_supplicant wpa_supplicant_drc
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant"
|
||||
BYPRODUCTS "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/wpa_supplicant_drc"
|
||||
USES_TERMINAL
|
||||
DEPENDS wpa_supplicant_configure
|
||||
)
|
||||
add_executable(wpa_supplicant IMPORTED)
|
||||
add_dependencies(wpa_supplicant wpa_supplicant_build)
|
||||
set_target_properties(wpa_supplicant PROPERTIES
|
||||
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/wpa_supplicant_drc"
|
||||
)
|
||||
add_custom_command(
|
||||
TARGET wpa_supplicant_build POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/wpa_supplicant_drc" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
|
||||
DEPENDS wpa_supplicant_build
|
||||
)
|
||||
|
||||
# Build client library
|
||||
add_custom_target(
|
||||
wpa_client_build
|
||||
COMMAND make libwpa_client.so
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant"
|
||||
DEPENDS wpa_supplicant
|
||||
BYPRODUCTS "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/libwpa_client.so"
|
||||
)
|
||||
add_library(wpa_client SHARED IMPORTED)
|
||||
set_target_properties(wpa_client PROPERTIES
|
||||
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/libwpa_client.so"
|
||||
IMPORTED_NO_SONAME true
|
||||
)
|
||||
add_dependencies(wpa_client wpa_client_build)
|
||||
|
||||
# Link our library with the client library
|
||||
target_link_libraries(vanilla PRIVATE
|
||||
wpa_client
|
||||
pthread
|
||||
)
|
||||
|
||||
# Copy files into build directory
|
||||
#add_custom_command(
|
||||
# TARGET wpa_supplicant POST_BUILD
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/wpa_supplicant" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/wpa_supplicant_drc"
|
||||
#)
|
||||
|
||||
install(TARGETS vanilla)
|
||||
install(IMPORTED_RUNTIME_ARTIFACTS wpa_client wpa_supplicant)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "gamepad.h"
|
||||
#include "status.h"
|
||||
#include "vanilla.h"
|
||||
#include "util.h"
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <wpa_ctrl.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "command.h"
|
||||
|
@ -18,7 +17,6 @@
|
|||
|
||||
#include "status.h"
|
||||
#include "util.h"
|
||||
#include "wpa.h"
|
||||
|
||||
static const uint32_t STOP_CODE = 0xCAFEBABE;
|
||||
|
||||
|
@ -37,7 +35,7 @@ void send_to_console(int fd, const void *data, size_t data_size, int port)
|
|||
{
|
||||
struct sockaddr_in address;
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_addr.s_addr = inet_addr("192.168.1.10");
|
||||
address.sin_addr.s_addr = inet_addr("127.0.0.1"); // inet_addr("192.168.1.10");
|
||||
address.sin_port = htons((uint16_t) (port - 100));
|
||||
ssize_t sent = sendto(fd, data, data_size, 0, (const struct sockaddr *) &address, sizeof(address));
|
||||
if (sent == -1) {
|
||||
|
@ -97,6 +95,8 @@ int main_loop(vanilla_event_handler_t event_handler, void *context)
|
|||
pthread_create(&input_thread, NULL, listen_input, &info);
|
||||
pthread_create(&cmd_thread, NULL, listen_command, &info);
|
||||
|
||||
print_info("ready!");
|
||||
|
||||
while (1) {
|
||||
usleep(250 * 1000);
|
||||
if (is_interrupted()) {
|
||||
|
@ -134,63 +134,9 @@ exit:
|
|||
return ret;
|
||||
}
|
||||
|
||||
int connect_as_gamepad_internal(struct wpa_ctrl *ctrl, const char *wireless_interface, vanilla_event_handler_t event_handler, void *context)
|
||||
int connect_as_gamepad_internal(vanilla_event_handler_t event_handler, void *context)
|
||||
{
|
||||
while (1) {
|
||||
while (!wpa_ctrl_pending(ctrl)) {
|
||||
sleep(2);
|
||||
print_info("WAITING FOR CONNECTION");
|
||||
|
||||
if (is_interrupted()) return VANILLA_ERROR;
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
size_t actual_buf_len = sizeof(buf);
|
||||
wpa_ctrl_recv(ctrl, buf, &actual_buf_len);
|
||||
print_info("CONN RECV: %.*s", actual_buf_len, buf);
|
||||
|
||||
if (memcmp(buf, "<3>CTRL-EVENT-CONNECTED", 23) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_interrupted()) return VANILLA_ERROR;
|
||||
}
|
||||
|
||||
print_info("CONNECTED TO CONSOLE");
|
||||
|
||||
// Use DHCP on interface
|
||||
pid_t dhclient_pid;
|
||||
int r = call_dhcp(wireless_interface, &dhclient_pid);
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
print_info("FAILED TO RUN DHCP ON %s", wireless_interface);
|
||||
return r;
|
||||
} else {
|
||||
print_info("DHCP ESTABLISHED");
|
||||
}
|
||||
|
||||
{
|
||||
// Destroy default route that dhclient will have created
|
||||
pid_t ip_pid;
|
||||
const char *ip_args[] = {"ip", "route", "del", "default", "via", "192.168.1.1", "dev", wireless_interface, NULL};
|
||||
r = start_process(ip_args, &ip_pid, NULL, NULL);
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
print_info("FAILED TO REMOVE CONSOLE ROUTE FROM SYSTEM");
|
||||
}
|
||||
|
||||
int ip_status;
|
||||
waitpid(ip_pid, &ip_status, 0);
|
||||
|
||||
if (!WIFEXITED(ip_status)) {
|
||||
print_info("FAILED TO REMOVE CONSOLE ROUTE FROM SYSTEM");
|
||||
}
|
||||
}
|
||||
|
||||
r = main_loop(event_handler, context);
|
||||
|
||||
int kill_ret = kill(dhclient_pid, SIGTERM);
|
||||
print_info("killing dhclient %i: %i", dhclient_pid, kill_ret);
|
||||
|
||||
return r;
|
||||
return main_loop(event_handler, context);
|
||||
}
|
||||
|
||||
int is_stop_code(const char *data, size_t data_length)
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
#ifndef VANILLA_GAMEPAD_H
|
||||
#define VANILLA_GAMEPAD_H
|
||||
|
||||
#include "ports.h"
|
||||
// #include "ports.h"
|
||||
#include "vanilla.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static const uint16_t PORT_MSG = 50310;
|
||||
static const uint16_t PORT_VID = 50320;
|
||||
static const uint16_t PORT_AUD = 50321;
|
||||
static const uint16_t PORT_HID = 50322;
|
||||
static const uint16_t PORT_CMD = 50323;
|
||||
|
||||
struct wpa_ctrl;
|
||||
|
||||
struct gamepad_thread_context
|
||||
|
@ -18,7 +26,7 @@ struct gamepad_thread_context
|
|||
int socket_cmd;
|
||||
};
|
||||
|
||||
int connect_as_gamepad_internal(struct wpa_ctrl *ctrl, const char *wireless_interface, vanilla_event_handler_t event_handler, void *context);
|
||||
int connect_as_gamepad_internal(vanilla_event_handler_t event_handler, void *context);
|
||||
unsigned int reverse_bits(unsigned int b, int bit_count);
|
||||
void send_to_console(int fd, const void *data, size_t data_size, int port);
|
||||
int is_stop_code(const char *data, size_t data_length);
|
||||
|
|
148
lib/sync.c
148
lib/sync.c
|
@ -1,148 +0,0 @@
|
|||
#include "sync.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <wpa_ctrl.h>
|
||||
|
||||
#include "gamepad/gamepad.h"
|
||||
#include "status.h"
|
||||
#include "util.h"
|
||||
#include "wpa.h"
|
||||
|
||||
int create_connect_config(const char *input_config, const char *bssid)
|
||||
{
|
||||
FILE *in_file = fopen(input_config, "r");
|
||||
if (!in_file) {
|
||||
print_info("FAILED TO OPEN INPUT CONFIG FILE");
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
|
||||
FILE *out_file = fopen(get_wireless_connect_config_filename(), "w");
|
||||
if (!out_file) {
|
||||
print_info("FAILED TO OPEN OUTPUT CONFIG FILE");
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
|
||||
int len;
|
||||
char buf[150];
|
||||
while (len = read_line_from_file(in_file, buf, sizeof(buf))) {
|
||||
if (memcmp("\tssid=", buf, 6) == 0) {
|
||||
fprintf(out_file, "\tscan_ssid=1\n\tbssid=%s\n", bssid);
|
||||
}
|
||||
|
||||
fwrite(buf, len, 1, out_file);
|
||||
|
||||
if (memcmp(buf, "update_config=1", 15) == 0) {
|
||||
static const char *ap_scan_line = "ap_scan=1\n";
|
||||
fwrite(ap_scan_line, strlen(ap_scan_line), 1, out_file);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(in_file);
|
||||
fclose(out_file);
|
||||
|
||||
return VANILLA_SUCCESS;
|
||||
}
|
||||
|
||||
int sync_with_console_internal(struct wpa_ctrl *ctrl, uint16_t code)
|
||||
{
|
||||
char buf[16384];
|
||||
const size_t buf_len = sizeof(buf);
|
||||
|
||||
int found_console = 0;
|
||||
char bssid[18];
|
||||
do {
|
||||
size_t actual_buf_len;
|
||||
|
||||
if (is_interrupted()) goto exit_loop;
|
||||
|
||||
// Request scan from hardware
|
||||
while (1) {
|
||||
if (is_interrupted()) goto exit_loop;
|
||||
|
||||
// print_info("SCANNING");
|
||||
actual_buf_len = buf_len;
|
||||
wpa_ctrl_command(ctrl, "SCAN", buf, &actual_buf_len);
|
||||
|
||||
if (!memcmp(buf, "FAIL-BUSY", 9)) {
|
||||
//print_info("DEVICE BUSY, RETRYING");
|
||||
sleep(5);
|
||||
} else if (!memcmp(buf, "OK", 2)) {
|
||||
break;
|
||||
} else {
|
||||
print_info("UNKNOWN SCAN RESPONSE: %.*s (RETRYING)", actual_buf_len, buf);
|
||||
sleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
//print_info("WAITING FOR SCAN RESULTS");
|
||||
actual_buf_len = buf_len;
|
||||
wpa_ctrl_command(ctrl, "SCAN_RESULTS", buf, &actual_buf_len);
|
||||
print_info("RECEIVED SCAN RESULTS");
|
||||
|
||||
const char *line = strtok(buf, "\n");
|
||||
while (line) {
|
||||
if (is_interrupted()) goto exit_loop;
|
||||
|
||||
if (strstr(line, "WiiU")) {
|
||||
print_info("FOUND WII U, TESTING WPS PIN");
|
||||
|
||||
// Make copy of bssid for later
|
||||
strncpy(bssid, line, sizeof(bssid));
|
||||
bssid[17] = '\0';
|
||||
|
||||
char wps_buf[100];
|
||||
snprintf(wps_buf, sizeof(wps_buf), "WPS_PIN %.*s %04d5678", 17, bssid, code);
|
||||
|
||||
size_t actual_buf_len = buf_len;
|
||||
wpa_ctrl_command(ctrl, wps_buf, buf, &actual_buf_len);
|
||||
|
||||
static const int max_wait = 20;
|
||||
int wait_count = 0;
|
||||
int cred_received = 0;
|
||||
|
||||
while (!is_interrupted()) {
|
||||
while (wait_count < max_wait && !wpa_ctrl_pending(ctrl)) {
|
||||
if (is_interrupted()) goto exit_loop;
|
||||
sleep(1);
|
||||
wait_count++;
|
||||
}
|
||||
|
||||
if (wait_count == max_wait) {
|
||||
print_info("GIVING UP, RETURNING TO SCANNING");
|
||||
break;
|
||||
}
|
||||
|
||||
actual_buf_len = buf_len;
|
||||
wpa_ctrl_recv(ctrl, buf, &actual_buf_len);
|
||||
print_info("CRED RECV: %.*s", buf_len, buf);
|
||||
|
||||
if (!memcmp("<3>WPS-CRED-RECEIVED", buf, 20)) {
|
||||
print_info("RECEIVED AUTHENTICATION FROM CONSOLE");
|
||||
cred_received = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cred_received) {
|
||||
// Tell wpa_supplicant to save config
|
||||
actual_buf_len = buf_len;
|
||||
print_info("SAVING CONFIG", actual_buf_len, buf);
|
||||
wpa_ctrl_command(ctrl, "SAVE_CONFIG", buf, &actual_buf_len);
|
||||
|
||||
// Create connect config which needs a couple more parameters
|
||||
create_connect_config(get_wireless_authenticate_config_filename(), bssid);
|
||||
|
||||
found_console = 1;
|
||||
}
|
||||
}
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
} while (!found_console);
|
||||
|
||||
exit_loop:
|
||||
return found_console ? VANILLA_SUCCESS : VANILLA_ERROR;
|
||||
}
|
10
lib/sync.h
10
lib/sync.h
|
@ -1,10 +0,0 @@
|
|||
#ifndef VANILLA_SYNC_H
|
||||
#define VANILLA_SYNC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct wpa_ctrl;
|
||||
|
||||
int sync_with_console_internal(struct wpa_ctrl *ctrl, uint16_t code);
|
||||
|
||||
#endif // VANILLA_SYNC_H
|
134
lib/util.c
134
lib/util.c
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
@ -10,73 +9,8 @@
|
|||
#include "vanilla.h"
|
||||
|
||||
// TODO: Static variables are undesirable
|
||||
char wireless_authenticate_config_filename[1024] = {0};
|
||||
char wireless_connect_config_filename[1024] = {0};
|
||||
int interrupted = 0;
|
||||
|
||||
const char *get_wireless_connect_config_filename()
|
||||
{
|
||||
if (wireless_connect_config_filename[0] == 0) {
|
||||
// Not initialized yet, do this now
|
||||
get_home_directory_file("vanilla_wpa_connect.conf", wireless_connect_config_filename, sizeof(wireless_connect_config_filename));
|
||||
}
|
||||
return wireless_connect_config_filename;
|
||||
}
|
||||
|
||||
const char *get_wireless_authenticate_config_filename()
|
||||
{
|
||||
if (wireless_authenticate_config_filename[0] == 0) {
|
||||
// Not initialized yet, do this now
|
||||
get_home_directory_file("vanilla_wpa_key.conf", wireless_authenticate_config_filename, sizeof(wireless_authenticate_config_filename));
|
||||
}
|
||||
return wireless_authenticate_config_filename;
|
||||
}
|
||||
|
||||
size_t read_line_from_fd(int pipe, char *output, size_t max_output_size)
|
||||
{
|
||||
size_t i = 0;
|
||||
while (i < max_output_size && read(pipe, output, 1) > 0) {
|
||||
int newline = (*output == '\n');
|
||||
output++;
|
||||
i++;
|
||||
if (newline) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*output = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
size_t read_line_from_file(FILE *file, char *output, size_t max_output_size)
|
||||
{
|
||||
return read_line_from_fd(fileno(file), output, max_output_size);
|
||||
}
|
||||
|
||||
size_t get_home_directory(char *buf, size_t buf_size)
|
||||
{
|
||||
size_t ret = snprintf(buf, buf_size, "%s/%s", getenv("HOME"), ".vanilla");
|
||||
if (ret <= buf_size) {
|
||||
mkdir(buf, 0755);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t get_home_directory_file(const char *filename, char *buf, size_t buf_size)
|
||||
{
|
||||
size_t max_path_length = get_max_path_length();
|
||||
char *dir = malloc(max_path_length);
|
||||
get_home_directory(dir, max_path_length);
|
||||
|
||||
size_t ret = snprintf(buf, buf_size, "%s/%s", dir, filename);
|
||||
free(dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t get_max_path_length()
|
||||
{
|
||||
return pathconf(".", _PC_PATH_MAX);
|
||||
}
|
||||
|
||||
void interrupt_handler(int signum)
|
||||
{
|
||||
print_info("INTERRUPT SIGNAL RECEIVED, CANCELLING...");
|
||||
|
@ -109,74 +43,6 @@ void uninstall_interrupt_handler()
|
|||
signal(SIGINT, SIG_DFL);
|
||||
}
|
||||
|
||||
int start_process(const char **argv, pid_t *pid_out, int *stdout_pipe, int *stderr_pipe)
|
||||
{
|
||||
// Set up pipes so child stdout can be read by the parent process
|
||||
int out_pipes[2], err_pipes[2];
|
||||
pipe(out_pipes);
|
||||
pipe(err_pipes);
|
||||
|
||||
// Get parent pid (allows us to check if parent was terminated immediately after fork)
|
||||
pid_t ppid_before_fork = getpid();
|
||||
|
||||
// Fork into parent/child processes
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
// We are in the child. Set child to terminate when parent does.
|
||||
int r = prctl(PR_SET_PDEATHSIG, SIGHUP);
|
||||
if (r == -1) {
|
||||
perror(0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// See if parent pid is still the same. If not, it must have been terminated, so we will exit too.
|
||||
if (getppid() != ppid_before_fork) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set up pipes so our stdout can be read by the parent process
|
||||
dup2(out_pipes[1], STDOUT_FILENO);
|
||||
dup2(err_pipes[1], STDERR_FILENO);
|
||||
close(out_pipes[0]);
|
||||
close(out_pipes[1]);
|
||||
close(err_pipes[0]);
|
||||
close(err_pipes[1]);
|
||||
|
||||
// Execute process (this will replace the running code)
|
||||
r = execvp(argv[0], (char * const *) argv);
|
||||
|
||||
// Handle failure to execute, use _exit so we don't interfere with the host
|
||||
_exit(1);
|
||||
} else if (pid < 0) {
|
||||
// Fork error
|
||||
return VANILLA_ERROR;
|
||||
} else {
|
||||
// Continuation of parent
|
||||
close(out_pipes[1]);
|
||||
close(err_pipes[1]);
|
||||
if (!stdout_pipe) {
|
||||
// Caller is not interested in the stdout
|
||||
close(out_pipes[0]);
|
||||
} else {
|
||||
// Caller is interested so we'll hand them the pipe
|
||||
*stdout_pipe = out_pipes[0];
|
||||
}
|
||||
if (!stderr_pipe) {
|
||||
close(err_pipes[0]);
|
||||
} else {
|
||||
*stderr_pipe = err_pipes[0];
|
||||
}
|
||||
|
||||
// If caller wants the pid, send it to them
|
||||
if (pid_out) {
|
||||
*pid_out = pid;
|
||||
}
|
||||
|
||||
return VANILLA_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t crc16(const void *data, size_t len)
|
||||
{
|
||||
const uint8_t *src = data;
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
|
||||
size_t read_line_from_fd(int fd, char *output, size_t max_output_size);
|
||||
size_t read_line_from_file(FILE *file, char *output, size_t max_output_size);
|
||||
size_t get_home_directory(char *buf, size_t buf_size);
|
||||
size_t get_home_directory_file(const char *filename, char *buf, size_t buf_size);
|
||||
size_t get_max_path_length();
|
||||
|
||||
void clear_interrupt();
|
||||
|
@ -22,11 +20,6 @@ void force_interrupt();
|
|||
void install_interrupt_handler();
|
||||
void uninstall_interrupt_handler();
|
||||
|
||||
int start_process(const char **argv, pid_t *pid_out, int *stdout_pipe, int *stderr_pipe);
|
||||
|
||||
const char *get_wireless_authenticate_config_filename();
|
||||
const char *get_wireless_connect_config_filename();
|
||||
|
||||
uint16_t crc16(const void* data, size_t len);
|
||||
|
||||
#endif // VANILLA_UTIL_H
|
|
@ -3,74 +3,18 @@
|
|||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <wpa_ctrl.h>
|
||||
|
||||
#include "gamepad/command.h"
|
||||
#include "gamepad/gamepad.h"
|
||||
#include "gamepad/input.h"
|
||||
#include "gamepad/video.h"
|
||||
#include "status.h"
|
||||
#include "sync.h"
|
||||
#include "util.h"
|
||||
#include "vanilla.h"
|
||||
#include "wpa.h"
|
||||
|
||||
struct sync_args {
|
||||
uint16_t code;
|
||||
};
|
||||
|
||||
struct connect_args {
|
||||
const char *wireless_interface;
|
||||
vanilla_event_handler_t event_handler;
|
||||
void *event_handler_context;
|
||||
};
|
||||
|
||||
int thunk_to_sync(struct wpa_ctrl *ctrl, void *data)
|
||||
int vanilla_start(vanilla_event_handler_t event_handler, void *context)
|
||||
{
|
||||
struct sync_args *args = (struct sync_args *) data;
|
||||
return sync_with_console_internal(ctrl, args->code);
|
||||
}
|
||||
|
||||
int thunk_to_connect(struct wpa_ctrl *ctrl, void *data)
|
||||
{
|
||||
struct connect_args *args = (struct connect_args *) data;
|
||||
return connect_as_gamepad_internal(ctrl, args->wireless_interface, args->event_handler, args->event_handler_context);
|
||||
}
|
||||
|
||||
int vanilla_sync_with_console(const char *wireless_interface, uint16_t code)
|
||||
{
|
||||
const char *wireless_conf_file;
|
||||
|
||||
FILE *config;
|
||||
wireless_conf_file = get_wireless_authenticate_config_filename();
|
||||
config = fopen(wireless_conf_file, "w");
|
||||
if (!config) {
|
||||
print_info("FAILED TO WRITE TEMP CONFIG: %s", wireless_conf_file);
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
|
||||
fprintf(config, "ctrl_interface=%s\nupdate_config=1\n", wpa_ctrl_interface);
|
||||
fclose(config);
|
||||
|
||||
struct sync_args args;
|
||||
args.code = code;
|
||||
|
||||
return wpa_setup_environment(wireless_interface, wireless_conf_file, thunk_to_sync, &args);
|
||||
}
|
||||
|
||||
int vanilla_connect_to_console(const char *wireless_interface, vanilla_event_handler_t event_handler, void *context)
|
||||
{
|
||||
struct connect_args args;
|
||||
args.wireless_interface = wireless_interface;
|
||||
args.event_handler = event_handler;
|
||||
args.event_handler_context = context;
|
||||
|
||||
return wpa_setup_environment(wireless_interface, get_wireless_connect_config_filename(), thunk_to_connect, &args);
|
||||
}
|
||||
|
||||
int vanilla_has_config()
|
||||
{
|
||||
return (access(get_wireless_connect_config_filename(), F_OK) == 0);
|
||||
return connect_as_gamepad_internal(event_handler, context);
|
||||
}
|
||||
|
||||
void vanilla_stop()
|
||||
|
|
|
@ -91,28 +91,14 @@ enum VanillaBatteryStatus {
|
|||
typedef void (*vanilla_event_handler_t)(void *context, int event_type, const char *data, size_t data_size);
|
||||
|
||||
/**
|
||||
* Attempt to sync with the console
|
||||
*
|
||||
* This will block until the task is over or vanilla_stop() is called from another thread.
|
||||
* Start listening for gamepad commands
|
||||
*/
|
||||
int vanilla_sync_with_console(const char *wireless_interface, uint16_t code);
|
||||
|
||||
/**
|
||||
* Attempt gameplay connection with console
|
||||
*
|
||||
* This will block until the task is over or vanilla_stop() is called from another thread.
|
||||
*/
|
||||
int vanilla_connect_to_console(const char *wireless_interface, vanilla_event_handler_t event_handler, void *context);
|
||||
|
||||
/**
|
||||
* Determine if we have a configuration file that we can connect with.
|
||||
*/
|
||||
int vanilla_has_config();
|
||||
int vanilla_start(vanilla_event_handler_t event_handler, void *context);
|
||||
|
||||
/**
|
||||
* Attempt to stop the current action
|
||||
*
|
||||
* This can be called from another thread to safely exit a blocking call to vanilla_sync_with_console() or vanilla_connect_to_console().
|
||||
* This can be called from another thread to safely exit a blocking call to vanilla_start().
|
||||
*/
|
||||
void vanilla_stop();
|
||||
|
||||
|
|
338
lib/wpa.c
338
lib/wpa.c
|
@ -1,338 +0,0 @@
|
|||
#include "wpa.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <wpa_ctrl.h>
|
||||
|
||||
#include "status.h"
|
||||
#include "util.h"
|
||||
#include "vanilla.h"
|
||||
|
||||
const char *wpa_ctrl_interface = "/var/run/wpa_supplicant_drc";
|
||||
|
||||
void wpa_msg(char *msg, size_t len)
|
||||
{
|
||||
print_info("%.*s", len, msg);
|
||||
}
|
||||
|
||||
void wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, char *buf, size_t *buf_len)
|
||||
{
|
||||
wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, buf_len, NULL /*wpa_msg*/);
|
||||
}
|
||||
|
||||
int get_binary_in_working_directory(const char *bin_name, char *buf, size_t buf_size)
|
||||
{
|
||||
size_t path_size = get_max_path_length();
|
||||
char *path_buf = malloc(path_size);
|
||||
if (!path_buf) {
|
||||
// Failed to allocate buffer, terminate
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get current working directory
|
||||
// TODO: This is Linux only and will require changes on other platforms
|
||||
ssize_t link_len = readlink("/proc/self/exe", path_buf, path_size);
|
||||
if (link_len < 0) {
|
||||
print_info("READLINK ERROR: %i", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Merge current working directory with wpa_supplicant name
|
||||
path_buf[link_len] = 0;
|
||||
dirname(path_buf);
|
||||
int r = snprintf(buf, path_size, "%s/%s", path_buf, bin_name);
|
||||
free(path_buf);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
ssize_t read_line_from_pipe(int pipe, char *buf, size_t buf_len)
|
||||
{
|
||||
int attempts = 0;
|
||||
const static int max_attempts = 5;
|
||||
ssize_t read_count = 0;
|
||||
while (read_count < buf_len) {
|
||||
ssize_t this_read = read(pipe, buf + read_count, 1);
|
||||
if (this_read == 0) {
|
||||
attempts++;
|
||||
if (is_interrupted() || attempts == max_attempts) {
|
||||
return -1;
|
||||
}
|
||||
sleep(1); // Wait for more output
|
||||
continue;
|
||||
}
|
||||
|
||||
attempts = 0;
|
||||
|
||||
if (buf[read_count] == '\n') {
|
||||
buf[read_count] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
read_count++;
|
||||
}
|
||||
return read_count;
|
||||
}
|
||||
|
||||
int wait_for_output(int pipe, const char *expected_output)
|
||||
{
|
||||
static const int max_attempts = 5;
|
||||
int nbytes, attempts = 0, success = 0;
|
||||
const int expected_len = strlen(expected_output);
|
||||
char buf[100];
|
||||
int read_count = 0;
|
||||
int ret = 0;
|
||||
do {
|
||||
// Read line from child process
|
||||
read_line_from_pipe(pipe, buf, sizeof(buf));
|
||||
|
||||
print_info("SUBPROCESS %s", buf);
|
||||
|
||||
// We got success message!
|
||||
if (!memcmp(buf, expected_output, expected_len)) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Haven't gotten success message (yet), wait and try again
|
||||
} while (attempts < max_attempts && !is_interrupted());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int start_wpa_supplicant(const char *wireless_interface, const char *config_file, pid_t *pid)
|
||||
{
|
||||
// TODO: drc-sim has `rfkill unblock wlan`, should we do that too?
|
||||
|
||||
// Kill any potentially orphaned wpa_supplicant_drcs
|
||||
const char *wpa_supplicant_drc = "wpa_supplicant_drc";
|
||||
const char *kill_argv[] = {"killall", "-9", wpa_supplicant_drc, NULL};
|
||||
pid_t kill_pid;
|
||||
int kill_pipe;
|
||||
int r = start_process(kill_argv, &kill_pid, &kill_pipe, NULL);
|
||||
int status;
|
||||
waitpid(kill_pid, &status, 0);
|
||||
|
||||
size_t path_size = get_max_path_length();
|
||||
char *wpa_buf = malloc(path_size);
|
||||
|
||||
get_binary_in_working_directory(wpa_supplicant_drc, wpa_buf, path_size);
|
||||
|
||||
const char *argv[] = {wpa_buf, "-Dnl80211", "-i", wireless_interface, "-c", config_file, NULL};
|
||||
print_info("USING WPA CONFIG: %s", config_file);
|
||||
int pipe;
|
||||
|
||||
r = start_process(argv, pid, &pipe, NULL);
|
||||
free(wpa_buf);
|
||||
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
return r;
|
||||
}
|
||||
|
||||
// Wait for WPA supplicant to start
|
||||
if (wait_for_output(pipe, "Successfully initialized wpa_supplicant")) {
|
||||
// I'm not sure why, but closing this pipe breaks wpa_supplicant in subtle ways, so just leave it.
|
||||
//close(pipe);
|
||||
|
||||
// WPA initialized correctly! Continue with action...
|
||||
return VANILLA_SUCCESS;
|
||||
} else {
|
||||
// Give up
|
||||
give_up:
|
||||
kill((*pid), SIGTERM);
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
int wpa_setup_environment(const char *wireless_interface, const char *wireless_conf_file, ready_callback_t callback, void *callback_data)
|
||||
{
|
||||
int ret = VANILLA_ERROR;
|
||||
|
||||
clear_interrupt();
|
||||
//install_interrupt_handler();
|
||||
|
||||
// Check status of interface with NetworkManager
|
||||
int is_managed = 0;
|
||||
if (is_networkmanager_managing_device(wireless_interface, &is_managed) != VANILLA_SUCCESS) {
|
||||
print_info("FAILED TO DETERMINE MANAGED STATE OF WIRELESS INTERFACE");
|
||||
//goto die;
|
||||
}
|
||||
|
||||
// If NetworkManager is managing this device, temporarily stop it from doing so
|
||||
if (is_managed) {
|
||||
if (disable_networkmanager_on_device(wireless_interface) != VANILLA_SUCCESS) {
|
||||
print_info("FAILED TO SET %s TO UNMANAGED, RESULTS MAY BE UNPREDICTABLE");
|
||||
} else {
|
||||
print_info("TEMPORARILY SET %s TO UNMANAGED", wireless_interface);
|
||||
}
|
||||
}
|
||||
|
||||
// Start modified WPA supplicant
|
||||
pid_t pid;
|
||||
int err = start_wpa_supplicant(wireless_interface, wireless_conf_file, &pid);
|
||||
if (err != VANILLA_SUCCESS || is_interrupted()) {
|
||||
print_info("FAILED TO START WPA SUPPLICANT");
|
||||
goto die_and_reenable_managed;
|
||||
}
|
||||
|
||||
// Get control interface
|
||||
const size_t buf_len = 1048576;
|
||||
char *buf = malloc(buf_len);
|
||||
snprintf(buf, buf_len, "%s/%s", wpa_ctrl_interface, wireless_interface);
|
||||
struct wpa_ctrl *ctrl;
|
||||
while (!(ctrl = wpa_ctrl_open(buf))) {
|
||||
if (is_interrupted()) goto die_and_kill;
|
||||
print_info("WAITING FOR CTRL INTERFACE");
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (is_interrupted() || wpa_ctrl_attach(ctrl) < 0) {
|
||||
print_info("FAILED TO ATTACH TO WPA");
|
||||
goto die_and_close;
|
||||
}
|
||||
|
||||
ret = callback(ctrl, callback_data);
|
||||
|
||||
die_and_detach:
|
||||
wpa_ctrl_detach(ctrl);
|
||||
|
||||
die_and_close:
|
||||
wpa_ctrl_close(ctrl);
|
||||
|
||||
die_and_kill:
|
||||
kill(pid, SIGTERM);
|
||||
|
||||
free(buf);
|
||||
|
||||
die_and_reenable_managed:
|
||||
if (is_managed) {
|
||||
print_info("SETTING %s BACK TO MANAGED", wireless_interface);
|
||||
enable_networkmanager_on_device(wireless_interface);
|
||||
}
|
||||
|
||||
die:
|
||||
// Remove our custom sigint signal handler
|
||||
//uninstall_interrupt_handler();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int call_dhcp(const char *network_interface, pid_t *dhclient_pid)
|
||||
{
|
||||
const char *argv[] = {"dhclient", "-d", "--no-pid", network_interface, NULL, NULL, NULL};
|
||||
|
||||
size_t buf_size = get_max_path_length();
|
||||
char *dhclient_buf = malloc(buf_size);
|
||||
char *dhclient_script = NULL;
|
||||
get_binary_in_working_directory("dhclient", dhclient_buf, buf_size);
|
||||
|
||||
if (access(dhclient_buf, F_OK) == 0) {
|
||||
// HACK: Assume we're working in our deployed environment
|
||||
// TODO: Should probably just incorporate dhclient (or something like it) directly as a library
|
||||
argv[0] = dhclient_buf;
|
||||
argv[4] = "-sf";
|
||||
|
||||
dhclient_script = malloc(buf_size);
|
||||
get_binary_in_working_directory("../sbin/dhclient-script", dhclient_script, buf_size);
|
||||
argv[5] = dhclient_script;
|
||||
|
||||
print_info("Using custom dhclient at: %s", argv[0]);
|
||||
} else {
|
||||
print_info("Using system dhclient");
|
||||
}
|
||||
|
||||
int dhclient_pipe;
|
||||
int r = start_process(argv, dhclient_pid, NULL, &dhclient_pipe);
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
print_info("FAILED TO CALL DHCLIENT");
|
||||
return r;
|
||||
}
|
||||
|
||||
free(dhclient_buf);
|
||||
if (dhclient_script) free(dhclient_script);
|
||||
|
||||
if (wait_for_output(dhclient_pipe, "bound to")) {
|
||||
return VANILLA_SUCCESS;
|
||||
} else {
|
||||
print_info("FAILED TO ESTABLISH DHCP");
|
||||
kill(*dhclient_pid, SIGTERM);
|
||||
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *nmcli = "nmcli";
|
||||
int is_networkmanager_managing_device(const char *wireless_interface, int *is_managed)
|
||||
{
|
||||
pid_t nmcli_pid;
|
||||
int pipe;
|
||||
|
||||
const char *argv[] = {nmcli, "device", "show", wireless_interface, NULL};
|
||||
|
||||
int r = start_process(argv, &nmcli_pid, &pipe, NULL);
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
// Assume nmcli is not installed so the host is not using NetworkManager
|
||||
print_info("FAILED TO LAUNCH NMCLI, RESULTS MAY BE UNPREDICTABLE");
|
||||
*is_managed = 0;
|
||||
return VANILLA_SUCCESS;
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(nmcli_pid, &status, 0);
|
||||
|
||||
if (!WIFEXITED(status)) {
|
||||
// Something went wrong
|
||||
print_info("NMCLI DID NOT EXIT NORMALLY");
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
|
||||
char buf[100];
|
||||
int ret = VANILLA_ERROR;
|
||||
while (read_line_from_fd(pipe, buf, sizeof(buf))) {
|
||||
if (memcmp(buf, "GENERAL.STATE", 13) == 0) {
|
||||
*is_managed = !strstr(buf, "unmanaged");
|
||||
ret = VANILLA_SUCCESS;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
close(pipe);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int set_networkmanager_on_device(const char *wireless_interface, int on)
|
||||
{
|
||||
const char *argv[] = {nmcli, "device", "set", wireless_interface, "managed", on ? "on" : "off", NULL};
|
||||
|
||||
pid_t nmcli_pid;
|
||||
int r = start_process(argv, &nmcli_pid, NULL, NULL);
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(nmcli_pid, &status, 0);
|
||||
if (WIFEXITED(status)) {
|
||||
return VANILLA_SUCCESS;
|
||||
} else {
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
int disable_networkmanager_on_device(const char *wireless_interface)
|
||||
{
|
||||
return set_networkmanager_on_device(wireless_interface, 0);
|
||||
}
|
||||
|
||||
int enable_networkmanager_on_device(const char *wireless_interface)
|
||||
{
|
||||
return set_networkmanager_on_device(wireless_interface, 1);
|
||||
}
|
|
@ -1,5 +1,66 @@
|
|||
add_executable(vanilla-pipe
|
||||
main.c
|
||||
wpa.c
|
||||
)
|
||||
|
||||
install(TARGETS vanilla-pipe)
|
||||
|
||||
target_include_directories(vanilla-pipe PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hostap/src/common
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hostap/src/utils
|
||||
${CMAKE_SOURCE_DIR}/lib
|
||||
)
|
||||
|
||||
# Build WPA supplicant
|
||||
add_custom_target(
|
||||
wpa_supplicant_configure
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/hostap/conf/wpa_supplicant.config" "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/.config"
|
||||
)
|
||||
add_custom_target(
|
||||
wpa_supplicant_build
|
||||
COMMAND make -j$$(nproc) && cp wpa_supplicant wpa_supplicant_drc
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant"
|
||||
BYPRODUCTS "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/wpa_supplicant_drc"
|
||||
USES_TERMINAL
|
||||
DEPENDS wpa_supplicant_configure
|
||||
)
|
||||
add_executable(wpa_supplicant IMPORTED)
|
||||
add_dependencies(wpa_supplicant wpa_supplicant_build)
|
||||
set_target_properties(wpa_supplicant PROPERTIES
|
||||
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/wpa_supplicant_drc"
|
||||
)
|
||||
add_custom_command(
|
||||
TARGET wpa_supplicant_build POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/wpa_supplicant_drc" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
|
||||
DEPENDS wpa_supplicant_build
|
||||
)
|
||||
|
||||
# Build client library
|
||||
add_custom_target(
|
||||
wpa_client_build
|
||||
COMMAND make libwpa_client.so
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant"
|
||||
DEPENDS wpa_supplicant
|
||||
BYPRODUCTS "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/libwpa_client.so"
|
||||
)
|
||||
add_library(wpa_client SHARED IMPORTED)
|
||||
set_target_properties(wpa_client PROPERTIES
|
||||
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/libwpa_client.so"
|
||||
IMPORTED_NO_SONAME true
|
||||
)
|
||||
add_dependencies(wpa_client wpa_client_build)
|
||||
|
||||
# Link our library with the client library
|
||||
target_link_libraries(vanilla-pipe PRIVATE
|
||||
wpa_client
|
||||
pthread
|
||||
vanilla
|
||||
)
|
||||
|
||||
# Copy files into build directory
|
||||
#add_custom_command(
|
||||
# TARGET wpa_supplicant POST_BUILD
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/hostap/wpa_supplicant/wpa_supplicant" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/wpa_supplicant_drc"
|
||||
#)
|
||||
|
||||
install(IMPORTED_RUNTIME_ARTIFACTS wpa_client wpa_supplicant)
|
||||
|
|
|
@ -1,132 +1,50 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../../lib/gamepad/ports.h"
|
||||
|
||||
typedef struct {
|
||||
int from;
|
||||
int to;
|
||||
} relay_ports;
|
||||
|
||||
struct in_addr client_address = {0};
|
||||
|
||||
void* do_relay(void *data)
|
||||
{
|
||||
relay_ports *ports = (relay_ports *) data;
|
||||
char buf[2048];
|
||||
ssize_t read_size;
|
||||
while ((read_size = recv(ports->from, buf, sizeof(buf), 0)) != -1) {
|
||||
struct sockaddr_in forward = {0};
|
||||
forward.sin_family = AF_INET;
|
||||
forward.sin_addr = client_address;
|
||||
forward.sin_port = ports->to;
|
||||
sendto(ports->to, buf, read_size, 0, (const struct sockaddr *) &forward, sizeof(forward));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int open_socket(in_port_t port)
|
||||
{
|
||||
struct sockaddr_in in = {0};
|
||||
in.sin_family = AF_INET;
|
||||
in.sin_addr.s_addr = INADDR_ANY;
|
||||
in.sin_port = htons(port);
|
||||
|
||||
int skt = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (skt == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind(skt, (const struct sockaddr *) &in, sizeof(in)) == -1) {
|
||||
printf("FAILED TO BIND PORT %u: %i\n", port, errno);
|
||||
close(skt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return skt;
|
||||
}
|
||||
|
||||
void *open_relay(void *data)
|
||||
{
|
||||
in_port_t port = (in_port_t) data;
|
||||
int ret = -1;
|
||||
|
||||
// Open an incoming port from the console
|
||||
int from_console = open_socket(port);
|
||||
if (from_console == -1) {
|
||||
goto close;
|
||||
}
|
||||
|
||||
// Open an incoming port from the frontend
|
||||
int from_frontend = open_socket(port + 100);
|
||||
if (from_frontend == -1) {
|
||||
goto close_console_connection;
|
||||
}
|
||||
|
||||
relay_ports a_ports, b_ports;
|
||||
|
||||
a_ports.from = from_console;
|
||||
a_ports.to = from_frontend;
|
||||
|
||||
b_ports.from = from_frontend;
|
||||
b_ports.to = from_console;
|
||||
|
||||
pthread_t a_thread, b_thread;
|
||||
pthread_create(&a_thread, NULL, do_relay, &a_ports);
|
||||
pthread_create(&b_thread, NULL, do_relay, &b_ports);
|
||||
|
||||
pthread_join(a_thread, NULL);
|
||||
pthread_join(b_thread, NULL);
|
||||
|
||||
ret = 0;
|
||||
|
||||
close_frontend_connection:
|
||||
close(from_frontend);
|
||||
|
||||
close_console_connection:
|
||||
close(from_console);
|
||||
|
||||
close:
|
||||
return (void *) ret;
|
||||
}
|
||||
#include "wpa.h"
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
if (argc != 3) {
|
||||
if (argc < 3) {
|
||||
goto show_help;
|
||||
}
|
||||
|
||||
if (!strcmp("-sync", argv[1])) {
|
||||
int code = atoi(argv[2]);
|
||||
const char *wireless_interface = argv[1];
|
||||
const char *mode = argv[2];
|
||||
|
||||
|
||||
if (!strcmp("-sync", mode)) {
|
||||
if (argc < 3) {
|
||||
printf("ERROR: -sync requires sync code\n\n");
|
||||
goto show_help;
|
||||
}
|
||||
|
||||
int code = atoi(argv[3]);
|
||||
if (code == 0) {
|
||||
printf("ERROR: Invalid sync code\n\n");
|
||||
goto show_help;
|
||||
}
|
||||
|
||||
vanilla_sync_with_console(wireless_interface, code);
|
||||
} else if (!strcmp("-connect", mode)) {
|
||||
if (argc < 3) {
|
||||
printf("ERROR: -sync requires local address\n\n");
|
||||
goto show_help;
|
||||
}
|
||||
|
||||
} else if (!strcmp("-connect", argv[1])) {
|
||||
inet_pton(AF_INET, argv[2], &client_address);
|
||||
if (!inet_pton(AF_INET, argv[3], &client_address)) {
|
||||
printf("ERROR: Invalid client address\n\n");
|
||||
goto show_help;
|
||||
}
|
||||
|
||||
pthread_t vid_thread, aud_thread, msg_thread, cmd_thread, hid_thread;
|
||||
|
||||
pthread_create(&vid_thread, NULL, open_relay, (void *) PORT_VID);
|
||||
pthread_create(&aud_thread, NULL, open_relay, (void *) PORT_AUD);
|
||||
pthread_create(&msg_thread, NULL, open_relay, (void *) PORT_MSG);
|
||||
pthread_create(&cmd_thread, NULL, open_relay, (void *) PORT_CMD);
|
||||
pthread_create(&hid_thread, NULL, open_relay, (void *) PORT_HID);
|
||||
|
||||
printf("READY\n");
|
||||
|
||||
pthread_join(vid_thread, NULL);
|
||||
pthread_join(aud_thread, NULL);
|
||||
pthread_join(msg_thread, NULL);
|
||||
pthread_join(cmd_thread, NULL);
|
||||
pthread_join(hid_thread, NULL);
|
||||
vanilla_connect_to_console(wireless_interface);
|
||||
} else if (!strcmp("-is_synced", mode)) {
|
||||
if (vanilla_has_config()) {
|
||||
printf("YES\n");
|
||||
} else {
|
||||
printf("NO\n");
|
||||
}
|
||||
} else {
|
||||
printf("ERROR: Invalid mode\n\n");
|
||||
goto show_help;
|
||||
|
@ -137,11 +55,17 @@ int main(int argc, const char **argv)
|
|||
show_help:
|
||||
printf("vanilla-pipe - brokers a connection between Vanilla and the Wii U\n");
|
||||
printf("\n");
|
||||
printf("Usage: %s <mode> <args>\n", argv[0]);
|
||||
printf("Usage: %s <wireless-interface> <mode> [args]\n", argv[0]);
|
||||
printf("\n");
|
||||
printf("Modes: \n");
|
||||
printf(" -sync <code> Sync/authenticate with the Wii U\n");
|
||||
printf(" -connect <client-address> Connect to the Wii U (requires syncing prior)\n");
|
||||
printf(" -is_synced Returns 1 if gamepad has been synced or 0 if it hasn't yet\n");
|
||||
printf("\n");
|
||||
printf("Sync code is a 4-digit PIN based on the card suits shown on the console.\n");
|
||||
printf("To calculate the code, use the following:\n");
|
||||
printf(" ♠ (spade) = 0 ♥ (heart) = 1 ♦ (diamond) = 2 ♣ (clover) = 3\n");
|
||||
printf(" Example: ♣♠♥♦ (clover, spade, heart, diamond) would equal 3012\n");
|
||||
printf("\n");
|
||||
|
||||
return 1;
|
||||
|
|
851
pipe/linux/wpa.c
Normal file
851
pipe/linux/wpa.c
Normal file
|
@ -0,0 +1,851 @@
|
|||
#include "wpa.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <wpa_ctrl.h>
|
||||
|
||||
#include "../../lib/gamepad/ports.h"
|
||||
|
||||
#include "status.h"
|
||||
#include "util.h"
|
||||
#include "vanilla.h"
|
||||
#include "wpa.h"
|
||||
|
||||
const char *wpa_ctrl_interface = "/var/run/wpa_supplicant_drc";
|
||||
|
||||
int is_interrupted()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void clear_interrupt(){}
|
||||
|
||||
void wpa_msg(char *msg, size_t len)
|
||||
{
|
||||
print_info("%.*s", len, msg);
|
||||
}
|
||||
|
||||
void wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, char *buf, size_t *buf_len)
|
||||
{
|
||||
wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, buf_len, NULL /*wpa_msg*/);
|
||||
}
|
||||
|
||||
int get_binary_in_working_directory(const char *bin_name, char *buf, size_t buf_size)
|
||||
{
|
||||
size_t path_size = get_max_path_length();
|
||||
char *path_buf = malloc(path_size);
|
||||
if (!path_buf) {
|
||||
// Failed to allocate buffer, terminate
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get current working directory
|
||||
// TODO: This is Linux only and will require changes on other platforms
|
||||
ssize_t link_len = readlink("/proc/self/exe", path_buf, path_size);
|
||||
if (link_len < 0) {
|
||||
print_info("READLINK ERROR: %i", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Merge current working directory with wpa_supplicant name
|
||||
path_buf[link_len] = 0;
|
||||
dirname(path_buf);
|
||||
int r = snprintf(buf, path_size, "%s/%s", path_buf, bin_name);
|
||||
free(path_buf);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
ssize_t read_line_from_pipe(int pipe, char *buf, size_t buf_len)
|
||||
{
|
||||
int attempts = 0;
|
||||
const static int max_attempts = 5;
|
||||
ssize_t read_count = 0;
|
||||
while (read_count < buf_len) {
|
||||
ssize_t this_read = read(pipe, buf + read_count, 1);
|
||||
if (this_read == 0) {
|
||||
attempts++;
|
||||
if (is_interrupted() || attempts == max_attempts) {
|
||||
return -1;
|
||||
}
|
||||
sleep(1); // Wait for more output
|
||||
continue;
|
||||
}
|
||||
|
||||
attempts = 0;
|
||||
|
||||
if (buf[read_count] == '\n') {
|
||||
buf[read_count] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
read_count++;
|
||||
}
|
||||
return read_count;
|
||||
}
|
||||
|
||||
int wait_for_output(int pipe, const char *expected_output)
|
||||
{
|
||||
static const int max_attempts = 5;
|
||||
int nbytes, attempts = 0, success = 0;
|
||||
const int expected_len = strlen(expected_output);
|
||||
char buf[100];
|
||||
int read_count = 0;
|
||||
int ret = 0;
|
||||
do {
|
||||
// Read line from child process
|
||||
read_line_from_pipe(pipe, buf, sizeof(buf));
|
||||
|
||||
print_info("SUBPROCESS %s", buf);
|
||||
|
||||
// We got success message!
|
||||
if (!memcmp(buf, expected_output, expected_len)) {
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Haven't gotten success message (yet), wait and try again
|
||||
} while (attempts < max_attempts && !is_interrupted());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int start_process(const char **argv, pid_t *pid_out, int *stdout_pipe, int *stderr_pipe)
|
||||
{
|
||||
// Set up pipes so child stdout can be read by the parent process
|
||||
int out_pipes[2], err_pipes[2];
|
||||
pipe(out_pipes);
|
||||
pipe(err_pipes);
|
||||
|
||||
// Get parent pid (allows us to check if parent was terminated immediately after fork)
|
||||
pid_t ppid_before_fork = getpid();
|
||||
|
||||
// Fork into parent/child processes
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == 0) {
|
||||
// We are in the child. Set child to terminate when parent does.
|
||||
int r = prctl(PR_SET_PDEATHSIG, SIGHUP);
|
||||
if (r == -1) {
|
||||
perror(0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// See if parent pid is still the same. If not, it must have been terminated, so we will exit too.
|
||||
if (getppid() != ppid_before_fork) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set up pipes so our stdout can be read by the parent process
|
||||
dup2(out_pipes[1], STDOUT_FILENO);
|
||||
dup2(err_pipes[1], STDERR_FILENO);
|
||||
close(out_pipes[0]);
|
||||
close(out_pipes[1]);
|
||||
close(err_pipes[0]);
|
||||
close(err_pipes[1]);
|
||||
|
||||
// Execute process (this will replace the running code)
|
||||
r = execvp(argv[0], (char * const *) argv);
|
||||
|
||||
// Handle failure to execute, use _exit so we don't interfere with the host
|
||||
_exit(1);
|
||||
} else if (pid < 0) {
|
||||
// Fork error
|
||||
return VANILLA_ERROR;
|
||||
} else {
|
||||
// Continuation of parent
|
||||
close(out_pipes[1]);
|
||||
close(err_pipes[1]);
|
||||
if (!stdout_pipe) {
|
||||
// Caller is not interested in the stdout
|
||||
close(out_pipes[0]);
|
||||
} else {
|
||||
// Caller is interested so we'll hand them the pipe
|
||||
*stdout_pipe = out_pipes[0];
|
||||
}
|
||||
if (!stderr_pipe) {
|
||||
close(err_pipes[0]);
|
||||
} else {
|
||||
*stderr_pipe = err_pipes[0];
|
||||
}
|
||||
|
||||
// If caller wants the pid, send it to them
|
||||
if (pid_out) {
|
||||
*pid_out = pid;
|
||||
}
|
||||
|
||||
return VANILLA_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
int start_wpa_supplicant(const char *wireless_interface, const char *config_file, pid_t *pid)
|
||||
{
|
||||
// TODO: drc-sim has `rfkill unblock wlan`, should we do that too?
|
||||
|
||||
// Kill any potentially orphaned wpa_supplicant_drcs
|
||||
const char *wpa_supplicant_drc = "wpa_supplicant_drc";
|
||||
const char *kill_argv[] = {"killall", "-9", wpa_supplicant_drc, NULL};
|
||||
pid_t kill_pid;
|
||||
int kill_pipe;
|
||||
int r = start_process(kill_argv, &kill_pid, &kill_pipe, NULL);
|
||||
int status;
|
||||
waitpid(kill_pid, &status, 0);
|
||||
|
||||
size_t path_size = get_max_path_length();
|
||||
char *wpa_buf = malloc(path_size);
|
||||
|
||||
get_binary_in_working_directory(wpa_supplicant_drc, wpa_buf, path_size);
|
||||
|
||||
const char *argv[] = {wpa_buf, "-Dnl80211", "-i", wireless_interface, "-c", config_file, NULL};
|
||||
print_info("USING WPA CONFIG: %s", config_file);
|
||||
int pipe;
|
||||
|
||||
r = start_process(argv, pid, &pipe, NULL);
|
||||
free(wpa_buf);
|
||||
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
return r;
|
||||
}
|
||||
|
||||
// Wait for WPA supplicant to start
|
||||
if (wait_for_output(pipe, "Successfully initialized wpa_supplicant")) {
|
||||
// I'm not sure why, but closing this pipe breaks wpa_supplicant in subtle ways, so just leave it.
|
||||
//close(pipe);
|
||||
|
||||
// WPA initialized correctly! Continue with action...
|
||||
return VANILLA_SUCCESS;
|
||||
} else {
|
||||
// Give up
|
||||
give_up:
|
||||
kill((*pid), SIGTERM);
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
int wpa_setup_environment(const char *wireless_interface, const char *wireless_conf_file, ready_callback_t callback, void *callback_data)
|
||||
{
|
||||
int ret = VANILLA_ERROR;
|
||||
|
||||
clear_interrupt();
|
||||
//install_interrupt_handler();
|
||||
|
||||
// Check status of interface with NetworkManager
|
||||
int is_managed = 0;
|
||||
if (is_networkmanager_managing_device(wireless_interface, &is_managed) != VANILLA_SUCCESS) {
|
||||
print_info("FAILED TO DETERMINE MANAGED STATE OF WIRELESS INTERFACE");
|
||||
//goto die;
|
||||
}
|
||||
|
||||
// If NetworkManager is managing this device, temporarily stop it from doing so
|
||||
if (is_managed) {
|
||||
if (disable_networkmanager_on_device(wireless_interface) != VANILLA_SUCCESS) {
|
||||
print_info("FAILED TO SET %s TO UNMANAGED, RESULTS MAY BE UNPREDICTABLE");
|
||||
} else {
|
||||
print_info("TEMPORARILY SET %s TO UNMANAGED", wireless_interface);
|
||||
}
|
||||
}
|
||||
|
||||
// Start modified WPA supplicant
|
||||
pid_t pid;
|
||||
int err = start_wpa_supplicant(wireless_interface, wireless_conf_file, &pid);
|
||||
if (err != VANILLA_SUCCESS || is_interrupted()) {
|
||||
print_info("FAILED TO START WPA SUPPLICANT");
|
||||
goto die_and_reenable_managed;
|
||||
}
|
||||
|
||||
// Get control interface
|
||||
const size_t buf_len = 1048576;
|
||||
char *buf = malloc(buf_len);
|
||||
snprintf(buf, buf_len, "%s/%s", wpa_ctrl_interface, wireless_interface);
|
||||
struct wpa_ctrl *ctrl;
|
||||
while (!(ctrl = wpa_ctrl_open(buf))) {
|
||||
if (is_interrupted()) goto die_and_kill;
|
||||
print_info("WAITING FOR CTRL INTERFACE");
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (is_interrupted() || wpa_ctrl_attach(ctrl) < 0) {
|
||||
print_info("FAILED TO ATTACH TO WPA");
|
||||
goto die_and_close;
|
||||
}
|
||||
|
||||
ret = callback(ctrl, callback_data);
|
||||
|
||||
die_and_detach:
|
||||
wpa_ctrl_detach(ctrl);
|
||||
|
||||
die_and_close:
|
||||
wpa_ctrl_close(ctrl);
|
||||
|
||||
die_and_kill:
|
||||
kill(pid, SIGTERM);
|
||||
|
||||
free(buf);
|
||||
|
||||
die_and_reenable_managed:
|
||||
if (is_managed) {
|
||||
print_info("SETTING %s BACK TO MANAGED", wireless_interface);
|
||||
enable_networkmanager_on_device(wireless_interface);
|
||||
}
|
||||
|
||||
die:
|
||||
// Remove our custom sigint signal handler
|
||||
//uninstall_interrupt_handler();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int call_dhcp(const char *network_interface, pid_t *dhclient_pid)
|
||||
{
|
||||
const char *argv[] = {"dhclient", "-d", "--no-pid", network_interface, NULL, NULL, NULL};
|
||||
|
||||
size_t buf_size = get_max_path_length();
|
||||
char *dhclient_buf = malloc(buf_size);
|
||||
char *dhclient_script = NULL;
|
||||
get_binary_in_working_directory("dhclient", dhclient_buf, buf_size);
|
||||
|
||||
if (access(dhclient_buf, F_OK) == 0) {
|
||||
// HACK: Assume we're working in our deployed environment
|
||||
// TODO: Should probably just incorporate dhclient (or something like it) directly as a library
|
||||
argv[0] = dhclient_buf;
|
||||
argv[4] = "-sf";
|
||||
|
||||
dhclient_script = malloc(buf_size);
|
||||
get_binary_in_working_directory("../sbin/dhclient-script", dhclient_script, buf_size);
|
||||
argv[5] = dhclient_script;
|
||||
|
||||
print_info("Using custom dhclient at: %s", argv[0]);
|
||||
} else {
|
||||
print_info("Using system dhclient");
|
||||
}
|
||||
|
||||
int dhclient_pipe;
|
||||
int r = start_process(argv, dhclient_pid, NULL, &dhclient_pipe);
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
print_info("FAILED TO CALL DHCLIENT");
|
||||
return r;
|
||||
}
|
||||
|
||||
free(dhclient_buf);
|
||||
if (dhclient_script) free(dhclient_script);
|
||||
|
||||
if (wait_for_output(dhclient_pipe, "bound to")) {
|
||||
return VANILLA_SUCCESS;
|
||||
} else {
|
||||
print_info("FAILED TO ESTABLISH DHCP");
|
||||
kill(*dhclient_pid, SIGTERM);
|
||||
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *nmcli = "nmcli";
|
||||
int is_networkmanager_managing_device(const char *wireless_interface, int *is_managed)
|
||||
{
|
||||
pid_t nmcli_pid;
|
||||
int pipe;
|
||||
|
||||
const char *argv[] = {nmcli, "device", "show", wireless_interface, NULL};
|
||||
|
||||
int r = start_process(argv, &nmcli_pid, &pipe, NULL);
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
// Assume nmcli is not installed so the host is not using NetworkManager
|
||||
print_info("FAILED TO LAUNCH NMCLI, RESULTS MAY BE UNPREDICTABLE");
|
||||
*is_managed = 0;
|
||||
return VANILLA_SUCCESS;
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(nmcli_pid, &status, 0);
|
||||
|
||||
if (!WIFEXITED(status)) {
|
||||
// Something went wrong
|
||||
print_info("NMCLI DID NOT EXIT NORMALLY");
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
|
||||
char buf[100];
|
||||
int ret = VANILLA_ERROR;
|
||||
while (read_line_from_fd(pipe, buf, sizeof(buf))) {
|
||||
if (memcmp(buf, "GENERAL.STATE", 13) == 0) {
|
||||
*is_managed = !strstr(buf, "unmanaged");
|
||||
ret = VANILLA_SUCCESS;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
close(pipe);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int set_networkmanager_on_device(const char *wireless_interface, int on)
|
||||
{
|
||||
const char *argv[] = {nmcli, "device", "set", wireless_interface, "managed", on ? "on" : "off", NULL};
|
||||
|
||||
pid_t nmcli_pid;
|
||||
int r = start_process(argv, &nmcli_pid, NULL, NULL);
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
return r;
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(nmcli_pid, &status, 0);
|
||||
if (WIFEXITED(status)) {
|
||||
return VANILLA_SUCCESS;
|
||||
} else {
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
int disable_networkmanager_on_device(const char *wireless_interface)
|
||||
{
|
||||
return set_networkmanager_on_device(wireless_interface, 0);
|
||||
}
|
||||
|
||||
int enable_networkmanager_on_device(const char *wireless_interface)
|
||||
{
|
||||
return set_networkmanager_on_device(wireless_interface, 1);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int from_socket;
|
||||
in_port_t from_port;
|
||||
int to_socket;
|
||||
in_addr_t to_address;
|
||||
in_port_t to_port;
|
||||
} relay_ports;
|
||||
|
||||
struct in_addr client_address = {0};
|
||||
|
||||
void* do_relay(void *data)
|
||||
{
|
||||
relay_ports *ports = (relay_ports *) data;
|
||||
char buf[2048];
|
||||
ssize_t read_size;
|
||||
while ((read_size = recv(ports->from_socket, buf, sizeof(buf), 0)) != -1) {
|
||||
struct sockaddr_in forward = {0};
|
||||
forward.sin_family = AF_INET;
|
||||
forward.sin_addr.s_addr = ports->to_address;
|
||||
forward.sin_port = htons(ports->to_port);
|
||||
|
||||
char ip[20];
|
||||
inet_ntop(AF_INET, &forward.sin_addr, ip, sizeof(ip));
|
||||
printf("received packet on port %i, forwarding to address %s:%i\n", ports->from_port, ip, ports->to_port);
|
||||
sendto(ports->to_socket, buf, read_size, 0, (const struct sockaddr *) &forward, sizeof(forward));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
relay_ports create_ports(int from_socket, in_port_t from_port, int to_socket, in_addr_t to_addr, in_port_t to_port)
|
||||
{
|
||||
relay_ports ports;
|
||||
ports.from_socket = from_socket;
|
||||
ports.from_port = from_port;
|
||||
ports.to_socket = to_socket;
|
||||
ports.to_address = to_addr;
|
||||
ports.to_port = to_port;
|
||||
return ports;
|
||||
}
|
||||
|
||||
int open_socket(in_port_t port)
|
||||
{
|
||||
struct sockaddr_in in = {0};
|
||||
in.sin_family = AF_INET;
|
||||
in.sin_addr.s_addr = INADDR_ANY;
|
||||
in.sin_port = htons(port);
|
||||
|
||||
int skt = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (skt == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind(skt, (const struct sockaddr *) &in, sizeof(in)) == -1) {
|
||||
printf("FAILED TO BIND PORT %u: %i\n", port, errno);
|
||||
close(skt);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return skt;
|
||||
}
|
||||
|
||||
void *open_relay(void *data)
|
||||
{
|
||||
in_port_t port = (in_port_t) data;
|
||||
int ret = -1;
|
||||
|
||||
// Open an incoming port from the console
|
||||
int from_console = open_socket(port);
|
||||
if (from_console == -1) {
|
||||
goto close;
|
||||
}
|
||||
|
||||
// Open an incoming port from the frontend
|
||||
int from_frontend = open_socket(port + 100);
|
||||
if (from_frontend == -1) {
|
||||
goto close_console_connection;
|
||||
}
|
||||
|
||||
relay_ports console_to_frontend = create_ports(from_console, port, from_frontend, client_address.s_addr, port + 200);
|
||||
relay_ports frontend_to_console = create_ports(from_frontend, port + 100, from_console, inet_addr("192.168.1.10"), port - 100);
|
||||
|
||||
pthread_t a_thread, b_thread;
|
||||
pthread_create(&a_thread, NULL, do_relay, &console_to_frontend);
|
||||
pthread_create(&b_thread, NULL, do_relay, &frontend_to_console);
|
||||
|
||||
pthread_join(a_thread, NULL);
|
||||
pthread_join(b_thread, NULL);
|
||||
|
||||
ret = 0;
|
||||
|
||||
close_frontend_connection:
|
||||
close(from_frontend);
|
||||
|
||||
close_console_connection:
|
||||
close(from_console);
|
||||
|
||||
close:
|
||||
return (void *) ret;
|
||||
}
|
||||
|
||||
void create_all_relays()
|
||||
{
|
||||
pthread_t vid_thread, aud_thread, msg_thread, cmd_thread, hid_thread;
|
||||
|
||||
pthread_create(&vid_thread, NULL, open_relay, (void *) PORT_VID);
|
||||
pthread_create(&aud_thread, NULL, open_relay, (void *) PORT_AUD);
|
||||
pthread_create(&msg_thread, NULL, open_relay, (void *) PORT_MSG);
|
||||
pthread_create(&cmd_thread, NULL, open_relay, (void *) PORT_CMD);
|
||||
pthread_create(&hid_thread, NULL, open_relay, (void *) PORT_HID);
|
||||
|
||||
printf("READY\n");
|
||||
|
||||
pthread_join(vid_thread, NULL);
|
||||
pthread_join(aud_thread, NULL);
|
||||
pthread_join(msg_thread, NULL);
|
||||
pthread_join(cmd_thread, NULL);
|
||||
pthread_join(hid_thread, NULL);
|
||||
}
|
||||
|
||||
int connect_as_gamepad_internal(struct wpa_ctrl *ctrl, const char *wireless_interface)
|
||||
{
|
||||
while (1) {
|
||||
while (!wpa_ctrl_pending(ctrl)) {
|
||||
sleep(2);
|
||||
print_info("WAITING FOR CONNECTION");
|
||||
|
||||
if (is_interrupted()) return VANILLA_ERROR;
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
size_t actual_buf_len = sizeof(buf);
|
||||
wpa_ctrl_recv(ctrl, buf, &actual_buf_len);
|
||||
print_info("CONN RECV: %.*s", actual_buf_len, buf);
|
||||
|
||||
if (memcmp(buf, "<3>CTRL-EVENT-CONNECTED", 23) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_interrupted()) return VANILLA_ERROR;
|
||||
}
|
||||
|
||||
print_info("CONNECTED TO CONSOLE");
|
||||
|
||||
// Use DHCP on interface
|
||||
pid_t dhclient_pid;
|
||||
int r = call_dhcp(wireless_interface, &dhclient_pid);
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
print_info("FAILED TO RUN DHCP ON %s", wireless_interface);
|
||||
return r;
|
||||
} else {
|
||||
print_info("DHCP ESTABLISHED");
|
||||
}
|
||||
|
||||
{
|
||||
// Destroy default route that dhclient will have created
|
||||
pid_t ip_pid;
|
||||
const char *ip_args[] = {"ip", "route", "del", "default", "via", "192.168.1.1", "dev", wireless_interface, NULL};
|
||||
r = start_process(ip_args, &ip_pid, NULL, NULL);
|
||||
if (r != VANILLA_SUCCESS) {
|
||||
print_info("FAILED TO REMOVE CONSOLE ROUTE FROM SYSTEM");
|
||||
}
|
||||
|
||||
int ip_status;
|
||||
waitpid(ip_pid, &ip_status, 0);
|
||||
|
||||
if (!WIFEXITED(ip_status)) {
|
||||
print_info("FAILED TO REMOVE CONSOLE ROUTE FROM SYSTEM");
|
||||
}
|
||||
}
|
||||
|
||||
create_all_relays();
|
||||
|
||||
int kill_ret = kill(dhclient_pid, SIGTERM);
|
||||
print_info("killing dhclient %i: %i", dhclient_pid, kill_ret);
|
||||
}
|
||||
|
||||
size_t read_line_from_fd(int pipe, char *output, size_t max_output_size)
|
||||
{
|
||||
size_t i = 0;
|
||||
while (i < max_output_size && read(pipe, output, 1) > 0) {
|
||||
int newline = (*output == '\n');
|
||||
output++;
|
||||
i++;
|
||||
if (newline) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*output = 0;
|
||||
return i;
|
||||
}
|
||||
|
||||
size_t read_line_from_file(FILE *file, char *output, size_t max_output_size)
|
||||
{
|
||||
return read_line_from_fd(fileno(file), output, max_output_size);
|
||||
}
|
||||
|
||||
size_t get_home_directory(char *buf, size_t buf_size)
|
||||
{
|
||||
size_t ret = snprintf(buf, buf_size, "%s/%s", getenv("HOME"), ".vanilla");
|
||||
if (ret <= buf_size) {
|
||||
mkdir(buf, 0755);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t get_home_directory_file(const char *filename, char *buf, size_t buf_size)
|
||||
{
|
||||
size_t max_path_length = get_max_path_length();
|
||||
char *dir = malloc(max_path_length);
|
||||
get_home_directory(dir, max_path_length);
|
||||
|
||||
size_t ret = snprintf(buf, buf_size, "%s/%s", dir, filename);
|
||||
free(dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t get_max_path_length()
|
||||
{
|
||||
return pathconf(".", _PC_PATH_MAX);
|
||||
}
|
||||
|
||||
char wireless_authenticate_config_filename[1024] = {0};
|
||||
char wireless_connect_config_filename[1024] = {0};
|
||||
|
||||
const char *get_wireless_connect_config_filename()
|
||||
{
|
||||
if (wireless_connect_config_filename[0] == 0) {
|
||||
// Not initialized yet, do this now
|
||||
get_home_directory_file("vanilla_wpa_connect.conf", wireless_connect_config_filename, sizeof(wireless_connect_config_filename));
|
||||
}
|
||||
return wireless_connect_config_filename;
|
||||
}
|
||||
|
||||
const char *get_wireless_authenticate_config_filename()
|
||||
{
|
||||
if (wireless_authenticate_config_filename[0] == 0) {
|
||||
// Not initialized yet, do this now
|
||||
get_home_directory_file("vanilla_wpa_key.conf", wireless_authenticate_config_filename, sizeof(wireless_authenticate_config_filename));
|
||||
}
|
||||
return wireless_authenticate_config_filename;
|
||||
}
|
||||
|
||||
struct sync_args {
|
||||
uint16_t code;
|
||||
};
|
||||
|
||||
struct connect_args {
|
||||
const char *wireless_interface;
|
||||
};
|
||||
|
||||
int create_connect_config(const char *input_config, const char *bssid)
|
||||
{
|
||||
FILE *in_file = fopen(input_config, "r");
|
||||
if (!in_file) {
|
||||
print_info("FAILED TO OPEN INPUT CONFIG FILE");
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
|
||||
FILE *out_file = fopen(get_wireless_connect_config_filename(), "w");
|
||||
if (!out_file) {
|
||||
print_info("FAILED TO OPEN OUTPUT CONFIG FILE");
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
|
||||
int len;
|
||||
char buf[150];
|
||||
while (len = read_line_from_file(in_file, buf, sizeof(buf))) {
|
||||
if (memcmp("\tssid=", buf, 6) == 0) {
|
||||
fprintf(out_file, "\tscan_ssid=1\n\tbssid=%s\n", bssid);
|
||||
}
|
||||
|
||||
fwrite(buf, len, 1, out_file);
|
||||
|
||||
if (memcmp(buf, "update_config=1", 15) == 0) {
|
||||
static const char *ap_scan_line = "ap_scan=1\n";
|
||||
fwrite(ap_scan_line, strlen(ap_scan_line), 1, out_file);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(in_file);
|
||||
fclose(out_file);
|
||||
|
||||
return VANILLA_SUCCESS;
|
||||
}
|
||||
|
||||
int sync_with_console_internal(struct wpa_ctrl *ctrl, uint16_t code)
|
||||
{
|
||||
char buf[16384];
|
||||
const size_t buf_len = sizeof(buf);
|
||||
|
||||
int found_console = 0;
|
||||
char bssid[18];
|
||||
do {
|
||||
size_t actual_buf_len;
|
||||
|
||||
if (is_interrupted()) goto exit_loop;
|
||||
|
||||
// Request scan from hardware
|
||||
while (1) {
|
||||
if (is_interrupted()) goto exit_loop;
|
||||
|
||||
// print_info("SCANNING");
|
||||
actual_buf_len = buf_len;
|
||||
wpa_ctrl_command(ctrl, "SCAN", buf, &actual_buf_len);
|
||||
|
||||
if (!memcmp(buf, "FAIL-BUSY", 9)) {
|
||||
//print_info("DEVICE BUSY, RETRYING");
|
||||
sleep(5);
|
||||
} else if (!memcmp(buf, "OK", 2)) {
|
||||
break;
|
||||
} else {
|
||||
print_info("UNKNOWN SCAN RESPONSE: %.*s (RETRYING)", actual_buf_len, buf);
|
||||
sleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
//print_info("WAITING FOR SCAN RESULTS");
|
||||
actual_buf_len = buf_len;
|
||||
wpa_ctrl_command(ctrl, "SCAN_RESULTS", buf, &actual_buf_len);
|
||||
print_info("RECEIVED SCAN RESULTS");
|
||||
|
||||
const char *line = strtok(buf, "\n");
|
||||
while (line) {
|
||||
if (is_interrupted()) goto exit_loop;
|
||||
|
||||
if (strstr(line, "WiiU")) {
|
||||
print_info("FOUND WII U, TESTING WPS PIN");
|
||||
|
||||
// Make copy of bssid for later
|
||||
strncpy(bssid, line, sizeof(bssid));
|
||||
bssid[17] = '\0';
|
||||
|
||||
char wps_buf[100];
|
||||
snprintf(wps_buf, sizeof(wps_buf), "WPS_PIN %.*s %04d5678", 17, bssid, code);
|
||||
|
||||
size_t actual_buf_len = buf_len;
|
||||
wpa_ctrl_command(ctrl, wps_buf, buf, &actual_buf_len);
|
||||
|
||||
static const int max_wait = 20;
|
||||
int wait_count = 0;
|
||||
int cred_received = 0;
|
||||
|
||||
while (!is_interrupted()) {
|
||||
while (wait_count < max_wait && !wpa_ctrl_pending(ctrl)) {
|
||||
if (is_interrupted()) goto exit_loop;
|
||||
sleep(1);
|
||||
wait_count++;
|
||||
}
|
||||
|
||||
if (wait_count == max_wait) {
|
||||
print_info("GIVING UP, RETURNING TO SCANNING");
|
||||
break;
|
||||
}
|
||||
|
||||
actual_buf_len = buf_len;
|
||||
wpa_ctrl_recv(ctrl, buf, &actual_buf_len);
|
||||
print_info("CRED RECV: %.*s", buf_len, buf);
|
||||
|
||||
if (!memcmp("<3>WPS-CRED-RECEIVED", buf, 20)) {
|
||||
print_info("RECEIVED AUTHENTICATION FROM CONSOLE");
|
||||
cred_received = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cred_received) {
|
||||
// Tell wpa_supplicant to save config
|
||||
actual_buf_len = buf_len;
|
||||
print_info("SAVING CONFIG", actual_buf_len, buf);
|
||||
wpa_ctrl_command(ctrl, "SAVE_CONFIG", buf, &actual_buf_len);
|
||||
|
||||
// Create connect config which needs a couple more parameters
|
||||
create_connect_config(get_wireless_authenticate_config_filename(), bssid);
|
||||
|
||||
found_console = 1;
|
||||
}
|
||||
}
|
||||
line = strtok(NULL, "\n");
|
||||
}
|
||||
} while (!found_console);
|
||||
|
||||
exit_loop:
|
||||
return found_console ? VANILLA_SUCCESS : VANILLA_ERROR;
|
||||
}
|
||||
|
||||
int thunk_to_sync(struct wpa_ctrl *ctrl, void *data)
|
||||
{
|
||||
struct sync_args *args = (struct sync_args *) data;
|
||||
return sync_with_console_internal(ctrl, args->code);
|
||||
}
|
||||
|
||||
int thunk_to_connect(struct wpa_ctrl *ctrl, void *data)
|
||||
{
|
||||
struct connect_args *args = (struct connect_args *) data;
|
||||
return connect_as_gamepad_internal(ctrl, args->wireless_interface);
|
||||
}
|
||||
|
||||
int vanilla_sync_with_console(const char *wireless_interface, uint16_t code)
|
||||
{
|
||||
const char *wireless_conf_file;
|
||||
|
||||
FILE *config;
|
||||
wireless_conf_file = get_wireless_authenticate_config_filename();
|
||||
config = fopen(wireless_conf_file, "w");
|
||||
if (!config) {
|
||||
print_info("FAILED TO WRITE TEMP CONFIG: %s", wireless_conf_file);
|
||||
return VANILLA_ERROR;
|
||||
}
|
||||
|
||||
fprintf(config, "ctrl_interface=%s\nupdate_config=1\n", wpa_ctrl_interface);
|
||||
fclose(config);
|
||||
|
||||
struct sync_args args;
|
||||
args.code = code;
|
||||
|
||||
return wpa_setup_environment(wireless_interface, wireless_conf_file, thunk_to_sync, &args);
|
||||
}
|
||||
|
||||
int vanilla_connect_to_console(const char *wireless_interface)
|
||||
{
|
||||
struct connect_args args;
|
||||
args.wireless_interface = wireless_interface;
|
||||
|
||||
return wpa_setup_environment(wireless_interface, get_wireless_connect_config_filename(), thunk_to_connect, &args);
|
||||
}
|
||||
|
||||
int vanilla_has_config()
|
||||
{
|
||||
return (access(get_wireless_connect_config_filename(), F_OK) == 0);
|
||||
}
|
|
@ -1,12 +1,15 @@
|
|||
#ifndef VANILLA_WPA_H
|
||||
#define VANILLA_WPA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
struct wpa_ctrl;
|
||||
extern const char *wpa_ctrl_interface;
|
||||
|
||||
extern struct in_addr client_address;
|
||||
|
||||
typedef int (*ready_callback_t)(struct wpa_ctrl *, void *);
|
||||
|
||||
int wpa_setup_environment(const char *wireless_interface, const char *wireless_conf_file, ready_callback_t callback, void *callback_data);
|
||||
|
@ -20,4 +23,8 @@ int is_networkmanager_managing_device(const char *wireless_interface, int *is_ma
|
|||
int disable_networkmanager_on_device(const char *wireless_interface);
|
||||
int enable_networkmanager_on_device(const char *wireless_interface);
|
||||
|
||||
int vanilla_sync_with_console(const char *wireless_interface, uint16_t code);
|
||||
int vanilla_connect_to_console(const char *wireless_interface);
|
||||
int vanilla_has_config();
|
||||
|
||||
#endif // VANILLA_WPA_H
|
Loading…
Reference in a new issue