#define _XOPEN_SOURCE 600 #include "lupi.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SSL_CTX* ctx = NULL; static int l_open(lua_State *L) { /* TODO: Any mem leaks? */ const char* hostaddr = lua_tostring(L, 1); int port = lua_tonumber(L, 2); struct addrinfo hints, *servinfo, *p; int status; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ((status = getaddrinfo(hostaddr, NULL, &hints, &servinfo)) != 0) { lua_pushnil(L); lua_pushstring(L, gai_strerror(status)); return 2; } int sockfd; for(p = servinfo; p != NULL; p = p->ai_next) { if(p->ai_family == AF_INET) { ((struct sockaddr_in*)p->ai_addr)->sin_port = htons(port); } else { ((struct sockaddr_in6*)p->ai_addr)->sin6_port = htons(port); } if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) { continue; } if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { close(sockfd); continue; } fcntl(sockfd, F_SETFL, O_NONBLOCK); break; } if (p == NULL) { lua_pushnil(L); lua_pushstring(L, "client: failed to connect"); return 2; } freeaddrinfo(servinfo); lua_pushnumber(L, sockfd); return 1; } static int l_write(lua_State *L) { size_t len = 0; int fd = lua_tonumber(L, 1); const char* data = lua_tolstring(L, 2, &len); int total = 0; int n; while(total < len) { n = send(fd, data+total, len, 0); if (n == -1) { if(errno == EPIPE) return 0; } total += n; len -= n; } lua_pushnumber(L, total); return 1; } static int l_read(lua_State *L) { int fd = lua_tonumber(L, 1); int sz = lua_tonumber(L, 2); char *buf = malloc(sz); if ((sz = recv(fd, buf, sz, 0)) == -1) { if(errno == EAGAIN || errno == EWOULDBLOCK) { lua_pushstring(L, ""); return 1; } lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; } lua_pushlstring(L, buf, sz); free(buf); return 1; } /* NOTE THAT AT THIS STAGE THIS IS DEVELOPED IS A WAY THAT WILL /JUST WORK/ AND WON'T BE NECESSARILY SECURE */ struct sslInfo { BIO *web; BIO *out; SSL *ssl; }; static int l_sslOpen(lua_State *L) { const char* hostaddr = lua_tostring(L, 1); const char* hostname = lua_tostring(L, 2); struct sslInfo* info; BIO *web = NULL, *out = NULL; SSL *ssl = NULL; int res = 0; web = BIO_new_ssl_connect(ctx); if(web == NULL) goto fail; res = BIO_set_conn_hostname(web, hostaddr); if(res != 1) goto fail; BIO_get_ssl(web, &ssl); if(ssl == NULL) goto fail; const char* const PREFERRED_CIPHERS = "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4"; res = SSL_set_cipher_list(ssl, PREFERRED_CIPHERS); if(res != 1) goto fail; res = SSL_set_tlsext_host_name(ssl, hostname); if(res != 1) goto fail; out = BIO_new_fp(stdout, BIO_NOCLOSE); if(NULL == out) goto fail; res = BIO_do_connect(web); if(res != 1) goto fail; res = BIO_do_handshake(web); if(res != 1) goto fail; X509* cert = SSL_get_peer_certificate(ssl); if(cert) { X509_free(cert); } if(NULL == cert) goto fail; res = SSL_get_verify_result(ssl); if(res != X509_V_OK) goto fail; goto nofail; fail: lua_pushnil(L); lua_pushstring(L, "Couldn't setup new SSL connection"); if(web) BIO_free_all(web); return 2; nofail: info = lua_newuserdata(L, sizeof(struct sslInfo)); info->web = web; info->out = out; info->ssl = ssl; return 1; } static int ssl_verify(int depth, X509_STORE_CTX *sctx) { return 1; } static void ssl_init() { /* https://wiki.openssl.org/index.php/SSL/TLS_Client */ (void)SSL_library_init(); SSL_load_error_strings(); const SSL_METHOD* method = SSLv23_method(); ctx = SSL_CTX_new(method); SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, ssl_verify); SSL_CTX_set_verify_depth(ctx, 4); const long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION; SSL_CTX_set_options(ctx, flags); } void internet_start(lua_State *L) { ssl_init(); signal(SIGPIPE, SIG_IGN); lua_createtable (L, 0, 1); pushctuple(L, "open", l_open); pushctuple(L, "write", l_write); pushctuple(L, "read", l_read); lua_setglobal(L, "net"); }