From 13a88f7adbeed04400f50ee38fbf6bf70dbe93d5 Mon Sep 17 00:00:00 2001 From: Steveice10 Date: Fri, 23 Dec 2016 23:10:39 -0800 Subject: [PATCH] Polish up URL sending protocol, adapt servefiles to send URLs over socket. --- servefiles/README.md | 6 +- servefiles/servefiles.bat | 1 - servefiles/servefiles.py | 66 ++++++++--------- source/ui/section/action/action.h | 3 +- source/ui/section/action/installtitledb.c | 2 +- source/ui/section/action/urlinstall.c | 24 +++++- source/ui/section/qrinstall.c | 89 ++++++++++++++++------- 7 files changed, 123 insertions(+), 68 deletions(-) delete mode 100644 servefiles/servefiles.bat diff --git a/servefiles/README.md b/servefiles/README.md index 792fbdf..34423b4 100644 --- a/servefiles/README.md +++ b/servefiles/README.md @@ -1,7 +1,5 @@ # servefiles -Simple Python script for serving local files to FBI's QR code installer. Requires [Python](https://www.python.org/downloads/), [Pillow](https://pypi.python.org/pypi/Pillow), [qrcode](https://pypi.python.org/pypi/qrcode), and [netifaces](https://pypi.python.org/pypi/netifaces). +Simple Python script for serving local files to FBI's remote installer. Requires [Python](https://www.python.org/downloads/). -**Usage**: python servefiles.py (file/directory) - -If you are on Windows, you can drag and drop a CIA file or directory onto **servefiles.bat**. +**Usage**: python servefiles.py (ip) (file/directory) diff --git a/servefiles/servefiles.bat b/servefiles/servefiles.bat deleted file mode 100644 index e8d3881..0000000 --- a/servefiles/servefiles.bat +++ /dev/null @@ -1 +0,0 @@ -python servefiles.py %1 diff --git a/servefiles/servefiles.py b/servefiles/servefiles.py index 8325af0..ab1868a 100644 --- a/servefiles/servefiles.py +++ b/servefiles/servefiles.py @@ -1,33 +1,30 @@ import atexit import os +import socket +import struct import sys import tempfile import threading +import time import urllib -import netifaces -import qrcode - -from PIL import ImageTk - try: from SimpleHTTPServer import SimpleHTTPRequestHandler from SocketServer import TCPServer - from Tkinter import Tk, Frame, Label, BitmapImage from urlparse import urljoin from urllib import pathname2url, quote except ImportError: from http.server import SimpleHTTPRequestHandler from socketserver import TCPServer - from tkinter import Tk, Frame, Label, BitmapImage from urllib.parse import urljoin, quote from urllib.request import pathname2url -if len(sys.argv) < 2: - print("Please specify a file/directory.") +if len(sys.argv) < 3: + print("Usage: " + sys.argv[0] + " ") sys.exit(1) -directory = sys.argv[1] +ip = sys.argv[1] +directory = sys.argv[2] if not os.path.exists(directory): print(directory + ": No such file or directory.") @@ -35,19 +32,19 @@ if not os.path.exists(directory): print("Preparing data...") -baseUrl = netifaces.ifaddresses(netifaces.gateways()['default'][netifaces.AF_INET][1])[2][0]['addr'] + ":8080/" -qrData = "" +baseUrl = [(s.connect(('8.8.8.8', 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1] + ":8080/" +payload = "" if os.path.isfile(directory): if directory.endswith(('.cia', '.tik')): - qrData += baseUrl + quote(os.path.basename(directory)) + payload += baseUrl + quote(os.path.basename(directory)) directory = os.path.dirname(directory) else: for file in [ file for file in next(os.walk(directory))[2] if file.endswith(('.cia', '.tik')) ]: - qrData += baseUrl + quote(file) + "\n" + payload += baseUrl + quote(file) + "\n" -if len(qrData) == 0: +if len(payload) == 0: print("No files to serve.") sys.exit(1) @@ -56,37 +53,38 @@ if not directory == "": print("") print("URLS:") -print(qrData) +print(payload) print("") -print("Generating QR code...") - -try: - qrImage = qrcode.make(qrData, box_size=5) -except qrcode.exceptions.DataOverflowError: - print("Error: URL list too large for a QR code. Try reducing file name lengths or the number of files to send.") - sys.exit(1) - print("Opening HTTP server on port 8080...") server = TCPServer(("", 8080), SimpleHTTPRequestHandler) thread = threading.Thread(target=server.serve_forever) thread.start() -atexit.register(server.shutdown) -print("Displaying QR code...") +try: + print("Sending URL(s) to " + ip + ":5000...") -root = Tk() -root.title("QR Code") + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((ip, 5000)) -frame = Frame(root) -frame.pack() + payloadBytes = bytes(payload, "ascii") + networkPayload = struct.pack('!L', len(payloadBytes)) + payloadBytes -qrBitmap = ImageTk.PhotoImage(qrImage) -qrLabel = Label(frame, image=qrBitmap) -qrLabel.pack() + sentLength = 0 + while sentLength < len(networkPayload): + sent = sock.send(networkPayload[sentLength:]) + if sent == 0: + raise RuntimeError("Socket connection broken.") + + sentLength += sent -root.mainloop() + while len(sock.recv(1)) < 1: + time.sleep(0.05) + + sock.close() +except Exception as e: + print("Error: " + str(e)) print("Shutting down HTTP server...") diff --git a/source/ui/section/action/action.h b/source/ui/section/action/action.h index 35d24d6..8fc659e 100644 --- a/source/ui/section/action/action.h +++ b/source/ui/section/action/action.h @@ -49,5 +49,6 @@ void action_import_secure_value(linked_list* items, list_item* selected); void action_export_secure_value(linked_list* items, list_item* selected); void action_delete_secure_value(linked_list* items, list_item* selected); -void action_url_install(const char* confirmMessage, const char* urls); +void action_url_install(const char* confirmMessage, const char* urls, void* finishedData, void (*finished)(void* data)); + void action_install_titledb(linked_list* items, list_item* selected); \ No newline at end of file diff --git a/source/ui/section/action/installtitledb.c b/source/ui/section/action/installtitledb.c index bcdd590..82ad645 100644 --- a/source/ui/section/action/installtitledb.c +++ b/source/ui/section/action/installtitledb.c @@ -10,5 +10,5 @@ void action_install_titledb(linked_list* items, list_item* selected) { char url[128]; snprintf(url, sizeof(url), "https://api.titledb.com/v0/proxy/%016llX", ((titledb_info*) selected->data)->titleId); - action_url_install("Install the selected title from TitleDB?", url); + action_url_install("Install the selected title from TitleDB?", url, NULL, NULL); } \ No newline at end of file diff --git a/source/ui/section/action/urlinstall.c b/source/ui/section/action/urlinstall.c index 6e5a001..994731d 100644 --- a/source/ui/section/action/urlinstall.c +++ b/source/ui/section/action/urlinstall.c @@ -19,6 +19,9 @@ typedef struct { char urls[URLS_MAX][URL_MAX]; + void* finishedData; + void (*finished)(void* data); + bool cdn; bool cdnDecided; @@ -31,6 +34,14 @@ typedef struct { data_op_data installInfo; } url_install_data; +static void action_url_install_free_data(url_install_data* data) { + if(data->finished != NULL) { + data->finished(data->finishedData); + } + + free(data); +} + static void action_url_install_cdn_check_onresponse(ui_view* view, void* data, bool response) { url_install_data* installData = (url_install_data*) data; @@ -248,6 +259,8 @@ static void action_url_install_install_update(ui_view* view, void* data, float* prompt_display("Success", "Install finished.", COLOR_TEXT, false, NULL, NULL, NULL); } + action_url_install_free_data(installData); + return; } @@ -268,14 +281,18 @@ static void action_url_install_confirm_onresponse(ui_view* view, void* data, boo info_display("Installing From URL(s)", "Press B to cancel.", true, data, action_url_install_install_update, NULL); } else { error_display_res(NULL, NULL, res, "Failed to initiate installation."); + + action_url_install_free_data(installData); } + } else { + action_url_install_free_data(installData); } } -void action_url_install(const char* confirmMessage, const char* urls) { +void action_url_install(const char* confirmMessage, const char* urls, void* finishedData, void (*finished)(void* data)) { url_install_data* data = (url_install_data*) calloc(1, sizeof(url_install_data)); if(data == NULL) { - error_display(NULL, NULL, "Failed to allocate QR install data."); + error_display(NULL, NULL, "Failed to allocate URL install data."); return; } @@ -313,6 +330,9 @@ void action_url_install(const char* confirmMessage, const char* urls) { } } + data->finishedData = finishedData; + data->finished = finished; + data->cdn = false; data->cdnDecided = false; diff --git a/source/ui/section/qrinstall.c b/source/ui/section/qrinstall.c index cc41bf2..ee1e6b8 100644 --- a/source/ui/section/qrinstall.c +++ b/source/ui/section/qrinstall.c @@ -202,7 +202,7 @@ static void remoteinstall_qr_update(ui_view* view, void* data, float* progress, remoteinstall_set_last_urls((const char*) qrData.payload); - action_url_install("Install from the scanned QR code?", (const char*) qrData.payload); + action_url_install("Install from the scanned QR code?", (const char*) qrData.payload, NULL, NULL); return; } } @@ -255,6 +255,7 @@ void remoteinstall_scan_qr_code() { typedef struct { int serverSocket; + int clientSocket; } remoteinstall_network_data; static int remoteinstall_network_recvwait(int sockfd, void* buf, size_t len, int flags) { @@ -269,7 +270,33 @@ static int remoteinstall_network_recvwait(int sockfd, void* buf, size_t len, int return ret < 0 ? ret : (int) read; } +static int remoteinstall_network_sendwait(int sockfd, void* buf, size_t len, int flags) { + errno = 0; + + int ret = 0; + size_t written = 0; + while(((ret = send(sockfd, buf + written, len - written, flags)) >= 0 && (written += ret) < len) || errno == EAGAIN) { + errno = 0; + } + + return ret < 0 ? ret : (int) written; +} + +static void remoteinstall_network_close_client(void* data) { + remoteinstall_network_data* networkData = (remoteinstall_network_data*) data; + + if(networkData->clientSocket != 0) { + u8 ack = 0; + remoteinstall_network_sendwait(networkData->clientSocket, &ack, sizeof(ack), 0); + + close(networkData->clientSocket); + networkData->clientSocket = 0; + } +} + static void remoteinstall_network_free_data(remoteinstall_network_data* data) { + remoteinstall_network_close_client(data); + if(data->serverSocket != 0) { close(data->serverSocket); data->serverSocket = 0; @@ -295,29 +322,44 @@ static void remoteinstall_network_update(ui_view* view, void* data, float* progr int sock = accept(networkData->serverSocket, (struct sockaddr*) &client, &clientLen); if(sock >= 0) { - u32 size = 0; - if(remoteinstall_network_recvwait(sock, &size, sizeof(size), 0) == sizeof(size)) { - if(size < 128 * 1024) { - char* urls = (char*) calloc(size, sizeof(char)); - if(urls != NULL) { - if(remoteinstall_network_recvwait(sock, urls, size, 0) == size) { - action_url_install("Install from the received URL(s)?", urls); - } else { - error_display_errno(NULL, NULL, errno, "Failed to read URL(s)."); - } + networkData->clientSocket = sock; - free(urls); - } else { - error_display(NULL, NULL, "Failed to allocate URL buffer."); - } - } else { - error_display(NULL, NULL, "Payload too large."); - } - } else { + u32 size = 0; + if(remoteinstall_network_recvwait(networkData->clientSocket, &size, sizeof(size), 0) != sizeof(size)) { error_display_errno(NULL, NULL, errno, "Failed to read payload length."); + + remoteinstall_network_close_client(data); + return; } - close(sock); + size = ntohl(size); + if(size >= 128 * 1024) { + error_display(NULL, NULL, "Payload too large."); + + remoteinstall_network_close_client(data); + return; + } + + char* urls = (char*) calloc(size, sizeof(char)); + if(urls == NULL) { + error_display(NULL, NULL, "Failed to allocate URL buffer."); + + remoteinstall_network_close_client(data); + return; + } + + if(remoteinstall_network_recvwait(networkData->clientSocket, urls, size, 0) != size) { + error_display_errno(NULL, NULL, errno, "Failed to read URL(s)."); + + free(urls); + remoteinstall_network_close_client(data); + return; + } + + remoteinstall_set_last_urls(urls); + action_url_install("Install from the received URL(s)?", urls, data, remoteinstall_network_close_client); + + free(urls); } else if(errno != EAGAIN) { if(errno == 22 || errno == 115) { ui_pop(); @@ -355,9 +397,6 @@ void remoteinstall_receive_urls_network() { data->serverSocket = sock; - int bufSize = 1024 * 32; - setsockopt(data->serverSocket, SOL_SOCKET, SO_RCVBUF, &bufSize, sizeof(bufSize)); - struct sockaddr_in server; server.sin_family = AF_INET; server.sin_port = htons(5000); @@ -393,7 +432,7 @@ void remoteinstall_manually_enter_urls() { if(swkbdInputText(&swkbd, textBuf, sizeof(textBuf)) == SWKBD_BUTTON_CONFIRM) { remoteinstall_set_last_urls(textBuf); - action_url_install("Install from the entered URL(s)?", textBuf); + action_url_install("Install from the entered URL(s)?", textBuf, NULL, NULL); return; } } @@ -401,7 +440,7 @@ void remoteinstall_manually_enter_urls() { void remoteinstall_repeat_last_request() { char textBuf[4096]; if(remoteinstall_get_last_urls(textBuf, sizeof(textBuf))) { - action_url_install("Install from the last requested URL(s)?", textBuf); + action_url_install("Install from the last requested URL(s)?", textBuf, NULL, NULL); } else { prompt_display("Failure", "No previously requested URL(s) could be found.", COLOR_TEXT, false, NULL, NULL, NULL); }