From e7ff54d58c6d10366b1a8bf34da1487197cbaf35 Mon Sep 17 00:00:00 2001 From: Firew0lf Date: Sun, 10 Apr 2016 01:47:52 +0200 Subject: [PATCH] Added SSL support to sockets, Added console/stdout support, Removed apt.init/apt.shutdown, Added some SSL options to httpc The console can __not__ be used with gfx.start() on a screen. You don't have to gfx.render() while print()ing, but you should do it when you are in the main loop. The SSL sockets don't work with Citra. --- source/apt.c | 32 ++------ source/cfgu.c | 21 ++++- source/ctr.c | 12 ++- source/gfx.c | 49 ++++++++++++ source/httpc.c | 21 +++++ source/news.c | 21 ++++- source/ptm.c | 25 +++++- source/socket.c | 197 +++++++++++++++++++++++++++++++++++++++++++---- source/texture.c | 30 ++++---- 9 files changed, 333 insertions(+), 75 deletions(-) diff --git a/source/apt.c b/source/apt.c index 99a1fd1..e860736 100644 --- a/source/apt.c +++ b/source/apt.c @@ -13,30 +13,6 @@ Used to manage the applets and application status. #include #include -/*** -Initialize the APT module. Useless. -@function init -*/ -static int apt_init(lua_State *L) { - Result ret = aptInit(); - if (ret!=0) { - lua_pushboolean(L, false); - lua_pushinteger(L, ret); - return 2; - } - lua_pushboolean(L, true); - return 1; -} - -/*** -Shutdown the APT module. Useless, don't use it. -@function shutdown -*/ -static int apt_shutdown(lua_State *L) { - aptExit(); - return 0; -} - /*** Open an APT session. Should only work if you don't use the homebrew menu. @function openSession @@ -132,8 +108,6 @@ static int apt_getMenuAppID(lua_State *L) { } static const struct luaL_Reg apt_lib[] = { - {"init", apt_init }, - {"shutdown", apt_shutdown }, {"openSession", apt_openSession }, {"closeSession", apt_closeSession }, {"setStatus", apt_setStatus }, @@ -327,6 +301,8 @@ struct { char *name; int value; } apt_constants[] = { }; int luaopen_apt_lib(lua_State *L) { + aptInit(); + luaL_newlib(L, apt_lib); for (int i = 0; apt_constants[i].name; i++) { @@ -340,3 +316,7 @@ int luaopen_apt_lib(lua_State *L) { void load_apt_lib(lua_State *L) { luaL_requiref(L, "ctr.apt", luaopen_apt_lib, false); } + +void unload_apt_lib(lua_State *L) { + aptExit(); +} diff --git a/source/cfgu.c b/source/cfgu.c index 1700e47..5b2e23b 100644 --- a/source/cfgu.c +++ b/source/cfgu.c @@ -14,13 +14,17 @@ Used to get some user config. #include #include +bool initStateCFGU = false; + /*** Initialize the CFGU module. @function init */ static int cfgu_init(lua_State *L) { - cfguInit(); - + if (!initStateCFGU) { + cfguInit(); + initStateCFGU = true; + } return 0; } @@ -29,8 +33,10 @@ Disable the CFGU module. @function shutdown */ static int cfgu_shutdown(lua_State *L) { - cfguExit(); - + if (initStateCFGU) { + cfguExit(); + initStateCFGU = false; + } return 0; } @@ -309,3 +315,10 @@ int luaopen_cfgu_lib(lua_State *L) { void load_cfgu_lib(lua_State *L) { luaL_requiref(L, "ctr.cfgu", luaopen_cfgu_lib, false); } + +void unload_cfgu_lib(lua_State *L) { + if (initStateCFGU) { + initStateCFGU = false; + cfguExit(); + } +} diff --git a/source/ctr.c b/source/ctr.c index 49c93ba..366fc10 100644 --- a/source/ctr.c +++ b/source/ctr.c @@ -28,6 +28,7 @@ The `ctr.news` module. @see ctr.news */ void load_news_lib(lua_State *L); +void unload_news_lib(lua_State *L); /*** The `ctr.ptm` module. @@ -35,6 +36,7 @@ The `ctr.ptm` module. @see ctr.ptm */ void load_ptm_lib(lua_State *L); +void unload_ptm_lib(lua_State *L); /*** The `ctr.hid` module. @@ -80,6 +82,7 @@ The `ctr.cfgu` module. @see ctr.cfgu */ void load_cfgu_lib(lua_State *L); +void unload_cfgu_lib(lua_State *L); /*** The `ctr.socket` module. @@ -109,6 +112,7 @@ The `ctr.apt` module. @see ctr.apt */ void load_apt_lib(lua_State *L); +void unload_apt_lib(lua_State *L); /*** The `ctr.mic` module. @@ -168,18 +172,18 @@ static const struct luaL_Reg ctr_lib[] = { // Subtables struct { char *name; void (*load)(lua_State *L); void (*unload)(lua_State *L); } ctr_libs[] = { { "gfx", load_gfx_lib, unload_gfx_lib }, - { "news", load_news_lib, NULL }, - { "ptm", load_ptm_lib, NULL }, + { "news", load_news_lib, unload_news_lib }, + { "ptm", load_ptm_lib, unload_ptm_lib }, { "hid", load_hid_lib, unload_hid_lib }, { "ir", load_ir_lib, NULL }, { "fs", load_fs_lib, unload_fs_lib }, { "httpc", load_httpc_lib, unload_httpc_lib }, { "qtm", load_qtm_lib, NULL }, - { "cfgu", load_cfgu_lib, NULL }, + { "cfgu", load_cfgu_lib, unload_cfgu_lib }, { "socket", load_socket_lib, NULL }, { "cam", load_cam_lib, NULL }, { "audio", load_audio_lib, unload_audio_lib }, - { "apt", load_apt_lib, NULL }, + { "apt", load_apt_lib, unload_apt_lib }, { "mic", load_mic_lib, NULL }, { "thread", load_thread_lib, NULL }, { NULL, NULL, NULL } diff --git a/source/gfx.c b/source/gfx.c index ed6bfe5..82342bb 100644 --- a/source/gfx.c +++ b/source/gfx.c @@ -12,6 +12,7 @@ The `gfx` module. //#include <3ds/vram.h> //#include <3ds/services/gsp.h> +#include <3ds/console.h> #include #include @@ -524,6 +525,51 @@ static int gfx_target___index(lua_State *L) { return 1; } +/*** +Initialize the console. You can print on it using print(), or any function that normally outputs to stdout. +Warning: you can't use a screen for both a console and drawing, you have to disable the console first. +@function console +@tparam[opt=gfx.TOP] number screen screen to draw the console on. +@tparam[opt=true] boolean debug enable stderr output on the console +*/ +u8 consoleScreen = GFX_TOP; +static int gfx_console(lua_State *L) { + consoleScreen = luaL_optinteger(L, 1, GFX_TOP); + bool err = false; + if (lua_isboolean(L, 2)) { + err = lua_toboolean(L, 2); + } + + consoleInit(consoleScreen, NULL); + if (err) + consoleDebugInit(debugDevice_CONSOLE); + + return 0; +} + +/*** +Clear the console. +@function clearConsole +*/ +static int gfx_clearConsole(lua_State *L) { + consoleClear(); + + return 0; +} + +/*** +Disable the console. +@function disableConsole +*/ +static int gfx_disableConsole(lua_State *L) { + gfxSetScreenFormat(consoleScreen, GSP_BGR8_OES); + gfxSetDoubleBuffering(consoleScreen, true); + gfxSwapBuffersGpu(); + gspWaitForVBlank(); + + return 0; +} + // Functions static const struct luaL_Reg gfx_lib[] = { { "start", gfx_start }, @@ -546,6 +592,9 @@ static const struct luaL_Reg gfx_lib[] = { { "getTextSize", gfx_getTextSize }, { "scissor", gfx_scissor }, { "target", gfx_target }, + { "console", gfx_console }, + { "clearConsole", gfx_clearConsole }, + { "disableConsole", gfx_disableConsole }, { NULL, NULL } }; diff --git a/source/httpc.c b/source/httpc.c index 1df43bd..ef70afc 100644 --- a/source/httpc.c +++ b/source/httpc.c @@ -9,6 +9,7 @@ The `httpc` module. #include <3ds.h> #include <3ds/types.h> #include <3ds/services/httpc.h> +#include <3ds/services/sslc.h> #include #include @@ -239,6 +240,25 @@ static int httpc_addTrustedRootCA(lua_State *L) { return 1; } +/*** +Set SSL options for a context. +@function :setSSLOptions +@tparam boolean disableVerify disable server certificate verification if `true` +@tparam[opt=false] boolean tlsv10 use TLS v1.0 if `true` +*/ +static int httpc_setSSLOptions(lua_State *L) { + httpcContext *context = lua_touserdata(L, 1); + + bool disVer = lua_toboolean(L, 2); + bool tsl10 = false; + if (lua_isboolean(L, 3)) + tsl10 = lua_toboolean(L, 3); + + httpcSetSSLOpt(context, (disVer?SSLCOPT_DisableVerify:0)|(tsl10?SSLCOPT_TLSv10:0)); + + return 0; +} + // object static const struct luaL_Reg httpc_methods[] = { {"open", httpc_open }, @@ -252,6 +272,7 @@ static const struct luaL_Reg httpc_methods[] = { {"addPostData", httpc_addPostData }, {"getResponseHeader", httpc_getResponseHeader }, {"addTrustedRootCA", httpc_addTrustedRootCA }, + {"setSSLOptions", httpc_setSSLOptions }, {NULL, NULL} }; diff --git a/source/news.c b/source/news.c index f97e6d9..5afdcbe 100644 --- a/source/news.c +++ b/source/news.c @@ -13,13 +13,17 @@ The `news` module. #include #include +bool initStateNews = false; + /*** Initialize the news module. @function init */ static int news_init(lua_State *L) { - newsInit(); - + if (!initStateNews) { + newsInit(); + initStateNews = true; + } return 0; } @@ -63,8 +67,10 @@ Disable the news module. @function shutdown */ static int news_shutdown(lua_State *L) { - newsExit(); - + if (initStateNews) { + newsExit(); + initStateNews = false; + } return 0; } @@ -83,3 +89,10 @@ int luaopen_news_lib(lua_State *L) { void load_news_lib(lua_State *L) { luaL_requiref(L, "ctr.news", luaopen_news_lib, 0); } + +void unload_news_lib(lua_State *L) { + if (initStateNews) { + newsExit(); + initStateNews = false; + } +} diff --git a/source/ptm.c b/source/ptm.c index 8a902e9..438acde 100644 --- a/source/ptm.c +++ b/source/ptm.c @@ -10,13 +10,19 @@ The `ptm` module. #include #include +bool initStatePTM = false; + /*** Initialize the PTM module. @function init */ static int ptm_init(lua_State *L) { - ptmuInit(); - ptmSysmInit(); + if (!initStatePTM) { + ptmuInit(); + ptmSysmInit(); + + initStatePTM = true; + } return 0; } @@ -26,8 +32,12 @@ Disable the PTM module. @function shutdown */ static int ptm_shutdown(lua_State *L) { - ptmuExit(); - ptmSysmExit(); + if (initStatePTM) { + ptmuExit(); + ptmSysmExit(); + + initStatePTM = false; + } return 0; } @@ -146,3 +156,10 @@ int luaopen_ptm_lib(lua_State *L) { void load_ptm_lib(lua_State *L) { luaL_requiref(L, "ctr.ptm", luaopen_ptm_lib, 0); } + +void unload_ptm_lib(lua_State *L) { + if (initStatePTM) { + ptmuExit(); + ptmSysmExit(); + } +} diff --git a/source/socket.c b/source/socket.c index c4eef76..fc2325d 100644 --- a/source/socket.c +++ b/source/socket.c @@ -8,6 +8,7 @@ The UDP part is only without connection. #include <3ds.h> #include <3ds/types.h> #include <3ds/services/soc.h> +#include <3ds/services/sslc.h> #include #include @@ -15,7 +16,9 @@ The UDP part is only without connection. #include #include #include +#include #include +#include #include #include #include @@ -26,21 +29,58 @@ typedef struct { int socket; struct sockaddr_in addr; struct hostent *host; // only used for client sockets + sslcContext sslContext; + bool isSSL; } socket_userdata; +bool initStateSocket = false; + +u32 rootCertChain = 0; + /*** Initialize the socket module @function init @tparam[opt=0x100000] number buffer size (in bytes), must be a multiple of 0x1000 */ static int socket_init(lua_State *L) { - u32 size = luaL_optinteger(L, 1, 0x100000); - Result ret = socInit((u32*)memalign(0x1000, size), size); + if (!initStateSocket) { + u32 size = luaL_optinteger(L, 1, 0x100000); + if (size%0x1000 != 0) { + lua_pushboolean(L, false); + lua_pushstring(L, "Not a multiple of 0x1000"); + return 2; + } + + u32* mem = (u32*)memalign(0x1000, size); + if (mem == NULL) { + lua_pushboolean(L, false); + lua_pushstring(L, "Failed to allocate memory"); + return 2; + } + + Result ret = socInit(mem, size); - if (ret) { - lua_pushboolean(L, false); - lua_pushinteger(L, ret); - return 2; + if (ret) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + + ret = sslcInit(0); + if (R_FAILED(ret)) { + lua_pushboolean(L, false); + lua_pushinteger(L, ret); + return 2; + } + + sslcCreateRootCertChain(&rootCertChain); + sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_CyberTrust, NULL); + sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_AddTrust_External_CA, NULL); + sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_COMODO, NULL); + sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_USERTrust, NULL); + sslcRootCertChainAddDefaultCert(rootCertChain, SSLC_DefaultRootCert_DigiCert_EV, NULL); + + initStateSocket = true; } lua_pushboolean(L, true); @@ -52,8 +92,11 @@ Disable the socket module. Must be called before exiting ctrµLua. @function shutdown */ static int socket_shutdown(lua_State *L) { + sslcDestroyRootCertChain(rootCertChain); + sslcExit(); socExit(); + initStateSocket = false; return 0; } @@ -76,6 +119,8 @@ static int socket_tcp(lua_State *L) { userdata->addr.sin_family = AF_INET; + userdata->isSSL = false; + return 1; } @@ -92,15 +137,36 @@ static int socket_udp(lua_State *L) { userdata->socket = socket(AF_INET, SOCK_DGRAM, 0); if (userdata->socket < 0) { lua_pushnil(L); - lua_pushstring(L, "Failed to create a TCP socket"); + lua_pushstring(L, strerror(errno)); return 2; } + fcntl(userdata->socket, F_SETFL, O_NONBLOCK); userdata->addr.sin_family = AF_INET; return 1; } +/*** +Add a trusted root CA to the certChain. +@function addTrustedRootCA +@tparam string cert DER cert +*/ +static int socket_addTrustedRootCA(lua_State *L) { + size_t size = 0; + const char* cert = luaL_checklstring(L, 1, &size); + + Result ret = sslcAddTrustedRootCA(rootCertChain, (u8*)cert, size, NULL); + if (R_FAILED(ret)) { + lua_pushnil(L); + lua_pushinteger(L, ret); + return 2; + } + + lua_pushboolean(L, true); + return 1; +} + /*** All sockets @section sockets @@ -130,6 +196,10 @@ Close an existing socket. static int socket_close(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + if (userdata->isSSL) { + sslcDestroyContext(&userdata->sslContext); + } + closesocket(userdata->socket); return 0; @@ -158,6 +228,7 @@ static int socket_accept(lua_State *L) { lua_pushnil(L); return 1; } + fcntl(client->socket, F_SETFL, O_NONBLOCK); return 1; } @@ -167,6 +238,7 @@ Connect a socket to a server. The TCP object becomes a TCPClient object. @function :connect @tparam string host address of the host @tparam number port port of the server +@tparam[opt=false] boolean ssl use SSL if `true` @treturn[1] boolean true if success @treturn[2] boolean false if failed @treturn[2] string error string @@ -175,11 +247,12 @@ static int socket_connect(lua_State *L) { socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); char *addr = (char*)luaL_checkstring(L, 2); int port = luaL_checkinteger(L, 3); + bool ssl = lua_toboolean(L, 4); userdata->host = gethostbyname(addr); if (userdata->host == NULL) { lua_pushnil(L); - lua_pushstring(L, "No such host"); + lua_pushstring(L, strerror(errno)); return 2; } @@ -188,10 +261,23 @@ static int socket_connect(lua_State *L) { if (connect(userdata->socket, (const struct sockaddr*)&userdata->addr, sizeof(userdata->addr)) < 0) { lua_pushnil(L); - lua_pushstring(L, "Connection failed"); + lua_pushstring(L, strerror(errno)); return 2; } + if (ssl) { // SSL context setup + sslcCreateContext(&userdata->sslContext, userdata->socket, SSLCOPT_Default, addr); + sslcContextSetRootCertChain(&userdata->sslContext, rootCertChain); + if (R_FAILED(sslcStartConnection(&userdata->sslContext, NULL, NULL))) { + sslcDestroyContext(&userdata->sslContext); + lua_pushnil(L); + lua_pushstring(L, "SSL connection failed"); + return 2; + } + userdata->isSSL = true; + } + fcntl(userdata->socket, F_SETFL, O_NONBLOCK); + lua_pushboolean(L, 1); return 1; } @@ -237,7 +323,11 @@ static int socket_receive(lua_State *L) { luaL_buffinit(L, &b); char buff; - while (recv(userdata->socket, &buff, 1, flags) > 0 && buff != '\n') luaL_addchar(&b, buff); + if (!userdata->isSSL) { + while (recv(userdata->socket, &buff, 1, flags) > 0 && buff != '\n') luaL_addchar(&b, buff); + } else { + while (!R_FAILED(sslcRead(&userdata->sslContext, &buff, 1, false)) && buff != '\n') luaL_addchar(&b, buff); + } luaL_pushresult(&b); return 1; @@ -247,7 +337,11 @@ static int socket_receive(lua_State *L) { luaL_buffinit(L, &b); char buff; - while (buff != '\n' && recv(userdata->socket, &buff, 1, flags) > 0) luaL_addchar(&b, buff); + if (!userdata->isSSL) { + while (buff != '\n' && recv(userdata->socket, &buff, 1, flags) > 0) luaL_addchar(&b, buff); + } else { + while (buff != '\n' && !R_FAILED(sslcRead(&userdata->sslContext, &buff, 1, false))) luaL_addchar(&b, buff); + } luaL_pushresult(&b); return 1; @@ -258,7 +352,17 @@ static int socket_receive(lua_State *L) { } char *buff = malloc(count+1); - int len = recv(userdata->socket, buff, count, flags); + int len; + if (!userdata->isSSL) { + len = recv(userdata->socket, buff, count, flags); + } else { + len = sslcRead(&userdata->sslContext, buff, count, false); + if (R_FAILED(len)) { + lua_pushnil(L); + lua_pushinteger(L, len); + return 2; + } + } *(buff+len) = 0x0; // text end lua_pushstring(L, buff); @@ -276,12 +380,68 @@ static int socket_send(lua_State *L) { size_t size = 0; char *data = (char*)luaL_checklstring(L, 2, &size); - size_t sent = send(userdata->socket, data, size, 0); + size_t sent; + if (!userdata->isSSL) { + sent = send(userdata->socket, data, size, 0); + } else { + sent = sslcWrite(&userdata->sslContext, data, size); + if (R_FAILED(sent)) { + lua_pushnil(L); + lua_pushinteger(L, sent); + return 2; + } + } + + if (sent < 0) { + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + return 2; + } lua_pushinteger(L, sent); return 1; } +/*** +Get some informations from a socket. +@function :getpeername +@treturn string IP +@treturn number port +*/ +static int socket_getpeername(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + + struct sockaddr_in addr; + socklen_t addrSize = sizeof(addr); + + getpeername(userdata->socket, (struct sockaddr*)&addr, &addrSize); + + lua_pushstring(L, inet_ntoa(addr.sin_addr)); + lua_pushinteger(L, ntohs(addr.sin_port)); + + return 2; +} + +/*** +Get some local informations from a socket. +@function :getsockname +@treturn string IP +@treturn number port +*/ +static int socket_getsockname(lua_State *L) { + socket_userdata *userdata = luaL_checkudata(L, 1, "LSocket"); + + struct sockaddr_in addr; + socklen_t addrSize = sizeof(addr); + + getsockname(userdata->socket, (struct sockaddr*)&addr, &addrSize); + + lua_pushstring(L, inet_ntoa(addr.sin_addr)); + lua_pushinteger(L, ntohs(addr.sin_port)); + + return 2; +} + /*** UDP sockets @section UDP @@ -360,10 +520,11 @@ static int socket_sendto(lua_State *L) { // module functions static const struct luaL_Reg socket_functions[] = { - {"init", socket_init }, - {"shutdown", socket_shutdown}, - {"tcp", socket_tcp }, - {"udp", socket_udp }, + {"init", socket_init }, + {"shutdown", socket_shutdown }, + {"tcp", socket_tcp }, + {"udp", socket_udp }, + {"addTrustedRootCA", socket_addTrustedRootCA}, {NULL, NULL} }; @@ -379,6 +540,8 @@ static const struct luaL_Reg socket_methods[] = { {"receivefrom", socket_receivefrom}, {"send", socket_send }, {"sendto", socket_sendto }, + {"getpeername", socket_getpeername}, + {"getsockname", socket_getsockname}, {NULL, NULL} }; diff --git a/source/texture.c b/source/texture.c index 6468da6..df87a22 100644 --- a/source/texture.c +++ b/source/texture.c @@ -280,23 +280,10 @@ static int texture_save(lua_State *L) { const char* path = luaL_checkstring(L, 2); u8 type = luaL_optinteger(L, 3, 0); - u32* buff = malloc(texture->texture->width * texture->texture->height * 4); - if (buff == NULL) { - lua_pushnil(L); - lua_pushstring(L, "Failed to allocate buffer"); - return 2; - } - for (int y=0;ytexture->height;y++) { - for (int x=0;xtexture->width;x++) { - buff[x+(y*texture->texture->width)] = __builtin_bswap32(sf2d_get_pixel(texture->texture, x, y)); - } - } - int result = 0; if (type == 0) { // PNG FILE* file = fopen(path, "wb"); if (file == NULL) { - free(buff); lua_pushnil(L); lua_pushstring(L, "Can open file"); return 2; @@ -313,7 +300,7 @@ static int texture_save(lua_State *L) { for(int y=0;ytexture->height;y++) { for (int x=0;xtexture->width;x++) { - ((u32*)row)[x] = buff[x+(y*texture->texture->width)]; + ((u32*)row)[x] = __builtin_bswap32(sf2d_get_pixel(texture->texture, x, y)); } png_write_row(png, row); } @@ -328,14 +315,25 @@ static int texture_save(lua_State *L) { result = 1; } else if (type == 2) { // BMP + u32* buff = malloc(texture->texture->width * texture->texture->height * 4); + if (buff == NULL) { + lua_pushnil(L); + lua_pushstring(L, "Failed to allocate buffer"); + return 2; + } + for (int y=0;ytexture->height;y++) { + for (int x=0;xtexture->width;x++) { + buff[x+(y*texture->texture->width)] = __builtin_bswap32(sf2d_get_pixel(texture->texture, x, y)); + } + } result = stbi_write_bmp(path, texture->texture->width, texture->texture->height, 4, buff); - } else { free(buff); + + } else { lua_pushnil(L); lua_pushstring(L, "Not a valid type"); return 2; } - free(buff); if (result == 0) { lua_pushnil(L);