diff --git a/.gitmodules b/.gitmodules index 6f41ef5..bf274ac 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "lib/hostap"] - path = lib/hostap + path = pipe/linux/hostap url = https://github.com/rolandoislas/drc-hostap diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c248a0..96fbd66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,5 +11,4 @@ SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") add_subdirectory(lib) add_subdirectory(pipe) -add_subdirectory(cli) add_subdirectory(app) diff --git a/app/backend.cpp b/app/backend.cpp index 456c71e..62c5bc5 100644 --- a/app/backend.cpp +++ b/app/backend.cpp @@ -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() diff --git a/cli/CMakeLists.txt b/cli/CMakeLists.txt deleted file mode 100644 index c5f95bd..0000000 --- a/cli/CMakeLists.txt +++ /dev/null @@ -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) \ No newline at end of file diff --git a/cli/main.c b/cli/main.c deleted file mode 100644 index 588ef9c..0000000 --- a/cli/main.c +++ /dev/null @@ -1,104 +0,0 @@ -/** - * This is a simple test app that can be used to inspect base functionality. - * - * Valid commands are: - * "SYNC " - * "CONNECT " - * "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 -#include -#include -#include - -#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; -} \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b30e652..b999b35 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -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) diff --git a/lib/gamepad/audio.c b/lib/gamepad/audio.c index 22d0f3e..455125a 100644 --- a/lib/gamepad/audio.c +++ b/lib/gamepad/audio.c @@ -5,6 +5,7 @@ #include #include "gamepad.h" +#include "status.h" #include "vanilla.h" #include "util.h" diff --git a/lib/gamepad/gamepad.c b/lib/gamepad/gamepad.c index bfcbde8..42e2482 100644 --- a/lib/gamepad/gamepad.c +++ b/lib/gamepad/gamepad.c @@ -9,7 +9,6 @@ #include #include #include -#include #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) diff --git a/lib/gamepad/gamepad.h b/lib/gamepad/gamepad.h index a14c3be..c27ce4b 100644 --- a/lib/gamepad/gamepad.h +++ b/lib/gamepad/gamepad.h @@ -1,9 +1,17 @@ #ifndef VANILLA_GAMEPAD_H #define VANILLA_GAMEPAD_H -#include "ports.h" +// #include "ports.h" #include "vanilla.h" +#include + +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); diff --git a/lib/sync.c b/lib/sync.c deleted file mode 100644 index 3476b5b..0000000 --- a/lib/sync.c +++ /dev/null @@ -1,148 +0,0 @@ -#include "sync.h" - -#include -#include -#include -#include -#include -#include - -#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; -} \ No newline at end of file diff --git a/lib/sync.h b/lib/sync.h deleted file mode 100644 index ea568e3..0000000 --- a/lib/sync.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef VANILLA_SYNC_H -#define VANILLA_SYNC_H - -#include - -struct wpa_ctrl; - -int sync_with_console_internal(struct wpa_ctrl *ctrl, uint16_t code); - -#endif // VANILLA_SYNC_H \ No newline at end of file diff --git a/lib/util.c b/lib/util.c index 3676f2b..3afe0b1 100644 --- a/lib/util.c +++ b/lib/util.c @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -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; diff --git a/lib/util.h b/lib/util.h index 87318a7..db2bf33 100644 --- a/lib/util.h +++ b/lib/util.h @@ -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 \ No newline at end of file diff --git a/lib/vanilla.c b/lib/vanilla.c index bfae2eb..6265fa7 100644 --- a/lib/vanilla.c +++ b/lib/vanilla.c @@ -3,74 +3,18 @@ #include #include #include -#include #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() diff --git a/lib/vanilla.h b/lib/vanilla.h index cffe3d4..70f6b30 100644 --- a/lib/vanilla.h +++ b/lib/vanilla.h @@ -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(); diff --git a/lib/wpa.c b/lib/wpa.c deleted file mode 100644 index 809b897..0000000 --- a/lib/wpa.c +++ /dev/null @@ -1,338 +0,0 @@ -#include "wpa.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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); -} \ No newline at end of file diff --git a/pipe/linux/CMakeLists.txt b/pipe/linux/CMakeLists.txt index 5a34bb3..7b85ec9 100644 --- a/pipe/linux/CMakeLists.txt +++ b/pipe/linux/CMakeLists.txt @@ -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) diff --git a/lib/hostap b/pipe/linux/hostap similarity index 100% rename from lib/hostap rename to pipe/linux/hostap diff --git a/pipe/linux/main.c b/pipe/linux/main.c index b8a4901..9a51c94 100644 --- a/pipe/linux/main.c +++ b/pipe/linux/main.c @@ -1,132 +1,50 @@ #include -#include -#include #include -#include #include -#include -#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 \n", argv[0]); + printf("Usage: %s [args]\n", argv[0]); printf("\n"); printf("Modes: \n"); printf(" -sync Sync/authenticate with the Wii U\n"); printf(" -connect 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; diff --git a/pipe/linux/wpa.c b/pipe/linux/wpa.c new file mode 100644 index 0000000..6be3e39 --- /dev/null +++ b/pipe/linux/wpa.c @@ -0,0 +1,851 @@ +#include "wpa.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} \ No newline at end of file diff --git a/lib/wpa.h b/pipe/linux/wpa.h similarity index 78% rename from lib/wpa.h rename to pipe/linux/wpa.h index 0d9670c..2d55af1 100644 --- a/lib/wpa.h +++ b/pipe/linux/wpa.h @@ -1,12 +1,15 @@ #ifndef VANILLA_WPA_H #define VANILLA_WPA_H +#include #include #include 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