From 930c847e78aeb559d374dd09b5c2b65d35561c57 Mon Sep 17 00:00:00 2001 From: Michael Ortmann <41313082+michaelortmann@users.noreply.github.com> Date: Tue, 7 Feb 2023 02:01:00 +0100 Subject: [PATCH 01/21] Initial commit --- eggdrop-basic.conf | 1 + eggdrop.conf | 7 + src/dcc.c | 166 +++++++----- src/eggdrop.h | 5 + src/main.c | 4 - src/mod/module.h | 3 + src/mod/modvals.h | 63 ++--- src/mod/webui.mod/Makefile | 47 ++++ src/mod/webui.mod/webui.c | 489 +++++++++++++++++++++++++++++++++++ src/mod/webui.mod/webui.html | 43 +++ src/modules.c | 29 ++- src/net.c | 8 + src/proto.h | 5 + src/tcldcc.c | 6 +- src/tls.c | 120 +++++---- text/webui.html | 51 ++++ 16 files changed, 897 insertions(+), 150 deletions(-) create mode 100644 src/mod/webui.mod/Makefile create mode 100644 src/mod/webui.mod/webui.c create mode 100644 src/mod/webui.mod/webui.html create mode 100644 text/webui.html diff --git a/eggdrop-basic.conf b/eggdrop-basic.conf index d14753d0e..0e07f5e2f 100755 --- a/eggdrop-basic.conf +++ b/eggdrop-basic.conf @@ -37,6 +37,7 @@ loadmodule console ; # Console setting storage loadmodule uptime ; # Centralized uptime stat collection (http://uptime.eggheads.org) #loadmodule ident ; # Ident support #loadmodule twitch ; # Twitch gaming service support +#loadmodule webui ; # WebUI support ##### BASIC SETTINGS ##### diff --git a/eggdrop.conf b/eggdrop.conf index 1b8de6651..9ba0f4264 100755 --- a/eggdrop.conf +++ b/eggdrop.conf @@ -1680,6 +1680,13 @@ loadmodule uptime # Set the ident port to use for ident-method 1. #set ident-port 113 + +#### WEBUI MODULE #### + +#loadmodule webui + +#listen +8080 webui + ##### AUTOSCRIPTS ##### # Load this script to enable the autoscripts functionality for Eggdrop. diff --git a/src/dcc.c b/src/dcc.c index 77cdb7e29..c10fd22dc 100644 --- a/src/dcc.c +++ b/src/dcc.c @@ -732,6 +732,8 @@ static void dcc_chat_pass(int idx, char *buf, int atr) /* Turn echo back on for telnet sessions (send IAC WON'T ECHO). */ if (dcc[idx].status & STAT_TELNET) tputs(dcc[idx].sock, TLN_IAC_C TLN_WONT_C TLN_ECHO_C "\n", 4); + else if (dcc[idx].status & STAT_WS) + tputs(dcc[idx].sock, WS_ECHO_ON, 1); dcc_chatter(idx); } } else { @@ -1292,11 +1294,18 @@ static void dcc_telnet(int idx, char *buf, int i) */ if (!(tls_vfyclients & TLS_VERIFYCN)) threaddata()->socklist[findsock(sock)].flags |= SOCK_VIRTUAL; - else if (ssl_handshake(dcc[i].sock, TLS_LISTEN, tls_vfyclients, - LOG_MISC, NULL, NULL)) { - killsock(dcc[i].sock); - lostdcc(i); - return; + else { +#ifdef TLS + if (!strcmp(dcc[idx].nick, "(webui)")) + ssl_cleanup(); /* reset ssl_ctx for websocket */ +#endif /* TLS */ + if (ssl_handshake(dcc[i].sock, TLS_LISTEN, + strcmp(dcc[idx].nick, "(webui)") ? tls_vfyclients : 0, + LOG_MISC, NULL, NULL)) { + killsock(dcc[i].sock); + lostdcc(i); + return; + } } } #endif @@ -1310,12 +1319,68 @@ static void dcc_telnet(int idx, char *buf, int i) dcc_dnshostbyip(&dcc[i].sockname); } +/* we need this for dcc_telnet_hostresolved() could now branch to DCC_TABLE + * and for either branch we need to continue here + */ +void dcc_telnet_hostresolved2(int i, int idx) { + int j, sock; + char *userhost = dcc[idx].host; /* TODO: writing host back to userhost looks like back and forth copying */ + /* Skip ident lookup if disabled */ + if (identtimeout <= 0) { + dcc[i].u.ident_sock = dcc[idx].sock; + dcc_telnet_got_ident(i, userhost); + return; + } + + changeover_dcc(i, &DCC_IDENTWAIT, 0); + dcc[i].timeval = now; + dcc[i].u.ident_sock = dcc[idx].sock; + sock = -1; + j = new_dcc(&DCC_IDENT, 0); + if (j < 0) + putlog(LOG_MISC, "*", DCC_IDENTFAIL, dcc[i].host, strerror(errno)); + else { + memcpy(&dcc[j].sockname, &dcc[i].sockname, sizeof(sockname_t)); + dcc[j].sock = getsock(dcc[j].sockname.family, 0); + if (dcc[j].sock >= 0) { + sockname_t name; + name.addrlen = sizeof(name.addr); + if (getsockname(dcc[i].sock, &name.addr.sa, &name.addrlen) < 0) + debug2("dcc: dcc_telnet_hostresolved(): getsockname() socket %ld error %s", dcc[i].sock, strerror(errno)); + setsnport(name, 0); + if (bind(dcc[j].sock, &name.addr.sa, name.addrlen) < 0) + debug2("dcc: dcc_telnet_hostresolved(): bind() socket %ld error %s", dcc[j].sock, strerror(errno)); + setsnport(dcc[j].sockname, 113); + if (connect(dcc[j].sock, &dcc[j].sockname.addr.sa, + dcc[j].sockname.addrlen) < 0 && (errno != EINPROGRESS)) { + killsock(dcc[j].sock); + lostdcc(j); + putlog(LOG_MISC, "*", DCC_IDENTFAIL, dcc[i].host, strerror(errno)); + j = 0; + } + sock = dcc[j].sock; + } + } + if (j < 0) { + dcc_telnet_got_ident(i, userhost); + return; + } + dcc[j].sock = sock; + dcc[j].port = 113; + dcc[j].addr = dcc[i].addr; + strcpy(dcc[j].host, dcc[i].host); + strcpy(dcc[j].nick, "*"); + dcc[j].u.ident_sock = dcc[i].sock; + dcc[j].timeval = now; + dprintf(j, "%d, %d\n", dcc[i].port, dcc[idx].port); +} + static void dcc_telnet_hostresolved(int i) { int idx; - int j = 0, sock; char s[sizeof lasttelnethost], *userhost; + debug0("dcc_telnet_hostresolved()"); strlcpy(dcc[i].host, dcc[i].u.dns->host, UHOSTLEN); for (idx = 0; idx < dcc_total; idx++) @@ -1374,54 +1439,16 @@ static void dcc_telnet_hostresolved(int i) return; } - /* Skip ident lookup if disabled */ - if (identtimeout <= 0) { - dcc[i].u.ident_sock = dcc[idx].sock; - dcc_telnet_got_ident(i, userhost); +#ifdef TLS + /* Skip ident lookup for webui http */ + if (!strcmp(dcc[idx].nick, "(webui)")) { + webui_dcc_telnet_hostresolved(i); return; } +#endif /* TLS */ - changeover_dcc(i, &DCC_IDENTWAIT, 0); - dcc[i].timeval = now; - dcc[i].u.ident_sock = dcc[idx].sock; - sock = -1; - j = new_dcc(&DCC_IDENT, 0); - if (j < 0) - putlog(LOG_MISC, "*", DCC_IDENTFAIL, dcc[i].host, strerror(errno)); - else { - memcpy(&dcc[j].sockname, &dcc[i].sockname, sizeof(sockname_t)); - dcc[j].sock = getsock(dcc[j].sockname.family, 0); - if (dcc[j].sock >= 0) { - sockname_t name; - name.addrlen = sizeof(name.addr); - if (getsockname(dcc[i].sock, &name.addr.sa, &name.addrlen) < 0) - debug2("dcc: dcc_telnet_hostresolved(): getsockname() socket %ld error %s", dcc[i].sock, strerror(errno)); - setsnport(name, 0); - if (bind(dcc[j].sock, &name.addr.sa, name.addrlen) < 0) - debug2("dcc: dcc_telnet_hostresolved(): bind() socket %ld error %s", dcc[j].sock, strerror(errno)); - setsnport(dcc[j].sockname, 113); - if (connect(dcc[j].sock, &dcc[j].sockname.addr.sa, - dcc[j].sockname.addrlen) < 0 && (errno != EINPROGRESS)) { - killsock(dcc[j].sock); - lostdcc(j); - putlog(LOG_MISC, "*", DCC_IDENTFAIL, dcc[i].host, strerror(errno)); - j = 0; - } - sock = dcc[j].sock; - } - } - if (j < 0) { - dcc_telnet_got_ident(i, userhost); - return; - } - dcc[j].sock = sock; - dcc[j].port = 113; - dcc[j].addr = dcc[i].addr; - strcpy(dcc[j].host, dcc[i].host); - strcpy(dcc[j].nick, "*"); - dcc[j].u.ident_sock = dcc[i].sock; - dcc[j].timeval = now; - dprintf(j, "%d, %d\n", dcc[i].port, dcc[idx].port); + strlcpy(dcc[i].host, userhost, UHOSTLEN); + dcc_telnet_hostresolved2(i, idx); } static void eof_dcc_telnet(int idx) @@ -1551,7 +1578,6 @@ static void dcc_telnet_id(int idx, char *buf, int atr) int ok = 0; struct flag_record fr = { FR_GLOBAL | FR_CHAN | FR_ANYWH, 0, 0, 0, 0, 0 }; struct dcc_table *old = dcc[idx].type; - if (detect_telnet((unsigned char *) buf)) { dcc[idx].status |= STAT_TELNET; strip_telnet(dcc[idx].sock, buf, &atr); @@ -1808,14 +1834,20 @@ static void dcc_telnet_pass(int idx, int atr) * */ - /* Turn off remote telnet echo (send IAC WILL ECHO). */ + /* Turn off remote telnet echo */ + char buf[512]; if (dcc[idx].status & STAT_TELNET) { - char buf[1030]; + /* For telnet sessions send IAC WILL ECHO */ egg_snprintf(buf, sizeof buf, "\n%s%s\r\n", escape_telnet(DCC_ENTERPASS), TLN_IAC_C TLN_WILL_C TLN_ECHO_C); tputs(dcc[idx].sock, buf, strlen(buf)); - } else + } else if (dcc[idx].status & STAT_WS) { + /* For webui sessions */ + snprintf(buf, sizeof buf, "\n%s" WS_ECHO_OFF "\n", DCC_ENTERPASS); + tputs(dcc[idx].sock, buf, strlen(buf)); + } else { dprintf(idx, "\n%s\n", DCC_ENTERPASS); + } } } @@ -2387,7 +2419,6 @@ static void dcc_telnet_got_ident(int i, char *host) /* Do not buffer data anymore. All received and stored data is passed * over to the dcc functions from now on. */ sockoptions(dcc[i].sock, EGG_OPTION_UNSET, SOCK_BUFFER); - dcc[i].type = &DCC_TELNET_ID; dcc[i].u.chat = get_data_ptr(sizeof(struct chat_info)); egg_bzero(dcc[i].u.chat, sizeof(struct chat_info)); @@ -2396,17 +2427,20 @@ static void dcc_telnet_got_ident(int i, char *host) * STATUS option as a hopefully harmless way to detect if the other * side is a telnet client or not. */ #ifdef TLS - if (!dcc[i].ssl) - dprintf(i, TLN_IAC_C TLN_WILL_C TLN_STATUS_C); + if (!dcc[i].ssl && strcmp(dcc[idx].nick, "(webui)")) + dprintf(i, TLN_IAC_C TLN_WILL_C TLN_STATUS_C); #endif - /* Copy acceptable-nick/host mask */ - dcc[i].status = STAT_TELNET | STAT_ECHO; - if (!strcmp(dcc[idx].nick, "(bots)")) - dcc[i].status |= STAT_BOTONLY; - if (!strcmp(dcc[idx].nick, "(users)")) - dcc[i].status |= STAT_USRONLY; - /* Copy acceptable-nick/host mask */ - strlcpy(dcc[i].nick, dcc[idx].host, HANDLEN); + /* Copy acceptable-nick/host mask */ + dcc[i].status = STAT_TELNET | STAT_ECHO; + if (!strcmp(dcc[idx].nick, "(users)")) + dcc[i].status |= STAT_USRONLY; + else if (!strcmp(dcc[idx].nick, "(bots)")) + dcc[i].status |= STAT_BOTONLY; + else if (!strcmp(dcc[idx].nick, "(webui)")) + dcc[i].status |= STAT_WS; + /* Copy acceptable-nick/host mask */ + strlcpy(dcc[i].nick, dcc[idx].host, HANDLEN); /* wo ist hier der sinn? dcc[idx].host ist immer *, oder? */ + dcc[i].timeval = now; strcpy(dcc[i].u.chat->con_chan, chanset ? chanset->dname : "*"); /* Displays a customizable banner. */ diff --git a/src/eggdrop.h b/src/eggdrop.h index 0b7015648..4d6c464ae 100644 --- a/src/eggdrop.h +++ b/src/eggdrop.h @@ -543,6 +543,7 @@ struct dupwait_info { #define STAT_USRONLY 0x00040 /* telnet on users-only connect */ #define STAT_PAGE 0x00080 /* page output to the user */ #define STAT_SERV 0x00100 /* this is a server connection */ +#define STAT_WS 0x00200 /* webui websocket */ /* For stripping out mIRC codes. */ #define STRIP_COLOR 0x00001 /* remove mIRC color codes */ @@ -654,6 +655,7 @@ typedef struct { #define SOCK_VIRTUAL 0x0200 /* not-connected socket (dont read it!) */ #define SOCK_BUFFER 0x0400 /* buffer data; don't notify dcc funcs */ #define SOCK_TCL 0x0800 /* tcl socket, don't do anything on it */ +#define SOCK_WS 0x1000 /* webui websocket */ /* Flags to sock_has_data */ @@ -805,4 +807,7 @@ struct dns_thread_node { extern struct dns_thread_node *dns_thread_head; #endif +#define WS_ECHO_ON "\x01" /* echo on */ +#define WS_ECHO_OFF "\x02" /* echo off */ + #endif /* _EGG_EGGDROP_H */ diff --git a/src/main.c b/src/main.c index 24dc95a4d..f9fc7f39d 100644 --- a/src/main.c +++ b/src/main.c @@ -178,10 +178,6 @@ int cx_line[16]; int cx_ptr = 0; #endif -#ifdef TLS -int ssl_cleanup(); -#endif - void fatal(const char *s, int recoverable) { int i; diff --git a/src/mod/module.h b/src/mod/module.h index 1fd109b5c..189bdb50c 100644 --- a/src/mod/module.h +++ b/src/mod/module.h @@ -527,6 +527,9 @@ typedef void (*chanout_butfunc)(int, int, const char *, ...) ATTRIBUTE_FORMAT(pr #define USERENTRY_ACCOUNT (*(struct user_entry_type *)(global[316])) #define get_user_by_account ((struct userrec * (*)(char *))global[317]) #define delaccount_by_handle ((int(*)(char *,char *))global[318]) +#define dcc_telnet_hostresolved2 ((void(*)(int, int))global[319]) +/* 320 - 323 */ +#define findsock ((int(*)(int))global[320]) diff --git a/src/mod/modvals.h b/src/mod/modvals.h index 5eda82948..1f07970be 100644 --- a/src/mod/modvals.h +++ b/src/mod/modvals.h @@ -23,36 +23,39 @@ #ifndef _EGG_MOD_MODVALS_H #define _EGG_MOD_MODVALS_H -/* #define HOOK_GET_FLAGREC 0 */ -/* #define HOOK_BUILD_FLAGREC 1 */ -/* #define HOOK_SET_FLAGREC 2 */ -#define HOOK_READ_USERFILE 3 -#define HOOK_REHASH 4 -#define HOOK_MINUTELY 5 -#define HOOK_DAILY 6 -#define HOOK_HOURLY 7 -#define HOOK_USERFILE 8 -#define HOOK_SECONDLY 9 -#define HOOK_PRE_REHASH 10 -#define HOOK_IDLE 11 -#define HOOK_5MINUTELY 12 -#define HOOK_LOADED 13 -#define HOOK_BACKUP 14 -#define HOOK_DIE 15 -#define REAL_HOOKS 16 -#define HOOK_SHAREOUT 105 -#define HOOK_SHAREIN 106 -#define HOOK_ENCRYPT_PASS 107 -#define HOOK_QSERV 108 -#define HOOK_ADD_MODE 109 -#define HOOK_MATCH_NOTEREJ 110 -#define HOOK_RFC_CASECMP 111 -#define HOOK_DNS_HOSTBYIP 112 -#define HOOK_DNS_IPBYHOST 113 -#define HOOK_ENCRYPT_STRING 114 -#define HOOK_DECRYPT_STRING 115 -#define HOOK_ENCRYPT_PASS2 116 -#define HOOK_VERIFY_PASS2 117 +/* #define HOOK_GET_FLAGREC 0 */ +/* #define HOOK_BUILD_FLAGREC 1 */ +/* #define HOOK_SET_FLAGREC 2 */ +#define HOOK_READ_USERFILE 3 +#define HOOK_REHASH 4 +#define HOOK_MINUTELY 5 +#define HOOK_DAILY 6 +#define HOOK_HOURLY 7 +#define HOOK_USERFILE 8 +#define HOOK_SECONDLY 9 +#define HOOK_PRE_REHASH 10 +#define HOOK_IDLE 11 +#define HOOK_5MINUTELY 12 +#define HOOK_LOADED 13 +#define HOOK_BACKUP 14 +#define HOOK_DIE 15 +#define REAL_HOOKS 16 +#define HOOK_SHAREOUT 105 +#define HOOK_SHAREIN 106 +#define HOOK_ENCRYPT_PASS 107 +#define HOOK_QSERV 108 +#define HOOK_ADD_MODE 109 +#define HOOK_MATCH_NOTEREJ 110 +#define HOOK_RFC_CASECMP 111 +#define HOOK_DNS_HOSTBYIP 112 +#define HOOK_DNS_IPBYHOST 113 +#define HOOK_ENCRYPT_STRING 114 +#define HOOK_DECRYPT_STRING 115 +#define HOOK_ENCRYPT_PASS2 116 +#define HOOK_VERIFY_PASS2 117 +#define HOOK_DCC_TELNET_HOSTRESOLVED 118 +#define HOOK_WEBUI_FRAME 119 +#define HOOK_WEBUI_UNFRAME 120 /* These are FIXED once they are in a release they STAY */ #define MODCALL_START 0 diff --git a/src/mod/webui.mod/Makefile b/src/mod/webui.mod/Makefile new file mode 100644 index 000000000..e3042a41f --- /dev/null +++ b/src/mod/webui.mod/Makefile @@ -0,0 +1,47 @@ +# Makefile for src/mod/webui.mod/ + +srcdir = . + + +doofus: + @echo "" && \ + echo "Let's try this from the right directory..." && \ + echo "" && \ + cd ../../../ && $(MAKE) + +static: ../webui.o + +modules: ../../../webui.$(MOD_EXT) + +../webui.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -DMAKING_MODS -c $(srcdir)/webui.c && mv -f webui.o ../ + +../../../webui.$(MOD_EXT): ../webui.o + $(LD) $(CFLAGS) -o ../../../webui.$(MOD_EXT) ../webui.o $(XLIBS) $(MODULE_XLIBS) && $(STRIP) ../../../webui.$(MOD_EXT) + +depend: + $(CC) $(CFLAGS) -MM $(srcdir)/webui.c -MT ../webui.o > .depend + +clean: + @rm -f .depend *.o *.$(MOD_EXT) *~ + +distclean: clean + +install: install-text + +install-text: + if test ! -f $(DEST)/text/webui.html; then \ + $(INSTALL_DATA) $(srcdir)/text/webui.html $(DEST)/text/; \ + fi + +#safety hash +../webui.o: .././webui.mod/webui.c ../../../src/mod/module.h \ + ../../../src/main.h ../../../config.h ../../../eggint.h ../../../lush.h \ + ../../../src/lang.h ../../../src/eggdrop.h ../../../src/compat/in6.h \ + ../../../src/flags.h ../../../src/cmdt.h ../../../src/tclegg.h \ + ../../../src/tclhash.h ../../../src/chan.h ../../../src/users.h \ + ../../../src/compat/compat.h ../../../src/compat/base64.h \ + ../../../src/compat/inet_aton.h ../../../src/compat/snprintf.h \ + ../../../src/compat/explicit_bzero.h ../../../src/compat/strlcpy.h \ + ../../../src/mod/modvals.h ../../../src/tandem.h \ + ../../../src/mod/server.mod/server.h diff --git a/src/mod/webui.mod/webui.c b/src/mod/webui.mod/webui.c new file mode 100644 index 000000000..d5da2e6cb --- /dev/null +++ b/src/mod/webui.mod/webui.c @@ -0,0 +1,489 @@ +/* + * webui.c -- part of webui.mod + */ +/* + * Copyright (C) 2023 Michael Ortmann MIT License + * Copyright (C) 2023 Eggheads Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#define MODULE_NAME "webui" + +#include +#include +#include /* base64 encode b64_ntop() and base64 decode b64_pton() */ +#include +#include +#include +#include +#include "src/version.h" +#include "src/mod/module.h" + +#define WS_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" +#define WS_KEY "Sec-WebSocket-Key:" +#define WS_KEYLEN 24 /* key is padded, so its always 24 bytes */ +#define WS_LEN 28 /* length of Sec-WebSocket-Accept header field value + * base64(len(sha1)) + * import math; (4 * math.ceil(20 / 3)) */ + +static Function *global = NULL; + +/* 0x15 = TLS ContentType alert + * 0x0a = TLS Alert unexpected_message + */ +static uint8_t alert[] = {0x15, 0x03, 0x01, 0x00, 0x02, 0x02, 0x0a}; + +/* wget https://www.eggheads.org/favicon.ico + * xxd -i favicon.ico + */ +static unsigned char favicon_ico[] = { + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x24, 0x00, 0x24, 0x00, 0xf7, 0x00, + 0x00, 0x2e, 0x2c, 0x2d, 0x23, 0x22, 0x24, 0x31, 0x31, 0x32, 0x3c, 0x3b, + 0x3c, 0x46, 0x41, 0x3f, 0x3a, 0x4a, 0x51, 0x3e, 0x5e, 0x71, 0x45, 0x44, + 0x45, 0x49, 0x46, 0x47, 0x48, 0x47, 0x48, 0x4b, 0x4a, 0x4b, 0x44, 0x44, + 0x49, 0x54, 0x53, 0x54, 0x54, 0x56, 0x58, 0x5c, 0x5b, 0x5c, 0x62, 0x60, + 0x5e, 0x78, 0x68, 0x5f, 0x48, 0x65, 0x76, 0x46, 0x68, 0x7e, 0x57, 0x68, + 0x76, 0x58, 0x69, 0x75, 0x63, 0x63, 0x65, 0x65, 0x66, 0x69, 0x6c, 0x6b, + 0x6c, 0x70, 0x6c, 0x6c, 0x6f, 0x6d, 0x70, 0x6e, 0x71, 0x74, 0x6e, 0x76, + 0x7e, 0x74, 0x73, 0x73, 0x74, 0x74, 0x78, 0x77, 0x78, 0x7b, 0x7b, 0x7b, + 0x7c, 0x81, 0x6d, 0x62, 0x80, 0x7e, 0x7e, 0x98, 0x7f, 0x70, 0x85, 0x80, + 0x7e, 0x1a, 0x5d, 0x82, 0x16, 0x6e, 0x9d, 0x22, 0x68, 0x8f, 0x3c, 0x67, + 0x86, 0x38, 0x75, 0x97, 0x1d, 0x75, 0xa7, 0x10, 0x74, 0xac, 0x1c, 0x77, + 0xa9, 0x09, 0x7e, 0xbc, 0x21, 0x76, 0xa9, 0x2b, 0x7f, 0xae, 0x24, 0x7b, + 0xaa, 0x33, 0x7b, 0xa5, 0x49, 0x6d, 0x84, 0x57, 0x73, 0x86, 0x5a, 0x76, + 0x87, 0x51, 0x79, 0x8e, 0x5b, 0x76, 0x8a, 0x4e, 0x7b, 0x95, 0x54, 0x7f, + 0x97, 0x69, 0x79, 0x85, 0x74, 0x7c, 0x83, 0x7c, 0x7e, 0x80, 0x62, 0x7f, + 0x91, 0x12, 0x80, 0xb9, 0x1f, 0x85, 0xb8, 0x34, 0x80, 0xa9, 0x22, 0x82, + 0xb5, 0x2c, 0x85, 0xb5, 0x3f, 0x87, 0xb1, 0x7c, 0x80, 0x83, 0x65, 0x82, + 0x98, 0x64, 0x88, 0x9c, 0x74, 0x88, 0x93, 0x47, 0x89, 0xad, 0x55, 0x89, + 0xab, 0x44, 0x8d, 0xb7, 0x4a, 0x8c, 0xb4, 0x4c, 0x91, 0xb4, 0x5a, 0x95, + 0xb9, 0x6a, 0x8d, 0xa0, 0x75, 0x92, 0xa2, 0x79, 0x93, 0xa3, 0x6a, 0x9c, + 0xb5, 0x04, 0x81, 0xc4, 0x01, 0x86, 0xcc, 0x0a, 0x82, 0xc3, 0x84, 0x83, + 0x84, 0x8a, 0x85, 0x85, 0x87, 0x88, 0x89, 0x8c, 0x8a, 0x8b, 0x80, 0x8e, + 0x97, 0x90, 0x8e, 0x90, 0x85, 0x92, 0x9b, 0x94, 0x93, 0x94, 0x96, 0x96, + 0x98, 0x95, 0x98, 0x9c, 0x9d, 0x9b, 0x9d, 0xa1, 0x90, 0x86, 0xa2, 0x9d, + 0x9e, 0xa2, 0x9f, 0xa1, 0x9b, 0xa0, 0xa6, 0x8d, 0xa4, 0xb6, 0x91, 0xa6, + 0xb3, 0xa2, 0xa2, 0xa3, 0xab, 0xa6, 0xa6, 0xa4, 0xa6, 0xaa, 0xa8, 0xa7, + 0xa8, 0xa6, 0xaa, 0xaf, 0xac, 0xab, 0xac, 0xb8, 0xb1, 0xaf, 0xb4, 0xb3, + 0xb4, 0xb9, 0xb7, 0xb7, 0xb5, 0xb7, 0xba, 0xb7, 0xb8, 0xb9, 0xbd, 0xbc, + 0xbd, 0xc0, 0xbf, 0xbf, 0xc2, 0xc0, 0xbf, 0xb2, 0xc1, 0xcb, 0xc5, 0xc2, + 0xc3, 0xca, 0xc5, 0xc5, 0xca, 0xc6, 0xc8, 0xcc, 0xcb, 0xcc, 0xd0, 0xcc, + 0xcd, 0xd5, 0xd0, 0xcf, 0xcf, 0xcf, 0xd0, 0xd1, 0xcf, 0xd0, 0xcd, 0xd1, + 0xd4, 0xcf, 0xd5, 0xd9, 0xd4, 0xd3, 0xd4, 0xdb, 0xd6, 0xd6, 0xda, 0xd8, + 0xd7, 0xdd, 0xdc, 0xdd, 0xd3, 0xda, 0xdd, 0xe1, 0xdd, 0xdd, 0xe2, 0xe0, + 0xdf, 0xde, 0xdf, 0xe0, 0xe0, 0xde, 0xe0, 0xdf, 0xe0, 0xe0, 0xe4, 0xe3, + 0xe4, 0xe9, 0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe6, 0xe8, 0xea, 0xed, 0xec, + 0xec, 0xf0, 0xee, 0xef, 0xf1, 0xf0, 0xef, 0xf4, 0xf3, 0xf3, 0xf8, 0xf7, + 0xf7, 0xf8, 0xf8, 0xf7, 0xf8, 0xf7, 0xf8, 0xfe, 0xfe, 0xfe, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x21, 0xf9, 0x04, 0x01, 0x00, 0x00, 0x93, 0x00, 0x2c, 0x00, 0x00, + 0x00, 0x00, 0x24, 0x00, 0x24, 0x00, 0x00, 0x08, 0xfe, 0x00, 0x27, 0x09, + 0x1c, 0x48, 0xb0, 0xa0, 0x40, 0x0e, 0x02, 0xa7, 0x18, 0x5c, 0xc8, 0xb0, + 0xa1, 0xc3, 0x87, 0x10, 0x09, 0xba, 0x59, 0x13, 0xb1, 0x62, 0xc1, 0x3a, + 0x82, 0x1c, 0x39, 0x5a, 0xf4, 0xa7, 0x8e, 0x45, 0x88, 0x6f, 0x08, 0x09, + 0x42, 0x24, 0xa9, 0xa4, 0x24, 0x48, 0x7d, 0x3e, 0x3a, 0xfc, 0x83, 0x88, + 0x91, 0xc9, 0x97, 0x80, 0x54, 0x2e, 0xb4, 0x53, 0x08, 0x11, 0xa4, 0x97, + 0x2f, 0xfd, 0xc8, 0x24, 0x38, 0xa7, 0x50, 0x46, 0x9c, 0x2f, 0x17, 0x51, + 0xdc, 0x39, 0xc9, 0x8e, 0xa0, 0x45, 0x87, 0xd2, 0x60, 0x21, 0xd3, 0xe7, + 0xa6, 0xa4, 0x47, 0x6f, 0xaa, 0xe8, 0xe8, 0x40, 0xf4, 0x0e, 0xa0, 0x3e, + 0x65, 0xd6, 0xc4, 0x79, 0xb3, 0xa6, 0xce, 0x4d, 0x41, 0x73, 0xfa, 0xd8, + 0xe9, 0x52, 0x95, 0xce, 0x97, 0x11, 0x17, 0x2c, 0x64, 0xe0, 0x30, 0xc5, + 0xce, 0x20, 0x2b, 0x0d, 0x12, 0x28, 0x58, 0x20, 0x20, 0x80, 0xcc, 0x2f, + 0x08, 0x04, 0x70, 0x58, 0x63, 0x27, 0xce, 0x14, 0x00, 0x07, 0x42, 0x0c, + 0x58, 0x03, 0x08, 0xd1, 0x21, 0x37, 0x04, 0x54, 0x76, 0x28, 0x73, 0x41, + 0x8b, 0xd3, 0x92, 0x75, 0x06, 0x38, 0x98, 0x82, 0x53, 0x61, 0xc5, 0x01, + 0x00, 0x12, 0x24, 0x78, 0x60, 0xa7, 0xe4, 0x9f, 0x46, 0x25, 0xad, 0x00, + 0xb0, 0x22, 0x29, 0x12, 0x1d, 0x47, 0x92, 0xb4, 0x1c, 0xa8, 0x28, 0x40, + 0x81, 0x1a, 0x0f, 0x0d, 0xfe, 0x48, 0x5a, 0xc4, 0x20, 0x4d, 0xc9, 0x2e, + 0x0b, 0x2e, 0x44, 0x6a, 0x54, 0x05, 0x90, 0xa4, 0x2e, 0x0e, 0x3e, 0xd2, + 0x59, 0x83, 0xa1, 0x4f, 0xc9, 0x3e, 0x8b, 0x4e, 0x7a, 0x48, 0x53, 0xc1, + 0x78, 0xa4, 0x93, 0x21, 0x64, 0xd2, 0xa1, 0x32, 0xe5, 0xd1, 0xcb, 0x36, + 0x17, 0x16, 0x9d, 0xc9, 0x70, 0x08, 0x12, 0xa4, 0x38, 0x03, 0xa4, 0xfe, + 0x03, 0x0a, 0x61, 0x05, 0x79, 0x52, 0x0b, 0x9d, 0x21, 0x91, 0xb1, 0xf0, + 0xa1, 0xc3, 0x80, 0x06, 0x32, 0xe7, 0x20, 0x72, 0xb4, 0xe6, 0x83, 0x85, + 0xc6, 0x87, 0x5e, 0x1e, 0xe2, 0x4a, 0x56, 0xba, 0x9f, 0x9b, 0x90, 0x2c, + 0x82, 0x1a, 0x50, 0x92, 0x38, 0x12, 0xc7, 0x4e, 0x73, 0xdc, 0xe1, 0x12, + 0x81, 0x2f, 0x25, 0x42, 0x54, 0x51, 0x7e, 0x30, 0xf8, 0x92, 0x1d, 0x0f, + 0xc6, 0x81, 0x07, 0x49, 0x12, 0x22, 0xf2, 0xa0, 0x40, 0x70, 0xe4, 0xc7, + 0xe0, 0x77, 0x1b, 0x0a, 0x74, 0x87, 0x23, 0x87, 0xf8, 0x71, 0x47, 0x1f, + 0x70, 0xf8, 0xf6, 0x88, 0x1e, 0x21, 0x0e, 0xf4, 0x06, 0x07, 0x03, 0x80, + 0x00, 0x01, 0x01, 0x54, 0xa4, 0x31, 0x54, 0x8b, 0x02, 0x35, 0x61, 0x40, + 0x0e, 0x5e, 0x88, 0x40, 0x01, 0x0a, 0x47, 0xe0, 0x38, 0x90, 0x0c, 0x45, + 0x88, 0x71, 0xc2, 0x03, 0x59, 0x8c, 0x71, 0xc3, 0x40, 0x76, 0x50, 0xa8, + 0x52, 0x11, 0xc8, 0x5d, 0x95, 0x83, 0x10, 0x68, 0xc4, 0x50, 0x81, 0x16, + 0x56, 0xe0, 0x40, 0x83, 0x13, 0x87, 0x38, 0x12, 0xc9, 0x22, 0x1f, 0xdd, + 0x20, 0x87, 0x49, 0x72, 0xa4, 0x40, 0x42, 0x01, 0x38, 0xe4, 0xa0, 0x41, + 0x0c, 0x2d, 0x40, 0x01, 0x84, 0x22, 0x25, 0x3d, 0xe2, 0x64, 0x44, 0x44, + 0x24, 0x77, 0xd2, 0x13, 0x51, 0x40, 0x61, 0x42, 0x07, 0x53, 0xe0, 0xc0, + 0x42, 0x14, 0x51, 0x00, 0x61, 0x48, 0x49, 0x8b, 0xcc, 0x61, 0xd1, 0x0d, + 0x7c, 0x78, 0xe9, 0x88, 0x12, 0x79, 0xc2, 0xc0, 0x05, 0x18, 0x61, 0x94, + 0x00, 0x68, 0x0f, 0x89, 0x12, 0xe2, 0xc4, 0x15, 0x11, 0xcd, 0xb1, 0x06, + 0x0e, 0x3e, 0x20, 0x91, 0xc4, 0x12, 0x3f, 0x44, 0xa1, 0x02, 0x05, 0x6c, + 0x74, 0x91, 0x46, 0x04, 0x3c, 0xe4, 0x19, 0x44, 0x12, 0x40, 0xd8, 0xd0, + 0x9f, 0x43, 0x44, 0x6d, 0xf4, 0xa1, 0x06, 0x16, 0x4c, 0x18, 0x01, 0xc4, + 0x0f, 0x2c, 0xbc, 0x50, 0x83, 0x15, 0x80, 0x80, 0xb1, 0x85, 0x0e, 0x35, + 0xa0, 0xf0, 0xc2, 0x0f, 0x30, 0x0c, 0x61, 0xc5, 0x17, 0x16, 0xa5, 0x41, + 0xc6, 0x1a, 0x66, 0xa4, 0x91, 0x85, 0x16, 0x53, 0x7c, 0xd1, 0x47, 0x1d, + 0x6b, 0x9c, 0x61, 0x85, 0x15, 0x5b, 0x68, 0x51, 0x06, 0x16, 0x69, 0xc8, + 0xb4, 0x46, 0x19, 0x77, 0x80, 0xe1, 0xd0, 0xab, 0x0b, 0x05, 0x04, 0x00, + 0x3b +}; + +static void webui_http_eof(int idx) +{ + debug2("webui: webui_http_eof() idx %i sock %li", idx, dcc[idx].sock); + killsock(dcc[idx].sock); + lostdcc(idx); +} + +static void webui_http_activity(int idx, char *buf, int len) +{ + struct rusage ru1, ru2; + int r, i; + char response[4096]; /* > sizeof webui.html + * TODO: dynamic size? else buffer overflow ;) + * we dont control webui.html size, that is user input! + */ + + if (len < 6) { /* TODO: better len check */ + putlog(LOG_MISC, "*", + "WEBUI error: %s sent something other than http GET request", + iptostr(&dcc[idx].sockname.addr.sa)); + killsock(dcc[idx].sock); + lostdcc(idx); + return; + } + if (buf[0] == 0x16) { /* 0x16 = TLS handshake */ + putlog(LOG_MISC, "*", + "WEBUI error: %s requested TLS handshake for non-ssl port", + iptostr(&dcc[idx].sockname.addr.sa)); + tputs(dcc[idx].sock, (char *) alert, sizeof alert); + killsock(dcc[idx].sock); + lostdcc(idx); + return; + } + r = getrusage(RUSAGE_SELF, &ru1); + debug2("webui: webui_http_activity(): idx %i len %i", idx, len); + buf[len] = '\0'; /* TODO: is there no better way? we already know len */ + debug0("webui: http()"); + if (buf[5] == ' ') { + debug0("webui: webui: GET /"); + #define PATH "text/webui.html" + int fd; + struct stat sb; + char *body; + if ((fd = open(PATH, O_RDONLY)) < 0) { + putlog(LOG_MISC, "*", "WEBUI error: open(" PATH "): %s", strerror(errno)); + /* TODO: send 404 and/or lostdcc() killsock() */ + return; + } + if (fstat(fd, &sb) < 0) { + putlog(LOG_MISC, "*", "WEBUI error: fstat(" PATH "): %s", strerror(errno)); + return; + } + + if ((body = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE +#ifdef MAP_POPULATE + | MAP_POPULATE +#endif + , fd, 0)) == MAP_FAILED) { + putlog(LOG_MISC, "*", "WEBUI error: mmap(" PATH "): %s\n", strerror(errno)); + return; + } + i = snprintf(response, sizeof response, + "HTTP/1.1 200 \r\n" /* textual phrase is OPTIONAL */ + "Content-Length: %li\r\n" + "Server: Eggdrop/%s+%s\r\n" + "\r\n%.*s", sb.st_size, EGG_STRINGVER, EGG_PATCH, (int) sb.st_size, body); + tputs(dcc[idx].sock, response, i); + debug2("webui: tputs(): >>>%s<<< %i", response, i); + if (munmap(body, sb.st_size) < 0) { + putlog(LOG_MISC, "*", "WEBUI error: munmap(): %s", strerror(errno)); + return; + } + } else if (buf[5] == 'f') { + debug0("webui: GET /favicon.ico"); + i = snprintf(response, sizeof response, + "HTTP/1.1 200 \r\n" /* textual phrase is OPTIONAL */ + "Content-Length: %zu\r\n" + "Content-Type: image/x-icon\r\n" + "Server: Eggdrop/%s+%s\r\n" /* TODO: stealth_telnets */ + "\r\n", + sizeof favicon_ico, EGG_STRINGVER, EGG_PATCH); + memcpy(response + i, favicon_ico, sizeof favicon_ico); + i += sizeof favicon_ico; + + tputs(dcc[idx].sock, response, i); + debug1("webui: tputs(): %i", i); + } else if (buf[5] == 'w') { + debug0("webui: GET /w"); + buf = strstr(buf, WS_KEY); + if (!buf) { + putlog(LOG_MISC, "*", "WEBUI error: Sec-WebSocket-Key not found"); + return; + } + buf += sizeof WS_KEY; + for(i = 0; i < WS_KEYLEN; i++) + if (!buf[i]) { + putlog(LOG_MISC, "*", "WEBUI error: Sec-WebSocket-Key too short"); + return; + } + debug0("webui: server requests websocket upgrade"); + + unsigned char hash[SHA_DIGEST_LENGTH]; +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); + const EVP_MD *md = EVP_sha1(); + unsigned int md_len; + EVP_DigestInit_ex(mdctx, md, NULL); + EVP_DigestUpdate(mdctx, buf, WS_KEYLEN); + EVP_DigestUpdate(mdctx, WS_GUID, (sizeof WS_GUID) - 1); + EVP_DigestFinal_ex(mdctx, hash, &md_len); + EVP_MD_CTX_free(mdctx); +#else + SHA_CTX c; + SHA1_Init(&c); + SHA1_Update(&c, buf, KEYLEN); + SHA1_Update(&c, WS_GUID, (sizeof WS_GUID) - 1); + SHA1_Final(hash, &c); +#endif + + char out[WS_LEN + 1]; + /* TODO: remove assert / debug */ + if (b64_ntop(hash, sizeof hash, out, sizeof out) != WS_LEN) { + putlog(LOG_MISC, "*", "WEBUI error: b64_ntop() != WS_LEN"); + return; + } + + i = snprintf(response, sizeof response, + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: %s\r\n" + "\r\n", out); + tputs(dcc[idx].sock, response, i); + debug2("webui: tputs(): >>>%s<<< %i", response, i); + + sock_list* socklist_i = &socklist[findsock(dcc[idx].sock)]; + socklist_i->flags |= SOCK_WS; + + + socklist_i->flags &= ~ SOCK_BINARY; /* we need it for net.c sockgets(), is there better place to do this? */ + debug1("webui: unset flag SOCK_BINARY sock %li\n", dcc[idx].sock); + strcpy(dcc[idx].host, "*"); /* important for later dcc_telnet_id wild_match, is there better place to do this? */ + /* .host becomes .nick in change_to_dcc_telnet_id() */ + debug4("webui: set flag SOCK_WS socklist %i idx %i sock %li status %lu\n", findsock(dcc[idx].sock), idx, dcc[idx].sock, dcc[idx].status); + + dcc[idx].status |= STAT_USRONLY; /* magick */ + for (i = 0; i < dcc_total; i++) /* quick hack, we need to link from idx, dont we? */ + if (!strcmp(dcc[i].nick, "(webui)")) { + debug0("webui: found (webui) dcc\n"); + break; + } + dcc[idx].u.other = NULL; /* fix ATTEMPTING TO FREE NON-MALLOC'D PTR: dccutil.c (561) */ + dcc_telnet_hostresolved2(idx, i); + + debug2("webui: CHANGEOVER -> idx %i sock %li\n", idx, dcc[idx].sock); + } else /* TODO: send 404 or something ? */ + debug0("webui: 404"); + if (len == 511) { + /* read probable remaining bytes */ + SSL *ssl = socklist[findsock(dcc[idx].sock)].ssl; + if (ssl) + debug1("webui: SSL_read(): len %i", SSL_read(ssl, buf, 511)); + else + debug1("webui: read(): len %li", read(dcc[idx].sock, buf, 511)); + } + if (!r && !getrusage(RUSAGE_SELF, &ru2)) + debug2("webui: webui_http_activity(): user %.3fms sys %.3fms", + (double) (ru2.ru_utime.tv_usec - ru1.ru_utime.tv_usec) / 1000 + + (double) (ru2.ru_utime.tv_sec - ru1.ru_utime.tv_sec ) * 1000, + (double) (ru2.ru_stime.tv_usec - ru1.ru_stime.tv_usec) / 1000 + + (double) (ru2.ru_stime.tv_sec - ru1.ru_stime.tv_sec ) * 1000); +} + +static void webui_http_display(int idx, char *buf) +{ + if (!dcc[idx].ssl) + strcpy(buf, "webui http"); + else + strcpy(buf, "webui https"); +} + +static struct dcc_table DCC_WEBUI_HTTP = { + "WEBUI_HTTP", + 0, + webui_http_eof, + webui_http_activity, + NULL, + NULL, + webui_http_display, + NULL, + NULL, + NULL, + NULL +}; + +static void webui_dcc_telnet_hostresolved(int i) +{ + debug1("webui_dcc_telnet_hostresolved(%i)", i); + changeover_dcc(i, &DCC_WEBUI_HTTP, 0); + sockoptions(dcc[i].sock, EGG_OPTION_SET, SOCK_BINARY); + dcc[i].u.other = NULL; /* important, else nfree() error in lostdcc on eof */ +} + +static void webui_frame(char **buf, unsigned int *len) { + static uint8_t out[2048]; + + /* no debug() or putlog() here or recursion */ + printf("webui: webui_frame() len %u\n", *len); + //printf(">>>%s<<<", *buf); + out[0] = 0x81; /* FIN + text frame */ + /* A server MUST NOT mask any frames that it sends to the client */ + if (*len < 0x7e) { + out[1] = *len; + /* TODO: we could offset buf and get rid of this memcpy() */ + memcpy(out + 2, *buf, *len); + *buf = (char *) out; + *len = *len + 2; + } else { + out[1] = 0x7e; + uint16_t len2 = htons(*len); + memcpy(out + 2, &len2, 2); + /* TODO: we could offset buf and get rid of this memcpy() */ + memcpy(out + 4, *buf, *len); + *buf = (char *) out; + *len = *len + 4; + } + /* FIXME:len > 0xffff */ +} + +/* TODO: return error code ? */ +static void webui_unframe(char *buf, int *len) +{ + int i; + uint8_t *key, *payload; + + debug1("webui: webui_unframe(): len %i", *len); + if (*len < 6) { /* TODO: better len check */ + + /* TODO: return error code ? */ + putlog(LOG_MISC, "*", "WEBUI error: someone sent something other than WebSocket protocol"); + /* + putlog(LOG_MISC, "*", + "WEBUI error: %s sent something other than WebSocket protocol", + iptostr(&dcc[idx].sockname.addr.sa)); + killsock(dcc[idx].sock); + lostdcc(idx); + */ + return; + } + if (buf[0] & 0x08) { + putlog(LOG_MISC, "*", "WEBUI: fixme: sent connection close not handled yet"); + /* + debug1("webui: webui_ws_activity(): %s sent connection close", + iptostr(&dcc[idx].sockname.addr.sa)); + killsock(dcc[idx].sock); + lostdcc(idx); + */ + return; + } + /* xor decrypt + */ + key = (uint8_t *) buf; + if (key[1] < 0xfe) { + key += 2; + *len -= 6; + } else if (key[1] == 0xfe) { + key += 4; + *len -= 8; + } else { + key += 10; + *len -= 14; + } + payload = key + 4; + for (i = 0; i < *len; i++) + payload[i] = payload[i] ^ key[i % 4]; + debug2("webui: webui_unframe(): payload >>>%.*s<<<", (int) *len, payload); + + memmove(buf, payload, *len); + /* we switched back from binary sock to text sock for sockgets() needs this for dcc_telnet_id() */ + /* so now we have to add \r\n here :/ */ + strcpy(buf + *len, "\r\n"); + *len+= 2; +} + +static char *webui_close(void) +{ + del_hook(HOOK_DCC_TELNET_HOSTRESOLVED, (Function) webui_dcc_telnet_hostresolved); + del_hook(HOOK_WEBUI_FRAME, (Function) webui_frame); + del_hook(HOOK_WEBUI_UNFRAME, (Function) webui_unframe); + return NULL; +} + +EXPORT_SCOPE char *webui_start(); + +static Function webui_table[] = { + (Function) webui_start, + (Function) webui_close, + NULL, + NULL, +}; + +char *webui_start(Function *global_funcs) +{ +#ifdef TLS + global = global_funcs; + module_register(MODULE_NAME, webui_table, 0, 9); + if (!module_depend(MODULE_NAME, "eggdrop", 109, 0)) { + module_undepend(MODULE_NAME); + return "This module requires Eggdrop 1.9.0 or later."; + } + add_hook(HOOK_DCC_TELNET_HOSTRESOLVED, (Function) webui_dcc_telnet_hostresolved); + add_hook(HOOK_WEBUI_FRAME, (Function) webui_frame); + add_hook(HOOK_WEBUI_UNFRAME, (Function) webui_unframe); + return NULL; +#else + return "Initialization failure: configured with --disable-tls or openssl not found"; +#endif +} diff --git a/src/mod/webui.mod/webui.html b/src/mod/webui.mod/webui.html new file mode 100644 index 000000000..eb9cd7f2a --- /dev/null +++ b/src/mod/webui.mod/webui.html @@ -0,0 +1,43 @@ + + + + Eggdrop + +

+    
+    
+  
+
diff --git a/src/modules.c b/src/modules.c
index 5bcab0e56..73f4cedf4 100644
--- a/src/modules.c
+++ b/src/modules.c
@@ -174,6 +174,9 @@ int (*rfc_toupper) (int) = _rfc_toupper;
 int (*rfc_tolower) (int) = _rfc_tolower;
 void (*dns_hostbyip) (sockname_t *) = core_dns_hostbyip;
 void (*dns_ipbyhost) (char *) = core_dns_ipbyhost;
+void (*webui_dcc_telnet_hostresolved) (int) = 0;
+void (*webui_frame) (char **, unsigned int *) = 0;
+void (*webui_unframe) (char *, int *) = 0;
 
 module_entry *module_list;
 dependancy *dependancy_list = NULL;
@@ -627,7 +630,10 @@ Function global_table[] = {
 /* 316 - 319 */
   (Function) & USERENTRY_ACCOUNT, /* struct user_entry_type *            */
   (Function) get_user_by_account,
-  (Function) delhost_by_handle
+  (Function) delhost_by_handle,
+  (Function) dcc_telnet_hostresolved2,
+/* 320 - 323 */
+  (Function) findsock
 };
 
 void init_modules(void)
@@ -1105,6 +1111,15 @@ void add_hook(int hook_num, Function func)
       if (dns_ipbyhost == core_dns_ipbyhost)
         dns_ipbyhost = (void (*)(char *)) func;
       break;
+    case HOOK_DCC_TELNET_HOSTRESOLVED:
+      webui_dcc_telnet_hostresolved = (void (*)(int)) func;
+      break;
+    case HOOK_WEBUI_FRAME:
+      webui_frame = (void (*)(char **, unsigned int *)) func;
+      break;
+    case HOOK_WEBUI_UNFRAME:
+      webui_unframe = (void (*)(char *, int *)) func;
+      break;
     }
 }
 
@@ -1175,6 +1190,18 @@ void del_hook(int hook_num, Function func)
       if (dns_ipbyhost == (void (*)(char *)) func)
         dns_ipbyhost = core_dns_ipbyhost;
       break;
+    case HOOK_DCC_TELNET_HOSTRESOLVED:
+      if (webui_dcc_telnet_hostresolved == (void (*)(int)) func)
+        webui_dcc_telnet_hostresolved = (void (*)(int)) null_func;
+      break;
+    case HOOK_WEBUI_FRAME:
+      if (webui_frame == (void (*)(char **, unsigned int *)) func)
+        webui_frame = (void (*)(char **, unsigned int *)) null_func;
+      break;
+    case HOOK_WEBUI_UNFRAME:
+      if (webui_unframe == (void (*)(char *, int *)) func)
+        webui_unframe = (void (*)(char *, int *)) null_func;
+      break;
     }
 }
 
diff --git a/src/net.c b/src/net.c
index 48a5c9e60..ae50e888e 100644
--- a/src/net.c
+++ b/src/net.c
@@ -1006,6 +1006,10 @@ int sockread(char *s, int *len, sock_list *slist, int slistmax, int tclonly)
           continue;           /* EAGAIN */
         }
       }
+#ifdef TLS
+      if (socklist[i].flags & SOCK_WS)
+        webui_unframe(s, &x);
+#endif /* TLS */
       s[x] = 0;
       *len = x;
       if (slist[i].flags & SOCK_PROXYWAIT) {
@@ -1173,6 +1177,8 @@ int sockgets(char *s, int *len)
     s[0] = 0;
     return ret;
   }
+  debug5("sockread sock %i return %i >>%s<< %i binary? %i", socklist[ret].sock, ret, xx, *len, socklist[ret].flags & SOCK_BINARY);
+  debug1("flags %i", socklist[ret].flags);
   /* sockread can return binary data while socket still has connectflag, process first */
   if (socklist[ret].flags & SOCK_BINARY && *len > 0) {
     socklist[ret].flags &= ~SOCK_CONNECT;
@@ -1332,6 +1338,8 @@ void tputs(int z, char *s, unsigned int len)
         return;
       }
 #ifdef TLS
+      if (socklist[i].flags & SOCK_WS) /* TODO: early enough for un-tls ws? */
+        webui_frame(&s, &len);
       if (socklist[i].ssl) {
         x = SSL_write(socklist[i].ssl, s, len);
         if (x < 0) {
diff --git a/src/proto.h b/src/proto.h
index 63cfc574c..05a72751b 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -54,6 +54,9 @@ extern int (*rfc_ncasecmp) (const char *, const char *, int);
 extern int (*rfc_toupper) (int);
 extern int (*rfc_tolower) (int);
 extern int (*match_noterej) (struct userrec *, char *);
+extern void (*webui_dcc_telnet_hostresolved) (int);
+extern void (*webui_frame) (char **, unsigned int *);
+extern void (*webui_unframe) (char *, int *);
 #endif
 
 /* botcmd.c */
@@ -134,6 +137,7 @@ int check_ansi(char *);
 void dupwait_notify(char *);
 #ifdef TLS
 int dcc_fingerprint(int);
+void dcc_telnet_hostresolved2(int, int);
 #endif
 
 /* dccutil.c */
@@ -310,6 +314,7 @@ int readtclprog(char *fname);
 
 /* tls.c */
 #ifdef TLS
+void ssl_cleanup();
 int ssl_handshake(int, int, int, int, char *, IntFunc);
 char *ssl_fpconv(char *in, char *out);
 const char *ssl_getuid(int sock);
diff --git a/src/tcldcc.c b/src/tcldcc.c
index 6a704de87..346eaed61 100644
--- a/src/tcldcc.c
+++ b/src/tcldcc.c
@@ -1218,6 +1218,8 @@ static int setlisten(Tcl_Interp *irp, char *ip, char *portp, char *type, char *m
     strcpy(dcc[idx].nick, "(users)");
   else if (!strcmp(type, "all"))
     strcpy(dcc[idx].nick, "(telnet)");
+  else if (!strcmp(type, "webui"))
+    strcpy(dcc[idx].nick, "(webui)");
   if (maskproc[0])
     strlcpy(dcc[idx].host, maskproc, UHOSTMAX);
   else
@@ -1303,9 +1305,9 @@ static int tcl_listen STDVAR
   }
   if ((strcmp(argv[i], "bots")) && (strcmp(argv[i], "users"))
         && (strcmp(argv[i], "all")) && (strcmp(argv[i], "off"))
-        && (strcmp(argv[i], "script"))) {
+        && (strcmp(argv[i], "script")) && (strcmp(argv[i], "webui"))) {
     Tcl_AppendResult(irp, "invalid listen type: must be one of ",
-          "bots, users, all, off, script", NULL);
+          "bots, users, all, off, script, webui", NULL);
     return TCL_ERROR;
   }
   strlcpy(type, argv[i], sizeof(type));
diff --git a/src/tls.c b/src/tls.c
index 615a1d2ac..1feeab209 100644
--- a/src/tls.c
+++ b/src/tls.c
@@ -109,6 +109,71 @@ static int ssl_seed(void)
   return 0;
 }
 
+/* Get the certificate, corresponding to the connection
+ * identified by sock.
+ *
+ * Return value: pointer to a X509 certificate or NULL if we couldn't
+ * look up the certificate.
+ */
+static X509 *ssl_getcert(int sock)
+{
+  int i;
+  struct threaddata *td = threaddata();
+
+  i = findsock(sock);
+  if (i == -1 || !td->socklist[i].ssl)
+    return NULL;
+  return SSL_get_peer_certificate(td->socklist[i].ssl);
+}
+
+/* Get the certificate fingerprint of the connection corresponding
+ * to the socket.
+ *
+ * Return value: ptr to the hexadecimal representation of the fingerprint
+ * or NULL in case of error.
+ */
+static char *ssl_getfp_from_cert(X509 *cert)
+{
+  char *p;
+  unsigned int i;
+  static char fp[SHA_DIGEST_LENGTH * 3];
+  unsigned char md[SHA_DIGEST_LENGTH];
+
+  if (!X509_digest(cert, EVP_sha1(), md, &i)) {
+    putlog(LOG_MISC, "*", "ERROR: TLS: ssl_getfp_from_cert(): X509_digest()");
+    X509_free(cert);
+    return NULL;
+  }
+  if (!(p = OPENSSL_buf2hexstr(md, i))) {
+    putlog(LOG_MISC, "*", "ERROR: TLS: ssl_getfp_from_cert(): OPENSSL_buf2hexstr()");
+    X509_free(cert);
+    return NULL;
+  }
+  strlcpy(fp, p, sizeof fp);
+  OPENSSL_free(p);
+
+  return fp;
+}
+
+/* Get the certificate fingerprint of the connection corresponding
+ * to the socket.
+ *
+ * Return value: ptr to the hexadecimal representation of the fingerprint
+ * or NULL if there's no certificate associated with the connection or in case
+ * of error.
+ */
+char *ssl_getfp(int sock)
+{
+  char *fp;
+  X509 *cert;
+
+  if (!(cert = ssl_getcert(sock)))
+    return NULL;
+  fp = ssl_getfp_from_cert(cert);
+  X509_free(cert);
+  return fp;
+}
+
 /* Prepares and initializes SSL stuff
  *
  * Creates a context object, supporting SSLv2/v3 & TLSv1 protocols;
@@ -157,6 +222,14 @@ int ssl_init()
           tls_certfile, ERR_error_string(ERR_get_error(), NULL));
       fatal("Unable to load TLS certificate (ssl-certificate config setting)!", 0);
     }
+    /* TODO: sha256 fingerprint
+     *       print this fingerprint to every user / every partyline login
+     *       maybe only print it when webui is enabled */
+
+    putlog(LOG_MISC, "*", "Certificate loaded: %s (sha1 fingerprint %s)",
+           tls_certfile,
+           ssl_getfp_from_cert(SSL_CTX_get0_certificate(ssl_ctx)));
+
     if (SSL_CTX_use_PrivateKey_file(ssl_ctx, tls_keyfile, SSL_FILETYPE_PEM) != 1) {
       putlog(LOG_MISC, "*", "ERROR: TLS: unable to load private key from %s: %s",
           tls_keyfile, ERR_error_string(ERR_get_error(), NULL));
@@ -319,53 +392,6 @@ char *ssl_fpconv(char *in, char *out)
   return NULL;
 }
 
-/* Get the certificate, corresponding to the connection
- * identified by sock.
- *
- * Return value: pointer to a X509 certificate or NULL if we couldn't
- * look up the certificate.
- */
-static X509 *ssl_getcert(int sock)
-{
-  int i;
-  struct threaddata *td = threaddata();
-
-  i = findsock(sock);
-  if (i == -1 || !td->socklist[i].ssl)
-    return NULL;
-  return SSL_get_peer_certificate(td->socklist[i].ssl);
-}
-
-/* Get the certificate fingerprint of the connection corresponding
- * to the socket.
- *
- * Return value: ptr to the hexadecimal representation of the fingerprint
- * or NULL if there's no certificate associated with the connection.
- */
-char *ssl_getfp(int sock)
-{
-  char *p;
-  unsigned int i;
-  X509 *cert;
-  static char fp[64];
-  unsigned char md[EVP_MAX_MD_SIZE];
-
-  if (!(cert = ssl_getcert(sock)))
-    return NULL;
-  if (!X509_digest(cert, EVP_sha1(), md, &i)) {
-    X509_free(cert);
-    return NULL;
-  }
-  if (!(p = OPENSSL_buf2hexstr(md, i))) {
-    X509_free(cert);
-    return NULL;
-  }
-  strlcpy(fp, p, sizeof fp);
-  OPENSSL_free(p);
-  X509_free(cert);
-  return fp;
-}
-
 /* Get the UID field from the certificate subject name.
  * The certificate is looked up using the socket of the connection.
  *
diff --git a/text/webui.html b/text/webui.html
new file mode 100644
index 000000000..fef5e3ea5
--- /dev/null
+++ b/text/webui.html
@@ -0,0 +1,51 @@
+
+
+
+  
+    
+    Eggdrop
+  
+  
+    

+    
+    
+  
+

From 052416361d2036d036de12aac93a674fe34edb4e Mon Sep 17 00:00:00 2001
From: Michael Ortmann 
Date: Tue, 7 May 2024 01:22:58 +0200
Subject: [PATCH 02/21] Enhance log

---
 src/tls.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/src/tls.c b/src/tls.c
index a71eabe2f..f755c6bb6 100644
--- a/src/tls.c
+++ b/src/tls.c
@@ -965,12 +965,20 @@ int ssl_handshake(int sock, int flags, int verify, int loglevel, char *host,
     return 0;
   }
   if ((err = ERR_peek_error())) {
-    putlog(data->loglevel, "*",
-           "TLS: handshake failed due to the following error: %s",
-           ERR_reason_error_string(err));
-    debug0("TLS: handshake failed due to the following errors: ");
-    while ((err = ERR_get_error()))
-      debug1("TLS: %s", ERR_error_string(err, NULL));
+    if (ERR_GET_LIB(ERR_peek_error()) == ERR_LIB_SSL &&
+        ERR_GET_REASON(err) == SSL_R_HTTP_REQUEST)
+      /* TODO: check if this is really a webui port, report source host (info in dcc[i]?) and give hint to use https:// */
+      putlog(LOG_MISC, "*", "TLS: error: plain HTTP request received on an SSL port, sock %i", sock);
+    else {
+      putlog(data->loglevel, "*",
+             "TLS: handshake failed due to the following error: %s",
+             ERR_reason_error_string(err));
+      debug0("TLS: handshake failed due to the following errors: ");
+      while ((err = ERR_get_error())) {
+	printf("err = %ul\n", err);
+        debug1("TLS: %s", ERR_error_string(err, NULL));
+      }
+    }
   }
 
   /* Attempt failed, cleanup and abort */

From 3758fca3745aaf56594d4eed14d0ba83af8297b1 Mon Sep 17 00:00:00 2001
From: Michael Ortmann 
Date: Tue, 7 May 2024 01:25:52 +0200
Subject: [PATCH 03/21] Remove debug log

---
 src/net.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/net.c b/src/net.c
index 4c80dde89..ba10f9515 100644
--- a/src/net.c
+++ b/src/net.c
@@ -1182,8 +1182,6 @@ int sockgets(char *s, int *len)
     s[0] = 0;
     return ret;
   }
-  debug5("sockread sock %i return %i >>%s<< %i binary? %i", socklist[ret].sock, ret, xx, *len, socklist[ret].flags & SOCK_BINARY);
-  debug1("flags %i", socklist[ret].flags);
   /* sockread can return binary data while socket still has connectflag, process first */
   if (socklist[ret].flags & SOCK_BINARY && *len > 0) {
     socklist[ret].flags &= ~SOCK_CONNECT;

From 54e10f77fe9b0bca64e94c94b2a3eb2c69a40e81 Mon Sep 17 00:00:00 2001
From: Michael Ortmann 
Date: Tue, 7 May 2024 03:51:54 +0200
Subject: [PATCH 04/21] Update Copyright

---
 src/mod/webui.mod/webui.c    | 4 ++--
 src/mod/webui.mod/webui.html | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/mod/webui.mod/webui.c b/src/mod/webui.mod/webui.c
index d5da2e6cb..26311a75d 100644
--- a/src/mod/webui.mod/webui.c
+++ b/src/mod/webui.mod/webui.c
@@ -2,8 +2,8 @@
  * webui.c -- part of webui.mod
  */
 /*
- * Copyright (C) 2023 Michael Ortmann MIT License
- * Copyright (C) 2023 Eggheads Development Team
+ * Copyright (C) 2023 - 2024 Michael Ortmann MIT License
+ * Copyright (C) 2024 Eggheads Development Team
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
diff --git a/src/mod/webui.mod/webui.html b/src/mod/webui.mod/webui.html
index eb9cd7f2a..b5696e7b1 100644
--- a/src/mod/webui.mod/webui.html
+++ b/src/mod/webui.mod/webui.html
@@ -1,7 +1,7 @@
 
 
 

From 063fc5c8a001c3397e9183084ae5470a5452ff84 Mon Sep 17 00:00:00 2001
From: Michael Ortmann 
Date: Tue, 7 May 2024 17:46:45 +0200
Subject: [PATCH 05/21] Renamed webui.html -> text/webui.html

---
 src/mod/webui.mod/{ => text}/webui.html | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename src/mod/webui.mod/{ => text}/webui.html (100%)

diff --git a/src/mod/webui.mod/webui.html b/src/mod/webui.mod/text/webui.html
similarity index 100%
rename from src/mod/webui.mod/webui.html
rename to src/mod/webui.mod/text/webui.html

From e23e9f291e397dbe5fdd3c22bb6a13b4b36e039f Mon Sep 17 00:00:00 2001
From: Michael Ortmann 
Date: Tue, 7 May 2024 18:15:19 +0200
Subject: [PATCH 06/21] add character encoding

---
 src/mod/webui.mod/text/webui.html | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/mod/webui.mod/text/webui.html b/src/mod/webui.mod/text/webui.html
index b5696e7b1..b0f7e562e 100644
--- a/src/mod/webui.mod/text/webui.html
+++ b/src/mod/webui.mod/text/webui.html
@@ -5,8 +5,11 @@
 -->
 
 
-  Eggdrop
-  
+  
+    
+    Eggdrop
+  
+