├── LICENSE ├── src ├── ssl.h ├── net.h ├── engine_cli.h ├── Makefile.am ├── engine_cli.c ├── packet.h ├── cli.h ├── ssl.c ├── protocol.h ├── engine.h ├── gsrn_cli.c ├── utils.h ├── net.c ├── gopt.h ├── cli.c ├── gsrnd.h ├── gsrnd.c ├── s.c ├── protocol.c ├── packet.c ├── proto_cli.h ├── peer.h ├── utils.c ├── common.h ├── engine_server.c ├── engine_client.c ├── engine.c └── peer.c ├── ChangeLog ├── Makefile.am ├── monitor ├── Dockerfile ├── stats.sh ├── funcs ├── ping_gsrn.sh └── heartbeat.sh ├── deploy ├── gsrnd.service ├── gsrn-hb.service ├── provision.sh ├── deploy.sh └── gsrnd_start.sh ├── README.md ├── bootstrap ├── README2.md ├── .github └── workflows │ └── release-push.yaml └── configure.ac /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright by THC. No permission is given in any form or fashion to do anything with this code. 3 | 4 | FUCK YOU. 5 | 6 | -------------------------------------------------------------------------------- /src/ssl.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __GSRN_SSL_H__ 3 | #define __GSRN_SSL_H__ 1 4 | 5 | const char *SSL_strerror(int err); 6 | 7 | #endif // !__GSRN_SSL_H__ -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 1.0.15 2 | * NULL reference inn gsrn_listen 3 | 4 | 1.0.12 5 | * Cake scheduler 6 | * TCP KeepAlive & SEND-Q/RECV-Q exhaustion 7 | 8 | 1.0.1-dev - 2021-06-08 9 | * initial release. 10 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = src 2 | dist_bin_SCRIPTS = deploy/gsrnd_start.sh monitor/heartbeat.sh 3 | EXTRA_DIST = README.md config bootstrap LICENSE ChangeLog deploy/provision.sh deploy/gsrnd.service deploy/gsrn-hb.service 4 | -------------------------------------------------------------------------------- /src/net.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __GSRN_NET_H___ 3 | #define __GSRN_NET_H___ 1 4 | 5 | int fd_net_bind(int fd, uint32_t ip, uint16_t port); 6 | int fd_net_listen(int fd, uint32_t ip, uint16_t port); 7 | int fd_new_socket(int type); 8 | int fd_net_accept(int listen_fd); 9 | 10 | #endif // !__GSRN_NET_H__ -------------------------------------------------------------------------------- /src/engine_cli.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __GSRN_ENGINE_CLI_H__ 3 | #define __GSRN_ENGINE_CLI_H__ 1 4 | 5 | void cb_bev_status_cli(struct bufferevent *bev, short what, void *arg); 6 | void cb_bev_write_cli(struct bufferevent *bev, void *arg); 7 | void cb_bev_read_cli(struct bufferevent *bev, void *arg); 8 | 9 | #endif // !__GSRN_ENGINE_CLI_H__ 10 | -------------------------------------------------------------------------------- /monitor/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | COPY heartbeat.sh funcs / 4 | COPY gsocket_latest_all.deb /tmp 5 | RUN apt update -y && \ 6 | apt install -y --no-install-recommends \ 7 | openssl && \ 8 | apt clean && \ 9 | rm -rf /var/lib/apt/lists/ && \ 10 | dpkg -i --ignore-depends sshfs /tmp/gsocket_latest_all.deb && \ 11 | echo DONE 12 | 13 | ENTRYPOINT ["/heartbeat.sh"] 14 | CMD ["gs1.thc.org", "gs2.thc.org", "gs3.thc.org"] 15 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = gsrnd gsrn_cli 2 | 3 | gsrnd_SOURCES = gsrnd.c utils.c net.c engine.c packet.c peer.c protocol.c cli.c engine_cli.c engine_server.c 4 | gsrnd_CFLAGS = @CFLAGS_STATIC@ 5 | 6 | gsrn_cli_SOURCES = gsrn_cli.c utils.c net.c engine_cli.c packet.c cli.c engine_client.c 7 | gsrn_cli_CFLAGS = @CFLAGS_STATIC@ 8 | 9 | noinst_HEADERS = common.h gsrnd.h utils.h net.h engine.h packet.h peer.h protocol.h cli.h gopt.h proto_cli.h engine_cli.h 10 | 11 | -------------------------------------------------------------------------------- /deploy/gsrnd.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Global Socket Relay Network Server 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | LimitNOFILE=infinity 8 | #LimitCORE=infinity 9 | Restart=always 10 | RestartSec=10 11 | #Environment="LD_LIBRARY_PATH=/sec/usr/lib" 12 | #Environment="GS_LIMIT=100Mbit" 13 | ExecStartPre=/bin/bash /usr/bin/gsrnd_start.sh 14 | ExecStart=/usr/bin/gsrnd -p22 -p25 -p53 -p67 -p80 -p443 -p7350 15 | StandardOutput=journal 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /src/engine_cli.c: -------------------------------------------------------------------------------- 1 | // Command Line Interface to configure gsrnd, retrieve status and log 2 | // information. 3 | // Linked against gsrnd and gsrn_cli 4 | 5 | #include "common.h" 6 | #include "utils.h" 7 | #include "net.h" 8 | #include "cli.h" 9 | #include "packet.h" 10 | #include "engine_cli.h" 11 | #include "gopt.h" 12 | 13 | void 14 | cb_bev_read_cli(struct bufferevent *bev, void *arg) 15 | { 16 | struct _cli *c = (struct _cli *)arg; 17 | PKT_dispatch(&c->pkt, bufferevent_get_input(bev)); 18 | } 19 | 20 | void 21 | cb_bev_write_cli(struct bufferevent *bev, void *arg) 22 | { 23 | // struct _cli *c = (struct _cli *)arg; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Global Socket Relay 2 | 3 | [YOU ARE LIKELY WRONG HERE. YOU WANT TO GO TO https://www.github.com/hackerschoice/gsocket.](https://www.github.com/hackerschoice/gsocket) 4 | 5 | This repository is the Global Socket Relay. There is rarely the need for you to run a Global Socket Relay. *YOU CAN RUN GSOCKET WITHOUT RUNNING A RELAY*. 6 | 7 | **[CLICK HERE TO USE GSOCKET.](https://www.github.com/hackerschoice/gsocket)** 8 | 9 | This repository is mostly for code review only. If you are sure you are at the right location then [click here to continue](README2.md). 10 | (We do encourage your opinion and encourage you to contribute. We are friendly to all opinions. Join us on Telegram). 11 | -------------------------------------------------------------------------------- /src/packet.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __GSRN_PACKET_H__ 3 | #define __GSRN_PACKET_H__ 1 4 | 5 | typedef void (*pkt_cb_t)(struct evbuffer *evbuf, size_t len, void *arg); 6 | 7 | typedef struct 8 | { 9 | int type; // current type 10 | pkt_cb_t funcs[256]; 11 | size_t lengths[256]; 12 | void *args[256]; 13 | int is_init; 14 | int is_malloc; 15 | } PKT; 16 | 17 | PKT *PKT_new(void); 18 | int PKT_init(PKT *pkt); 19 | void PKT_setcb(PKT *pkt, uint8_t type, size_t len, pkt_cb_t func, void *arg); 20 | void PKT_delcb(PKT *pkt, uint8_t type); 21 | void PKT_dispatch(PKT *pkt, struct evbuffer *evbuf); 22 | void PKT_set_void(PKT *pkt); 23 | void PKT_free(PKT *pkt); 24 | 25 | #endif // !__GSRN_PACKET_H__ -------------------------------------------------------------------------------- /monitor/stats.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | BASEDIR="$(cd "$(dirname "${0}")" || exit; pwd)" 4 | source "${BASEDIR}/funcs" 5 | 6 | date_bin="date" 7 | command -v gdate >/dev/null && date_bin="gdate" 8 | unset GSOCKET_IP 9 | 10 | [ -z "$VERBOSE" ] && echo >&2 "Use VERBOSE=[12] for verbose output" 11 | cmd="" 12 | [ "${VERBOSE:-0}" -eq 1 ] && cmd='(echo -e "stats\nlist server"; sleep 1) | gsrn_cli;' 13 | [ "${VERBOSE:-0}" -gt 1 ] && cmd="(echo -e 'stats\nlist cli'; sleep 1) | gsrn_cli;" 14 | cmd+='echo -e "netstat EST: \e[0;33m$(netstat -ant | grep EST | wc -l)\e[0m";' 15 | 16 | for h in "${HOSTS[@]}"; do 17 | echo "=====${h}=====" 18 | ssh "${SSH_ARGS[@]}" "${h%%.*}" "$cmd" 19 | done 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/cli.h: -------------------------------------------------------------------------------- 1 | #ifndef __GSRN_CLI_H__ 2 | #define __GSRN_CLI_H__ 1 3 | 4 | #include "packet.h" 5 | #include "proto_cli.h" 6 | 7 | struct _cli 8 | { 9 | struct bufferevent *bev; 10 | struct evbuffer *eb; 11 | PKT pkt; 12 | 13 | int flags; 14 | }; 15 | 16 | #define FL_CLI_IS_CONNECTED (0x01) 17 | #define FL_CLI_IS_LOGSTREAM (0x02) 18 | 19 | struct _cli *CLI_new(int fd, SSL *ssl, int is_server); 20 | void CLI_free(struct _cli *c); 21 | void CLI_write(struct _cli *c, struct evbuffer *eb); 22 | void CLI_payload(struct _cli *c, uint8_t type, uint16_t len, const void *payload); 23 | void CLI_msg(struct _cli *c, const void *msg, size_t sz); 24 | void CLI_printf(struct _cli *c, const char *fmt, ...); 25 | 26 | #endif // !__GSRN_CLI_H__ -------------------------------------------------------------------------------- /deploy/gsrn-hb.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Global Socket Relay Network Heartbeat 3 | After=network.target 4 | StartLimitIntervalSec=0 5 | 6 | [Service] 7 | Type=simple 8 | #LimitNOFILE=infinity 9 | #LimitCORE=infinity 10 | #WorkingDirectory=/tmp 11 | Restart=always 12 | RestartSec=60 13 | #Need root for port 443 14 | User=gsnet 15 | Environment="LD_LIBRARY_PATH=/sec/usr/lib" 16 | Environment="PATH=/sec/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 17 | ExecStart=/sec/usr/bin/heartbeat.sh 18 | StandardOutput=journal 19 | 20 | [Install] 21 | WantedBy=multi-user.target 22 | 23 | # /etc/systemd/system/gsrn-hb.service 24 | # systemctl enable gsrn-hb 25 | # systemctl start gsrn-hb 26 | # journalctl -u gsrn-hb -f --no-hostname 27 | -------------------------------------------------------------------------------- /src/ssl.c: -------------------------------------------------------------------------------- 1 | 2 | #include "common.h" 3 | 4 | const char * 5 | SSL_strerror(int err) 6 | { 7 | switch (err) 8 | { 9 | case SSL_ERROR_NONE: 10 | return D_GRE("None"); 11 | case SSL_ERROR_ZERO_RETURN: 12 | return "ZERO_RETURN (close-notify recv)"; 13 | case SSL_ERROR_WANT_READ: 14 | return D_YEL("WANT_READ"); 15 | case SSL_ERROR_WANT_WRITE: 16 | return D_YEL("WANT_WRITE"); 17 | case SSL_ERROR_WANT_CONNECT: 18 | return "WANT CONNECT"; 19 | case SSL_ERROR_WANT_ACCEPT: 20 | return "WANT ACCEPT"; 21 | case SSL_ERROR_WANT_X509_LOOKUP: 22 | return "WANT X509 LOOKUP"; 23 | #ifdef SSL_ERROR_WANT_ASYNC 24 | case SSL_ERROR_WANT_ASYNC: 25 | return "WANT_ASYNC"; 26 | #endif 27 | case SSL_ERROR_SYSCALL: 28 | return D_RED("SYSCALL"); 29 | case SSL_ERROR_SSL: 30 | return D_RED("FATAL ERROR"); 31 | } 32 | return "unknown :/"; 33 | } 34 | -------------------------------------------------------------------------------- /monitor/funcs: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | MIN() 4 | { 5 | echo $(($1>$2 ? $2 : $1)) 6 | } 7 | 8 | ERREXIT() 9 | { 10 | local code 11 | code="$1" 12 | 13 | [[ $? -ne 0 ]] && code="$?" 14 | [[ -z $code ]] && code=99 15 | 16 | shift 1 17 | [[ -n $1 ]] && echo -e >&2 "ERROR: $*" 18 | 19 | exit "$code" 20 | } 21 | 22 | XMD5() { md5sum "${1}" 2>/dev/null | cut -f1 -d' '; } 23 | 24 | # Load the list of GSRN hosts if no arg is specified. File looks like this: 25 | # DOMAIN=thc.org 26 | # HOSTS+=("gs1.${DOMAIN}") 27 | # HOSTS+=("gs2.${DOMAIN}") 28 | # HOSTS+=("gs3.${DOMAIN}") 29 | # HOSTS+=("gs4.${DOMAIN}") 30 | # HOSTS+=("gs5.${DOMAIN}") 31 | [[ ${#@} -eq 0 ]] && source "${BASEDIR}/.gsrn_hosts" 32 | # Otherwise use hosts from command line arguments. 33 | [[ -z $HOSTS ]] && HOSTS=("${@}") 34 | [[ -z $HOSTS ]] && { echo -e "$(basename "${0}") [gsrn-hostname] ..."; exit 255; } 35 | : 36 | -------------------------------------------------------------------------------- /src/protocol.h: -------------------------------------------------------------------------------- 1 | #ifndef __GSRN_PROTOCOL_H__ 2 | #define __GSRN_PROTOCOL_H__ 1 3 | 4 | #include "peer.h" 5 | 6 | void GSRN_send_status(struct _peer *p, uint8_t err_type, uint8_t err_code, const char *msg); 7 | void GSRN_send_start(struct _peer *p, uint8_t flags); 8 | void GSRN_send_pong(struct _peer *p, uint8_t *payload); 9 | 10 | #define GSRN_send_status_fatal(p, c, m) GSRN_send_status(p, GS_STATUS_TYPE_FATAL, c, m); 11 | #define GSRN_send_status_warn(p, c, m) GSRN_send_status(p, GS_STATUS_TYPE_WARN, c, m); 12 | void GSRN_change_state(struct _peer *p, uint8_t state); 13 | 14 | #define GSRN_STATE_INIT (0x00) 15 | #define GSRN_STATE_LISTEN (0x01) 16 | #define GSRN_STATE_CONNECT (0x02) 17 | #define GSRN_STATE_BUDDY_UP (0x03) 18 | #define GSRN_STATE_ACCEPT (0x04) 19 | #define GSRN_STATE_FINISHED (0x05) 20 | 21 | 22 | #endif // !__GRSRN_PROTOCOL_H__ 23 | -------------------------------------------------------------------------------- /src/engine.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __GSRN_ENGINE_H__ 3 | #define __GSRN_ENGINE_H__ 1 4 | 5 | void cb_accept(int fd, short ev, void *arg); 6 | void cb_accept_ssl(int fd, short ev, void *arg); 7 | void cb_accept_cnc(int fd, short ev, void *arg); 8 | 9 | void cb_bev_status(struct bufferevent *bev, short what, void *arg); 10 | void cb_bev_write(struct bufferevent *bev, void *arg); 11 | void cb_bev_read(struct bufferevent *bev, void *arg); 12 | 13 | void cb_gsrn_protocol_error(struct evbuffer *eb, size_t len, void *arg); 14 | void cb_gsrn_listen(struct evbuffer *eb, size_t len, void *arg); 15 | void cb_gsrn_connect(struct evbuffer *eb, size_t len, void *arg); 16 | void cb_gsrn_accept(struct evbuffer *eb, size_t len, void *arg); 17 | void cb_gsrn_ping(struct evbuffer *eb, size_t len, void *arg); 18 | 19 | void cb_shutdown_complete(void *p); 20 | 21 | void init_engine(void); 22 | 23 | #endif // !__GSRN_ENGINE_H__ 24 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | DIE=0 4 | 5 | (autoconf --version) < /dev/null > /dev/null 2>&1 || { 6 | echo 7 | echo "You must have autoconf installed." 8 | DIE=1 9 | } 10 | 11 | # libtool --version check not done... 12 | 13 | (automake --version) < /dev/null > /dev/null 2>&1 || { 14 | echo 15 | echo "You must have automake installed." 16 | DIE=1 17 | } 18 | 19 | if test "$DIE" -eq 1; then 20 | exit 1 21 | fi 22 | 23 | echo Removing old files... 24 | rm -f configure Makefile Makefile.in tools/Makefile tools/Makesfile.in src/Makefile src/Makefile.in config.h config.status aclocal.m4 config.cache config.log 25 | [ -d "config" ] && rm -rf config 26 | mkdir config 27 | 28 | echo "aclocal -I ." 29 | aclocal -I . 30 | if test $? -ne 0; then 31 | exit 1 32 | fi 33 | 34 | # glibtoolize -c 35 | 36 | echo "autoheader" 37 | autoheader 38 | if test $? -ne 0; then 39 | exit 1 40 | fi 41 | echo "automake --foreign --add-missing -Wno-syntax" 42 | automake --foreign --copy --add-missing -Wno-syntax 43 | if test $? -ne 0; then 44 | exit 1 45 | fi 46 | echo "autoconf" 47 | autoconf 48 | echo "BOOTSTRAP complete" 49 | 50 | -------------------------------------------------------------------------------- /src/gsrn_cli.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "gopt.h" 3 | 4 | // struct _gc gc; 5 | 6 | static void 7 | usage_cli(char *err) 8 | { 9 | if (err) 10 | fprintf(stderr, "%s", err); 11 | 12 | fprintf(stderr, "Version %s\n" 13 | " -p CLI port [default=%d]\n" 14 | " -d CLI IP address [default=127.0.0.1]\n" 15 | "", VERSION, CLI_DEFAULT_PORT); 16 | 17 | if (err) 18 | exit(255); 19 | 20 | exit(0); 21 | } 22 | 23 | static void 24 | do_getopt_cli(int argc, char *argv[]) 25 | { 26 | int c; 27 | 28 | opterr = 0; 29 | while ((c = getopt(argc, argv, "p:i:vl")) != -1) 30 | { 31 | switch (c) 32 | { 33 | case 'p': 34 | gopt.port_cli = atoi(optarg); 35 | break; 36 | case 'h': 37 | gopt.ip_cli = inet_addr(optarg); 38 | break; 39 | case 'v': 40 | gopt.verbosity += 1; 41 | break; 42 | case 'l': 43 | gopt.flags |= GSR_FL_LOGSTREAM; 44 | break; 45 | default: 46 | usage_cli("Wrong parameter\n"); 47 | } 48 | } 49 | } 50 | 51 | int 52 | main(int argc, char *argv[]) 53 | { 54 | init_defaults(PRG_CLI); 55 | 56 | do_getopt_cli(argc, argv); 57 | init_vars(); 58 | 59 | event_base_dispatch(gopt.evb); 60 | } -------------------------------------------------------------------------------- /deploy/provision.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | # Bootstrapping a bare-linux until we are ready 4 | 5 | ln -sf /dev/null ~/.bash_history 6 | apt update 7 | apt install -y tmux jq build-essential curl iptables ripgrep fd-find bc htop conntrack libevent-dev libssl-dev net-tools gsocket 8 | ln -s /usr/bin/fdfind /usr/bin/fd 9 | echo nf_conntrack >>/etc/modules 10 | echo "net.ipv4.tcp_syncookies=0" >>/etc/sysctl.conf 11 | 12 | ### Check journal file 13 | ps -A --sort -rss -o comm,pmem,rss | head -n 5 14 | sed 's/#SystemMaxFileSize.*/SystemMaxFileSize=50M/' -i /etc/systemd/journald.conf 15 | sed 's/#SystemMaxUse.*/SystemMaxUse=10M/' -i /etc/systemd/journald.conf 16 | systemctl restart systemd-journald 17 | 18 | sed 's/.*Port 22$/Port 64222/' -i /etc/ssh/sshd_config 19 | systemctl restart ssh 20 | 21 | useradd gsnet 22 | cp -a /etc/skel /home/gsnet 23 | mkdir /home/gsnet/.ssh 24 | touch /home/gsnet/.ssh/authorized_keys 25 | chown -R gsnet:gsnet /home/gsnet 26 | 27 | [[ -L /etc/resolv.conf ]] && { 28 | systemctl stop systemd-resolved 29 | systemctl disable systemd-resolved 30 | rm /etc/resolv.conf 31 | echo -e "nameserver 1.1.1.1\nnameserver 8.8.8.8" >/etc/resolv.conf 32 | } 33 | 34 | -------------------------------------------------------------------------------- /monitor/ping_gsrn.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | BASEDIR="$(cd "$(dirname "${0}")" || exit; pwd)" 4 | source "${BASEDIR}/funcs" 5 | 6 | date_bin="date" 7 | command -v gdate >/dev/null && date_bin="gdate" 8 | unset GSOCKET_IP 9 | 10 | [[ "$($date_bin +%s%N)" == *N ]] && { 11 | echo >&2 "No GNU-date found. $date_bin +%s%N is bad. Try brew install coreutils" 12 | exit 255 13 | } 14 | 15 | gsrn_ping() 16 | { 17 | local n=0 18 | SECRET=$(gs-netcat -g) 19 | 20 | export GSOCKET_HOST="$1" 21 | export SECRET 22 | VARBACK=$(mktemp) 23 | 24 | GSPID="$(gs-netcat -s "$SECRET" -l -e cat 2>/dev/null >/dev/null /dev/null; do 30 | ! [[ $x =~ ^17 ]] && continue 31 | D=$(($($date_bin +%s%N) - x)) 32 | printf "%.3fms " "$(echo "$D"/1000000 | bc -l)" 33 | [ "$D" -gt "$M" ] && continue 34 | M="$D" 35 | echo "$M" >"$VARBACK" 36 | done 37 | D=$(<"$VARBACK") 38 | rm -f "${VARBACK:?}" 39 | printf "\t\tMIN %.3fms\n" "$(echo "$D"/1000000 | bc -l)" 40 | 41 | kill "$GSPID" 42 | } 43 | 44 | for h in "${HOSTS[@]}"; do 45 | gsrn_ping "$h" 46 | done 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __GSRN_UTILS_H__ 3 | #define __GSRN_UTILS_H__ 1 4 | 5 | typedef enum 6 | { 7 | PRG_GSRND = 0, 8 | PRG_CLI = 1 9 | } prg_t; 10 | 11 | struct _port 12 | { 13 | TAILQ_ENTRY(_port) ll; 14 | uint16_t port; 15 | struct event *ev; 16 | }; 17 | 18 | TAILQ_HEAD(_port_listhead, _port); 19 | 20 | const char *strx128(uint128_t x, char *val, size_t sz); 21 | const char *strx128x(uint128_t x); 22 | void init_defaults(prg_t prg); 23 | void init_vars(void); 24 | const char *BEV_strerror(short what); 25 | void PORTSQ_add(struct _port_listhead *head, int port); 26 | void PORTSQ_listen(struct _port_listhead *head, uint32_t ip, uint16_t port_default, event_callback_fn cb_func); 27 | void PORTSQ_close(struct _port_listhead *head); 28 | void PORTSQ_free(struct _port_listhead *head); 29 | void close_del_ev(struct event **evptr); 30 | const char *PEER_L_name(uint8_t pl_id); 31 | uint128_t GS_hexto128(const char *hex); 32 | char * GS_addr128hex(char *dst, uint128_t addr); 33 | const char *gs_log_ipport2str_r(char *dst, size_t dsz, uint32_t ip, uint16_t port); 34 | const char *gs_log_ipport2str(uint32_t ip, uint16_t port); 35 | const char *gs_log_in_addr2str_r(char *dst, size_t dsz, struct sockaddr_in *addr_in); 36 | const char *gs_log_in_addr2str(struct sockaddr_in *addr_in); 37 | int fd_limit_init(void); 38 | int fd_limit_unlimited(void); 39 | int fd_limit_limited(void); 40 | 41 | #endif // !__GSRN_UTILS_H__ 42 | -------------------------------------------------------------------------------- /src/net.c: -------------------------------------------------------------------------------- 1 | 2 | #include "common.h" 3 | 4 | int 5 | fd_net_bind(int fd, uint32_t ip, uint16_t port) 6 | { 7 | struct sockaddr_in addr; 8 | int ret; 9 | 10 | memset(&addr, 0, sizeof addr); 11 | addr.sin_family = AF_INET; 12 | addr.sin_addr.s_addr = htonl(ip); 13 | addr.sin_port = htons(port); 14 | 15 | ret = bind(fd, (struct sockaddr *)&addr, sizeof addr); 16 | if (ret < 0) 17 | return ret; 18 | 19 | return 0; 20 | } 21 | 22 | int 23 | fd_net_listen(int fd, uint32_t ip, uint16_t port) 24 | { 25 | int ret; 26 | 27 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof (int)); 28 | 29 | ret = fd_net_bind(fd, ip, port); 30 | if (ret < 0) 31 | return ret; 32 | 33 | ret = listen(fd, SOMAXCON); 34 | if (ret != 0) 35 | return -1; 36 | 37 | return 0; 38 | } 39 | 40 | int 41 | fd_new_socket(int type) 42 | { 43 | int fd; 44 | int ret; 45 | 46 | fd = socket(PF_INET, type, 0); 47 | if (fd < 0) 48 | return -2; 49 | 50 | ret = fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL, 0)); 51 | if (ret != 0) 52 | return -2; 53 | 54 | return fd; 55 | } 56 | 57 | int 58 | fd_net_accept(int listen_fd) 59 | { 60 | int sox; 61 | int val = 1; 62 | // int ret; 63 | 64 | sox = accept(listen_fd, NULL, NULL); 65 | if (sox < 0) 66 | { 67 | return -2; 68 | } 69 | 70 | setsockopt(sox, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof (val)); 71 | // val = 60; 72 | // setsockopt(sox, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof (val)); 73 | // val = 15; 74 | // setsockopt(sox, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof (val)); 75 | // val = 4; 76 | // setsockopt(sox, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof (val)); 77 | // ret = fcntl(sox, F_SETFL, O_NONBLOCK | fcntl(sox, F_GETFL, 0)); 78 | // if (ret != 0) 79 | // return -2; 80 | 81 | return sox; 82 | } 83 | 84 | -------------------------------------------------------------------------------- /src/gopt.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __GSRN_GOPT_H__ 3 | #define __GSRN_GOPT_H__ 1 4 | 5 | #include "peer.h" 6 | #include "utils.h" 7 | 8 | 9 | struct _tree_mgr 10 | { 11 | int n_nodes; 12 | // e.g. A single list can have >1 peers listening 13 | int n_entries[MAX_LISTS_BY_ADDR]; // Total Listening/Waiting/Connected 14 | // e.g. Record how many (different) addr are listening 15 | int n_uniq[MAX_LISTS_BY_ADDR]; // Uniq 16 | void *tree; 17 | // peers_walk() will call *func for each peer in the binary-tree. 18 | walk_peers_func_t walk_peers_func; 19 | void *walk_peers_func_arg; 20 | }; 21 | 22 | struct _gopt 23 | { 24 | prg_t prg; // GSRND or CLI 25 | FILE *err_fp; 26 | FILE *log_fp; 27 | SSL_CTX *ssl_ctx; 28 | uint32_t ip_cli; // 127.0.0.1 29 | uint16_t port_cli; // Only used by gsrn_cli.c 30 | uint16_t port_cnc; // Concentrator port (no SSL) 31 | uint32_t ip_cnc; // IP of concentrator 32 | 33 | struct _port_listhead ports_head; 34 | struct _port_listhead ports_cli_head; 35 | 36 | int verbosity; 37 | uint32_t flags; 38 | struct event_base *evb; // libevent base 39 | // struct event *ev_listen; // Listening socket event 40 | // struct event *ev_listen_ssl; // Listening socket event 41 | struct event *ev_listen_con; // Listening socket event 42 | int is_concentrator; 43 | struct rlimit rlim_fd; 44 | 45 | uint64_t usec_now; 46 | // binary trees for listening and waiting peers. 47 | struct _tree_mgr t_peers; 48 | 49 | // CLI output buffer 50 | struct evbuffer *cli_out_evb; 51 | }; 52 | #define GSR_FL_LOGSTREAM (0x01) 53 | 54 | // Server (daemon) globals 55 | struct _gd 56 | { 57 | struct event *ev_listen_cli; 58 | // Unique ID (like PID) for linked-list entries 59 | uint32_t peer_id; 60 | int is_log_ip; 61 | // Minimum accepted protocol version 62 | uint8_t min_version_major; 63 | uint8_t min_version_minor; 64 | }; 65 | 66 | #endif // !__GSRN_GOPT_H__ 67 | -------------------------------------------------------------------------------- /src/cli.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "engine_cli.h" 3 | #include "cli.h" 4 | #include "gopt.h" 5 | 6 | // Only used by server (not cli) 7 | struct _cli *logstream_cli; 8 | 9 | struct _cli * 10 | CLI_new(int fd, SSL *ssl, int is_server) 11 | { 12 | struct _cli *c; 13 | 14 | c = calloc(1, sizeof *c); 15 | if (c == NULL) 16 | return NULL; 17 | 18 | PKT_init(&c->pkt); 19 | // c->fd = fd; 20 | 21 | // if (c->ssl) 22 | // c->bev = bufferevent_openssl_socket_new(gopt.evb, -1, c->ssl, BUFFEREVENT_SSL_ACCEPTING/CONNECTING, BEV_OPT_DEFER_CALLBACKS) 23 | // else 24 | c->bev = bufferevent_socket_new(gopt.evb, fd, BEV_OPT_DEFER_CALLBACKS | BEV_OPT_CLOSE_ON_FREE); 25 | bufferevent_setcb(c->bev, cb_bev_read_cli, cb_bev_write_cli, cb_bev_status_cli, c); 26 | 27 | c->eb = evbuffer_new(); 28 | 29 | 30 | return c; 31 | } 32 | 33 | void 34 | CLI_free(struct _cli *c) 35 | { 36 | evbuffer_free(c->eb); 37 | XBEV_FREE(c->bev); 38 | // XCLOSE(c->fd); 39 | XFREE(c); 40 | } 41 | 42 | void 43 | CLI_write(struct _cli *c, struct evbuffer *eb) 44 | { 45 | bufferevent_write_buffer(c->bev, eb); 46 | } 47 | 48 | // Send a variable length message 49 | void 50 | CLI_payload(struct _cli *c, uint8_t type, uint16_t payload_len, const void *payload) 51 | { 52 | struct _cli_hdr_tlv hdr; 53 | 54 | hdr.type = type; 55 | hdr.len = payload==NULL?0:htons(payload_len); 56 | evbuffer_add(c->eb, &hdr, sizeof hdr); 57 | if (payload != NULL) 58 | evbuffer_add(c->eb, payload, payload_len); 59 | 60 | CLI_write(c, c->eb); 61 | } 62 | 63 | void 64 | CLI_msg(struct _cli *c, const void *msg, size_t sz) 65 | { 66 | evbuffer_add(c->eb, msg, sz); 67 | 68 | CLI_write(c, c->eb); 69 | } 70 | 71 | void 72 | CLI_printf(struct _cli *c, const char *fmt, ...) 73 | { 74 | va_list ap; 75 | int rv; 76 | char buf[1024]; 77 | 78 | va_start(ap, fmt); 79 | rv = vsnprintf(buf, sizeof buf, fmt, ap); 80 | va_end(ap); 81 | rv = MIN(sizeof buf, rv); 82 | 83 | CLI_payload(c, GSRN_CLI_TYPE_MSG, rv, buf); 84 | } 85 | -------------------------------------------------------------------------------- /src/gsrnd.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __GSRN_GSRND_H__ 3 | #define __GSRN_GSRND_H__ 1 4 | 5 | // Seconds to wait for first GSRN message 6 | #define GSRN_1STMSG_TIMEOUT (5) 7 | // up to 1.3.38 use 60*2 and thereafter 45 8 | #define GSRN_MSG_TIMEOUT (MAX(60*2, GSRN_DEFAULT_PING_INTERVAL) + 10) 9 | #define GSRN_FLUSH_TV_TIMEOUT (3) 10 | // Seconds to wait for ACCEPT after START was send in buddy_up() 11 | #define GSRN_ACCEPT_TIMEOUT (5) 12 | #define GSRN_SHORTWAIT_TIMEOUT (5) 13 | // 2h and 5 seconds. This can be further restricted by systemwide 14 | // TCP Keepalive messages rather than by application layer. 15 | #define GSRN_IDLE_TIMEOUT (60*60*2+5) 16 | // listening token to 'linger' for 15 seconds before allowing new 17 | // listening gsocket with different token (same gsocket addr). 18 | #define GSRN_SHUTDOWN_IDLE_TIMEOUT (10) 19 | // Delay BAD_AUTH error by DELAY+random(JITTER) if 2 or more BAD_AUTH within 20 | // BAD_AUTH_WINDOW happen. This happens when user starts 'gs-nc -l' multiple 21 | // times and using the same GS_SECRET. 22 | #define GSRN_BAD_AUTH_WINDOW (15) 23 | #define GSRN_BAD_AUTH_DELAY (240) // Wait DELAY+JITTER seconds before returning BAD-AUTH 24 | #define GSRN_BAD_AUTH_JITTER (10) 25 | 26 | // Keep at least 10 FD's as reserved so if we run out of FD's that 27 | // CLI can still connect 28 | #define GSRN_FD_RESERVE (10) 29 | 30 | #define TVSEC(sec) &(struct timeval){sec, 0} // convert 'sec' to 'struct timeval' 31 | #define TVMSEC(msec) &(struct timeval){msec / 1000, (msec % 1000) * 1000} // convert 'msec' to 'struct timeval' 32 | 33 | 34 | struct _gstats 35 | { 36 | uint64_t start_usec; // TS when GSRN started 37 | uint64_t reset_usec; // TS when stat got reset last. 38 | uint64_t n_gs_connect; // GS-CONNECT count 39 | uint64_t n_gs_listen; // GS-LISTEN count 40 | uint64_t n_bad_auth; // BAD-AUTH count 41 | uint64_t n_gs_refused; // GS Buddy not listening 42 | }; 43 | 44 | #endif // !__GSRN_GSRND_H__ 45 | -------------------------------------------------------------------------------- /README2.md: -------------------------------------------------------------------------------- 1 | # Global Socket Relay 2 | 3 | Chances are that you are wrong here. Click here if you want to learn about gsocket: [https://www.github.com/hackerschoice/gsocket](https://www.github.com/hackerschoice.com/gsocket). You do **NOT** need this to use gsocket. 4 | 5 | *You have been warned...* 6 | 7 | **Installation from static binary** 8 | ```shell 9 | DIR="/opt/gsrn" 10 | [ ! -d "${DIR:?}" ] && mkdir -p "${DIR}" 11 | set -o pipefail 12 | [ -f /etc/systemd/resolved.conf ] && { grep -qm1 =udp /etc/systemd/resolved.conf || { 13 | echo "DNSStubListener=udp" >>/etc/systemd/resolved.conf 14 | systemctl stop systemd-resolved; }; } 15 | curl -SsfL "https://github.com/hackerschoice/gsocket-relay/releases/latest/download/gsrnd-linux-$(uname -m)" -o "${DIR:?}/gsrnd" \ 16 | && curl -SsfL "https://github.com/hackerschoice/gsocket-relay/releases/latest/download/gsrn_cli-linux-$(uname -m)" -o "${DIR:?}/gsrn_cli" \ 17 | && curl -SsfL "https://github.com/hackerschoice/gsocket-relay/raw/refs/heads/main/deploy/gsrnd_start.sh" -o "${DIR:?}/gsrnd_start.sh" \ 18 | && (cd "${DIR}" && chmod 755 gsrnd gsrn_cli gsrnd_start.sh) \ 19 | && curl -SsfL 'https://github.com/hackerschoice/gsocket-relay/raw/refs/heads/main/deploy/gsrnd.service' | sed "s|/usr/bin/|${DIR:?}/|" >/etc/systemd/system/gsrnd.service \ 20 | && systemctl enable gsrnd \ 21 | && systemctl start gsrnd && systemctl status gsrnd \ 22 | && journalctl -u gsrnd -f --no-hostname 23 | ``` 24 | 25 | --- 26 | 27 | ### OLD STUFF 28 | 29 | **Installation from source** 30 | ``` 31 | $ sudo apt install make automake autoconf gcc libevent-dev libssl-dev 32 | $ git clone --depth 1 https://github.com/hackerschoice/gsocket-relay.git 33 | $ cd gsocket-relay 34 | $ git clone --depth 1 https://github.com/hackerschoice/gsocket 35 | $ (cd gsocket; ./bootstrap && ./configure && make) 36 | $ ./bootstrap && ./configure && make 37 | ``` 38 | 39 | **Running the relay** 40 | ``` 41 | $ src/gsrnd 42 | ``` 43 | 44 | **Testing** 45 | ``` 46 | $ cd gsocket/tests 47 | $ GSOCKET_IP=127.0.0.1 ./run_gs_tests.sh 48 | ``` 49 | -------------------------------------------------------------------------------- /src/gsrnd.c: -------------------------------------------------------------------------------- 1 | 2 | #if 0 3 | 4 | TODO: 5 | - Complete GSRN-SSL_shutdown (it is initiated by client so server does not have to do anything?) 6 | - Implement FAST-CONNECT where all data after CONNECT is app-layer data. 7 | - Implement propper event timeouts and do not piggy back on bufferevents read timeout (verify source) 8 | #endif 9 | 10 | #include "common.h" 11 | #include "utils.h" 12 | #include "peer.h" 13 | #include "gopt.h" 14 | 15 | static void 16 | usage(char *err) 17 | { 18 | if (err) 19 | fprintf(stderr, "%s", err); 20 | 21 | fprintf(stderr, "Version %s\n" 22 | " -p TCP listening port [default=%d]\n" 23 | " -m TCP port for cli [default=%d]\n" 24 | " -C Listen for cnc connections [default=no]\n" 25 | " -c TCP port of cnc [default=%d]\n" 26 | " -d Destination IP of CNC server [default=none]\n" 27 | " -L Logfile [default=stderr]\n" 28 | " -v Verbosity level\n" 29 | " -a Log IP addresses [disabled by default]\n" 30 | "", VERSION, GSRN_DEFAULT_PORT, CLI_DEFAULT_PORT, GSRN_DEFAULT_PORT_CON); 31 | 32 | if (err) 33 | exit(255); 34 | 35 | exit(0); 36 | } 37 | 38 | static void 39 | do_getopt(int argc, char *argv[]) 40 | { 41 | int c; 42 | 43 | opterr = 0; 44 | while ((c = getopt(argc, argv, "L:p:c:d:m:hva")) != -1) 45 | { 46 | switch (c) 47 | { 48 | case 'L': 49 | gopt.log_fp = fopen(optarg, "a"); 50 | if (gopt.log_fp == NULL) 51 | ERREXIT("fopen(%s): %s\n", optarg, strerror(errno)); 52 | gopt.err_fp = gopt.log_fp; 53 | fprintf(gopt.log_fp, "Log file started.\n"); 54 | break; 55 | case 'p': 56 | DEBUGF("Adding port %d\n", atoi(optarg)); 57 | PORTSQ_add(&gopt.ports_head, atoi(optarg)); 58 | // gopt.port = atoi(optarg); 59 | break; 60 | case 'm': 61 | PORTSQ_add(&gopt.ports_cli_head, atoi(optarg)); 62 | // gopt.port_cli = atoi(optarg); 63 | break; 64 | case 'C': 65 | gopt.is_concentrator = 1; 66 | break; 67 | case 'c': 68 | gopt.port_cnc = atoi(optarg); 69 | break; 70 | case 'd': 71 | gopt.ip_cnc = inet_addr(optarg); 72 | break; 73 | case 'v': 74 | gopt.verbosity += 1; 75 | break; 76 | case 'a': 77 | gd.is_log_ip = 1; 78 | break; 79 | case 'h': 80 | usage(NULL); 81 | break; 82 | default: 83 | usage("Wrong parameter\n"); 84 | } 85 | } 86 | } 87 | 88 | int 89 | main(int argc, char *argv[]) 90 | { 91 | init_defaults(PRG_GSRND); 92 | do_getopt(argc, argv); 93 | init_vars(); 94 | 95 | event_base_dispatch(gopt.evb); 96 | exit(0); 97 | return 255; // NOT REACHED 98 | } 99 | -------------------------------------------------------------------------------- /src/s.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | 3 | struct _gopt gopt; 4 | struct _g_debug_ctx g_dbg_ctx; 5 | 6 | int 7 | main() 8 | { 9 | 10 | BIO *sbio, *bbio, *acpt, *out; 11 | int len; 12 | char tmpbuf[1024]; 13 | SSL_CTX *ctx; 14 | SSL *ssl; 15 | 16 | /* XXX Seed the PRNG if needed. */ 17 | 18 | ctx = SSL_CTX_new(TLS_server_method()); 19 | if (!SSL_CTX_use_certificate_file(ctx, "server.pem", SSL_FILETYPE_PEM) 20 | || !SSL_CTX_use_PrivateKey_file(ctx, "server.pem", SSL_FILETYPE_PEM) 21 | || !SSL_CTX_check_private_key(ctx)) { 22 | fprintf(stderr, "Error setting up SSL_CTX\n"); 23 | ERR_print_errors_fp(stderr); 24 | exit(1); 25 | } 26 | 27 | /* XXX Other things like set verify locations, EDH temp callbacks. */ 28 | 29 | /* New SSL BIO setup as server */ 30 | sbio = BIO_new_ssl(ctx, 0); 31 | BIO_get_ssl(sbio, &ssl); 32 | if (ssl == NULL) { 33 | fprintf(stderr, "Can't locate SSL pointer\n"); 34 | ERR_print_errors_fp(stderr); 35 | exit(1); 36 | } 37 | 38 | SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); 39 | bbio = BIO_new(BIO_f_buffer()); 40 | sbio = BIO_push(bbio, sbio); 41 | acpt = BIO_new_accept("4433"); 42 | 43 | /* 44 | * By doing this when a new connection is established 45 | * we automatically have sbio inserted into it. The 46 | * BIO chain is now 'swallowed' by the accept BIO and 47 | * will be freed when the accept BIO is freed. 48 | */ 49 | BIO_set_accept_bios(acpt, sbio); 50 | out = BIO_new_fp(stdout, BIO_NOCLOSE); 51 | 52 | /* Setup accept BIO */ 53 | if (BIO_do_accept(acpt) <= 0) { 54 | fprintf(stderr, "Error setting up accept BIO\n"); 55 | ERR_print_errors_fp(stderr); 56 | exit(1); 57 | } 58 | 59 | if (BIO_do_accept(acpt) <= 0) { 60 | fprintf(stderr, "Error in connection\n"); 61 | ERR_print_errors_fp(stderr); 62 | exit(1); 63 | } 64 | 65 | /* We only want one connection so remove and free accept BIO */ 66 | sbio = BIO_pop(acpt); 67 | BIO_free_all(acpt); 68 | 69 | if (BIO_do_handshake(sbio) <= 0) { 70 | fprintf(stderr, "Error in SSL handshake\n"); 71 | ERR_print_errors_fp(stderr); 72 | exit(1); 73 | } 74 | 75 | BIO_puts(sbio, "HTTP/1.0 200 OK\r\nContent-type: text/plain\r\n\r\n"); 76 | BIO_puts(sbio, "\r\nConnection Established\r\nRequest headers:\r\n"); 77 | BIO_puts(sbio, "--------------------------------------------------\r\n"); 78 | 79 | for ( ; ; ) { 80 | len = BIO_gets(sbio, tmpbuf, 1024); 81 | if (len <= 0) 82 | break; 83 | BIO_write(sbio, tmpbuf, len); 84 | BIO_write(out, tmpbuf, len); 85 | /* Look for blank line signifying end of headers*/ 86 | if (tmpbuf[0] == '\r' || tmpbuf[0] == '\n') 87 | break; 88 | } 89 | 90 | BIO_puts(sbio, "--------------------------------------------------\r\n"); 91 | BIO_puts(sbio, "\r\n"); 92 | BIO_flush(sbio); 93 | BIO_free_all(sbio); 94 | 95 | } 96 | 97 | -------------------------------------------------------------------------------- /deploy/deploy.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | 4 | # GS_FORCE=1 - Overwrite even if version exists 5 | VER_RELAY="$(grep AC_INIT ../configure.ac | cut -f3 -d"[" | cut -f1 -d']')" 6 | VER_GS="$(grep AC_INIT ../../gsocket/configure.ac | cut -f3 -d"[" | cut -f1 -d']')" 7 | 8 | deploy_host() 9 | { 10 | local port 11 | port=$1 12 | host=$2 13 | [[ -z $host ]] && { echo >&2 "Bad parameters"; exit 2; } 14 | echo "Deploying to ${host}:${port}...." 15 | ssh -p $port "gsnet@${host}" "{ [[ \"v${VER_GS}\" = \$(gsocket/tools/gs-netcat -h 2>&1 | grep ^OpenSSL |cut -f9 -d' '|tr -d ')') ]] || exit 255; } && exit 0" || { 16 | echo "Updating gsocket-${VER_GS}..." 17 | scp -P $port ../../gsocket/gsocket-${VER_GS}.tar.gz "gsnet@${host}:" 18 | ssh -p $port "gsnet@${host}" "tar xfz gsocket-${VER_GS}.tar.gz && \ 19 | { [[ -h gsocket ]] && rm -f gsocket; true; } && \ 20 | ln -sf gsocket-${VER_GS} gsocket && \ 21 | (cd gsocket && ./configure --prefix=\$HOME/usr && make all install)" 22 | } || exit 255 23 | 24 | [[ -n $GS_FORCE ]] || { ssh -p $port "gsnet@${host}" "[[ \"${VER_RELAY}\" = \$(usr/bin/gsrnd -h 2>&1 | grep ^Vers | cut -f2 -d' ') ]] && exit 0" && { echo "gsocket-relay-${VER_RELAY} already installed. Skipping."; return; }; } 25 | scp -P $port ../gsocket-relay-${VER_RELAY}.tar.gz "gsnet@${host}:" 26 | ssh -p $port "gsnet@${host}" "tar xfz gsocket-relay-${VER_RELAY}.tar.gz && \ 27 | { [[ -d \$HOME/usr ]] || mkdir \$HOME/usr; } && \ 28 | { [[ -f usr/bin/gsrnd ]] && { BDIR=\"backup-\$(usr/bin/gsrnd -h 2>&1 | grep ^Vers | cut -f2 -d' ')_\$(date '+%s')\"; mkdir \"\$BDIR\"; mv usr/bin/gsrnd usr/bin/gsrn_cli \"\$BDIR\"; }; true; } && \ 29 | { [[ -h gsocket-relay-${VER_RELAY}/gsocket ]] && rm -f gsocket-relay-${VER_RELAY}/gsocket; true; } && \ 30 | (cd gsocket-relay-${VER_RELAY} && ln -s ../gsocket gsocket && ./configure --prefix=\$HOME/usr && make all install) && \ 31 | { [[ \"${VER_RELAY}\" = \$(usr/bin/gsrnd -h 2>&1 | grep ^Vers | cut -f2 -d' ') ]] || { echo \"${host}: Cant execute gsrnd\"; exit 255; } } && \ 32 | exit 0 33 | " 34 | } 35 | 36 | 37 | restart_host() 38 | { 39 | local port 40 | port=$1 41 | host=$2 42 | [[ -z $host ]] && { echo >&2 "Bad parameters"; exit 2; } 43 | ssh -p $port "root@${host}" "systemctl restart gsrnd && sleep 1 && systemctl status gsrnd" 44 | } 45 | 46 | # wget https://www.openssl.org/source/openssl-1.1.1k.tar.gz 47 | # tar xfvz openssl-1.1.1k.tar.gz 48 | # (cd openssl-1.1.1k && ./Configure linux-$(uname -m) --prefix=$HOME/usr && make all install_sw) 49 | # sudo yum -y update 50 | # sudo yum -y install gcc libevent-devel 51 | # 52 | # sudo apt -y install libevent-dev 53 | echo "Relay : ${VER_RELAY}" 54 | echo "GSocket: ${VER_GS}" 55 | 56 | hosts="gs1 gs2 gs3" 57 | [[ -n "$1" ]] && hosts="$@" 58 | 59 | g_port=64222 60 | 61 | for h in $hosts; do 62 | [[ "$h" =~ 'g16' ]] && g_port=22 63 | 64 | deploy_host "$g_port" "$h" || exit 254 65 | done 66 | 67 | echo "Press Enter to restart gsrnd or Ctrl-C to quit." 68 | read 69 | for h in $hosts; do 70 | [[ "$h" =~ 'g16' ]] && g_port=22 71 | restart_host "$g_port" "$h" || exit 254 72 | done 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/protocol.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "engine.h" 3 | #include "protocol.h" 4 | 5 | #define GSRN_msg_init(msg, xtype) do {memset(msg, 0, sizeof *msg); (msg)->type = xtype;}while(0) 6 | 7 | void 8 | GSRN_send_status(struct _peer *p, uint8_t err_type, uint8_t code, const char *err) 9 | { 10 | struct _gs_status msg; 11 | 12 | GSRN_msg_init(&msg, GS_PKT_TYPE_STATUS); 13 | msg.err_type = err_type; 14 | msg.code = code; 15 | 16 | if (err != NULL) 17 | snprintf((char *)msg.msg, sizeof msg.msg, "%s", err); 18 | 19 | bufferevent_write(p->bev, &msg, sizeof msg); 20 | } 21 | 22 | void 23 | GSRN_send_start(struct _peer *p, uint8_t flags) 24 | { 25 | struct _gs_start msg; 26 | 27 | GSRN_msg_init(&msg, GS_PKT_TYPE_START); 28 | msg.flags = flags; 29 | 30 | bufferevent_write(p->bev, &msg, sizeof msg); 31 | } 32 | 33 | void 34 | GSRN_send_pong(struct _peer *p, uint8_t *payload) 35 | { 36 | struct _gs_pong msg; 37 | 38 | GSRN_msg_init(&msg, GS_PKT_TYPE_PONG); 39 | memcpy(msg.payload, payload, sizeof msg.payload); 40 | 41 | bufferevent_write(p->bev, &msg, sizeof msg); 42 | } 43 | 44 | // Set messages that we are allowed to accept given current-type 45 | void 46 | GSRN_change_state(struct _peer *p, uint8_t state) 47 | { 48 | switch (state) 49 | { 50 | case GSRN_STATE_INIT: 51 | // Allowed 52 | PKT_setcb(&p->pkt, GS_PKT_TYPE_LISTEN , sizeof (struct _gs_listen) , cb_gsrn_listen, p); 53 | PKT_setcb(&p->pkt, GS_PKT_TYPE_CONNECT, sizeof (struct _gs_connect), cb_gsrn_connect, p); 54 | PKT_setcb(&p->pkt, GS_PKT_TYPE_PING , sizeof (struct _gs_ping) , cb_gsrn_ping, p); 55 | // Not allowed 56 | PKT_setcb(&p->pkt, GS_PKT_TYPE_ACCEPT, sizeof (struct _gs_accept), cb_gsrn_protocol_error, p); 57 | PKT_setcb(&p->pkt, GS_PKT_TYPE_START , sizeof (struct _gs_start) , cb_gsrn_protocol_error, p); 58 | PKT_setcb(&p->pkt, GS_PKT_TYPE_PONG , sizeof (struct _gs_pong) , cb_gsrn_protocol_error, p); 59 | break; 60 | case GSRN_STATE_LISTEN: 61 | case GSRN_STATE_CONNECT: 62 | PKT_setcb(&p->pkt, GS_PKT_TYPE_LISTEN, sizeof (struct _gs_listen), cb_gsrn_protocol_error, p); 63 | PKT_setcb(&p->pkt, GS_PKT_TYPE_CONNECT, sizeof (struct _gs_connect), cb_gsrn_protocol_error, p); 64 | break; 65 | case GSRN_STATE_ACCEPT: 66 | // Accept no further GSRN messages 67 | PKT_setcb(&p->pkt, GS_PKT_TYPE_LISTEN, sizeof (struct _gs_listen), cb_gsrn_protocol_error, p); 68 | PKT_setcb(&p->pkt, GS_PKT_TYPE_CONNECT, sizeof (struct _gs_connect), cb_gsrn_protocol_error, p); 69 | PKT_setcb(&p->pkt, GS_PKT_TYPE_PING, sizeof (struct _gs_ping), cb_gsrn_protocol_error, p); 70 | PKT_setcb(&p->pkt, GS_PKT_TYPE_ACCEPT, sizeof (struct _gs_accept), cb_gsrn_protocol_error, p); 71 | break; 72 | case GSRN_STATE_BUDDY_UP: 73 | // Disallow 74 | PKT_setcb(&p->pkt, GS_PKT_TYPE_LISTEN, sizeof (struct _gs_listen), cb_gsrn_protocol_error, p); 75 | PKT_setcb(&p->pkt, GS_PKT_TYPE_CONNECT, sizeof (struct _gs_connect), cb_gsrn_protocol_error, p); 76 | // Ignore 77 | PKT_setcb(&p->pkt, GS_PKT_TYPE_PING, sizeof (struct _gs_ping), NULL, p); 78 | // Allow 79 | PKT_setcb(&p->pkt, GS_PKT_TYPE_ACCEPT, sizeof (struct _gs_accept), cb_gsrn_accept, p); 80 | break; 81 | case GSRN_STATE_FINISHED: 82 | // Allowed 83 | // Ignored 84 | PKT_setcb(&p->pkt, GS_PKT_TYPE_PING , sizeof (struct _gs_ping) , NULL, p); 85 | // Not allowed 86 | PKT_setcb(&p->pkt, GS_PKT_TYPE_CONNECT, sizeof (struct _gs_connect), cb_gsrn_protocol_error, p); 87 | PKT_setcb(&p->pkt, GS_PKT_TYPE_LISTEN , sizeof (struct _gs_listen) , cb_gsrn_protocol_error, p); 88 | PKT_setcb(&p->pkt, GS_PKT_TYPE_ACCEPT , sizeof (struct _gs_accept) , cb_gsrn_protocol_error, p); 89 | PKT_setcb(&p->pkt, GS_PKT_TYPE_START , sizeof (struct _gs_start) , cb_gsrn_protocol_error, p); 90 | PKT_setcb(&p->pkt, GS_PKT_TYPE_PONG , sizeof (struct _gs_pong) , cb_gsrn_protocol_error, p); 91 | break; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /monitor/heartbeat.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | [[ $BASH_VERSION == "3."* ]] && { echo >&2 "BASH to old (${BASH_VERSION}). Try /usr/local/bin/bash ${0}."; exit 255; } 4 | # Test if all servers are alive and functional 5 | INTERVAL=160 # Check all servers every n seconds 6 | 7 | BASEDIR="$(cd "$(dirname "${0}")" || exit; pwd)" 8 | source "${BASEDIR}/funcs" || exit 9 | [[ -f "${BASEDIR}/.env" ]] && source "${BASEDIR}/.env" 10 | 11 | command -v gs-netcat >/dev/null || ERREXIT 254 "Not found: gs-netcat" 12 | command -v md5sum >/dev/null || ERREXIT 254 "Not found: md5sum" 13 | command -v jq >/dev/null || ERREXIT 253 "Not found: jq" 14 | command -v gdate >/dev/null && date(){ gdate "$@"; } 15 | # Set these in .env 16 | [[ -z $TG_TOKEN ]] && ERREXIT 252 "TG_TOKEN not set." 17 | [[ -z $TG_CHATID ]] && ERREXIT 251 "TG_CHATID not set." 18 | 19 | [[ -z $MYNAME ]] && { 20 | MYNAME=$(hostname) 21 | MYNAME=${MYNAME%%.*} 22 | } 23 | 24 | [ -z "$DOMAIN" ] && DOMAIN=thc.org 25 | 26 | waitkp() 27 | { 28 | local x 29 | local rounds 30 | # How many seconds the test should take 31 | local sleep_wd 32 | sleep_wd=15 33 | 34 | x=0 35 | rounds=$((sleep_wd * 2)) 36 | 37 | while :; do 38 | kill -0 $1 &>/dev/null || return 39 | sleep 0.5 40 | x=$((x+1)) 41 | [[ $x -gt $rounds ]] && break 42 | done 43 | kill $1 44 | } 45 | 46 | 47 | tg_msg() 48 | { 49 | local str 50 | [[ -z "$TG_TOKEN" ]] && return 51 | 52 | str=$(curl -fLSs --retry 3 --max-time 15 --data-urlencode "text=\[$(date '+%F %T' -u)]\[${MYNAME:-GS}] $*" "https://api.telegram.org/bot${TG_TOKEN}/sendMessage?chat_id=${TG_CHATID}&parse_mode=Markdown" | jq '.ok') 53 | [[ $str != "true" ]] && ERREXIT 249 "Telegram API failed...." 54 | return 0 55 | } 56 | 57 | heartbeat() 58 | { 59 | # Start a server 60 | local server 61 | server=$1 62 | sn="${server//./_}" 63 | 64 | rm -f /tmp/gsrn_heartbeat_server_err.txt /tmp/gsrn_heartbeat_server_out.txt /tmp/gsrn_heartbeat_client_err.txt /tmp/gsrn_heartbeat_client_out.txt 65 | 66 | # Start 2 background processes 67 | GSPID1="$(sh -c 'GSOCKET_HOST="'$1.${DOMAIN}'" gs-netcat -s "'$SECRET'" -l /tmp/gsrn_heartbeat_server_err.txt >/tmp/gsrn_heartbeat_server_out.txt & echo ${!}')" 68 | GSPID2="$(sh -c 'GSOCKET_HOST="'$1.${DOMAIN}'" gs-netcat -s "'$SECRET'" -w /tmp/gsrn_heartbeat_client_err.txt >/tmp/gsrn_heartbeat_client_out.txt & echo ${!}')" 69 | 70 | # Wait max sleep_wd seconds for them to complete. 71 | waitkp "$GSPID1" 72 | waitkp "$GSPID2" 73 | 74 | # Compare results 75 | if [[ "$md5_in" == "$(XMD5 /tmp/gsrn_heartbeat_server_out.txt)" ]] && [[ "$md5_in" == "$(XMD5 /tmp/gsrn_heartbeat_client_out.txt)" ]]; then 76 | OK_COUNT=$((OK_COUNT+=1)) 77 | [[ -n ${failed["${sn}"]} ]] && { 78 | tg_msg "✅ OK: Server: '$server'" 79 | } 80 | unset failed["${sn}"] 81 | return 82 | fi 83 | 84 | # Return if we already reported this error 85 | [[ -n ${failed["${sn}"]} ]] && return 86 | 87 | # Report error ONCE until test is OK again 88 | failed["${sn}"]=1 89 | tg_msg '🔥 FAILED: Server '"${server}"': 90 | _=====Server=====_ 91 | ``` 92 | '"$(grep -v ^= /tmp/gsrn_heartbeat_server_err.txt)"' 93 | ```_=====Client=====_ 94 | ``` 95 | '"$(grep -v ^= /tmp/gsrn_heartbeat_client_err.txt)"' 96 | ```' 97 | 98 | } 99 | 100 | init_vars() 101 | { 102 | echo "$(date) Hello World" >/tmp/gsrn_heartbeat_in.txt 103 | md5_in="$(XMD5 /tmp/gsrn_heartbeat_in.txt)" 104 | SECRET="$(gs-netcat -g)" 105 | } 106 | 107 | tg_msg '🏁 Starting *monitor* for '"${HOSTS[*]}" 108 | 109 | declare -A failed 110 | ts_last="$(date +%s)" 111 | while :; do 112 | init_vars 113 | 114 | OK_COUNT=0 115 | unset ERR_MSG 116 | for h in "${HOSTS[@]}"; do 117 | heartbeat "$h" 118 | done 119 | # [[ -n $ERR_MSG ]] && { 120 | # tg_msg "$ERR_MSG" 121 | # } 122 | 123 | [[ "$OK_COUNT" -eq "${#HOSTS[@]}" ]] && echo "OK_COUNT=$OK_COUNT" 124 | # ERREXIT 0 "DEBUG TESTING" 125 | ts_now="$(date +%s)" 126 | 127 | sleep $((INTERVAL - (ts_now - ts_last))) 128 | ts_last=$((ts_last + INTERVAL)) 129 | done 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /.github/workflows/release-push.yaml: -------------------------------------------------------------------------------- 1 | name: Static Build 2 | on: [workflow_dispatch] 3 | 4 | permissions: 5 | contents: write 6 | 7 | env: 8 | OPENSSL_VER: 1.1.1w 9 | LIBEVENT_VER: 2.1.12-stable 10 | TAG: v1.0.15 11 | # TAG: ${{ github.ref == 'ref/heads/main' && 'latest' || github.ref }} 12 | 13 | jobs: 14 | Linux-Cross: 15 | strategy: 16 | matrix: 17 | arch: [x86_64, aarch64] 18 | include: 19 | - arch: x86_64 20 | cc: x86_64-linux-musl 21 | ssl: linux-generic64 22 | - arch: aarch64 23 | cc: aarch64-linux-musl 24 | ssl: linux-generic64 25 | runs-on: ubuntu-latest 26 | container: 27 | image: muslcc/x86_64:${{ matrix.arch }}-linux-musl 28 | steps: 29 | - uses: actions/checkout@v3 30 | 31 | - name: Add build dependencies 32 | run: | 33 | apk add --update --no-cache --no-progress tar git autoconf automake make curl bsd-compat-headers file 34 | 35 | - name: Cache SSL + LibEvent 36 | id: cachessl 37 | uses: actions/cache@v3 38 | with: 39 | path: /opt 40 | key: ${{ matrix.arch }}-ssl 41 | 42 | - name: Generate OpenSSL + Libevent 43 | if: steps.cachessl.outputs.cache-hit != 'true' 44 | run: | 45 | curl -SsfL https://www.openssl.org/source/openssl-${OPENSSL_VER:-1.1.1w}.tar.gz | tar -xzf - -C /tmp/ 46 | ( cd /tmp/openssl-${OPENSSL_VER:-1.1.1w} && \ 47 | ./Configure --prefix=/opt no-tests no-dso no-threads no-shared ${{ matrix.ssl }} && \ 48 | make install_sw ) 49 | curl -SsfL https://github.com/libevent/libevent/releases/download/release-${LIBEVENT_VER:-2.1.12-stable}/libevent-${LIBEVENT_VER:-2.1.12-stable}.tar.gz | tar -xzf - -C /tmp 50 | ( cd /tmp/libevent-${LIBEVENT_VER:-2.1.12-stable} && \ 51 | PKG_CONFIG_PATH=/opt/lib/pkgconfig CFLAGS="-I/opt/include" LDFLAGS="-L/opt/lib" ./configure --prefix=/opt --enable-static --host=${{ matrix.arch }} && \ 52 | make install ) 53 | rm -rf /tmp/openssl-${OPENSSL_VER:-1.1.1w} /opt/bin/openssl /opt/bin/c_rehash 54 | 55 | - name: SaveCache 56 | if: steps.cachessl.outputs.cache-hit != 'true' 57 | uses: actions/cache/save@v3 58 | with: 59 | path: /opt 60 | key: ${{ matrix.arch }}-ssl 61 | 62 | - name: Adding gsocket 63 | run: | 64 | git clone -b beta --depth 1 --single-branch https://github.com/hackerschoice/gsocket.git 65 | cd gsocket 66 | ./bootstrap 67 | ./configure --prefix=/opt --enable-realprefix=/usr --enable-static --host=${{ matrix.arch }} 68 | make all 69 | 70 | - name: Compiling GSRND 71 | run: | 72 | ./bootstrap 73 | LDFLAGS="-L/opt/lib" LIBS="-lssl -lcrypto" ./configure --prefix=/opt --enable-static --host=${{ matrix.arch }} 74 | make 75 | file src/gsrnd 76 | mv src/gsrnd gsrnd-linux-${{ matrix.arch }} 77 | mv src/gsrn_cli gsrn_cli-linux-${{ matrix.arch }} 78 | 79 | - name: Upload to release 80 | uses: svenstaro/upload-release-action@v2 81 | with: 82 | repo_token: ${{ secrets.GITHUB_TOKEN }} 83 | file_glob: true 84 | file: gsrn*-linux-${{ matrix.arch }} 85 | overwrite: true 86 | tag: ${{ env.TAG }} 87 | 88 | # jobs: 89 | # Build: 90 | # runs-on: ubuntu-latest 91 | # steps: 92 | # - uses: actions/checkout@v3 93 | 94 | # - name: Setup Alpine x86_64 95 | # uses: jirutka/setup-alpine@v1 96 | 97 | # - name: Add build dependencies 98 | # run: | 99 | # apk add --update --no-cache --no-progress git gcc autoconf automake bash make curl libc-dev libevent-dev openssl-dev bsd-compat-headers 100 | # shell: alpine.sh --root {0} 101 | 102 | # - name: Add gsocket 103 | # run: | 104 | # git clone -b beta --depth 1 --single-branch https://github.com/hackerschoice/gsocket.git 105 | # cd gsocket 106 | # ./bootstrap 107 | # ./configure --prefix=/opt --enable-realprefix=/usr 108 | # make all 109 | # shell: alpine.sh --root {0} 110 | 111 | # - name: Compiling GSRN 112 | # run: | 113 | # ./bootstrap 114 | # ./configure --prefix=/opt --enable-static 115 | # make 116 | # mv src/gsrnd gsrnd-linux-x86_64 117 | # mv src/gsrn_cli gsrn_cli-linux-x86_64 118 | # shell: alpine.sh --root {0} 119 | 120 | # - name: Upload to release 121 | # uses: svenstaro/upload-release-action@v2 122 | # with: 123 | # repo_token: ${{ secrets.GITHUB_TOKEN }} 124 | # file_glob: true 125 | # file: gsrn*-linux-x86_64 126 | # overwrite: true 127 | # tag: ${{ env.TAG }} 128 | #### FAILS because alpine does not have libevent_openssl 129 | -------------------------------------------------------------------------------- /deploy/gsrnd_start.sh: -------------------------------------------------------------------------------- 1 | 2 | #! /usr/bin/env bash 3 | 4 | BASEDIR="$(cd "$(dirname "${0}")" || exit; pwd)" 5 | # source "${BASEDIR}/funcs" || exit 6 | [[ -f "${BASEDIR}/.env" ]] && source "${BASEDIR}/.env" || echo >&2 ".env not found. TG notify disabled." 7 | 8 | [[ -z $MYNAME ]] && { 9 | MYNAME=$(hostname) 10 | MYNAME=${MYNAME%%.*} 11 | } 12 | 13 | tg_msg() 14 | { 15 | local str 16 | [[ -z "$TG_TOKEN" ]] && return 17 | 18 | str=$(curl -fLSs --retry 3 --max-time 15 --data-urlencode "text=\[$(date '+%F %T' -u)]\[${MYNAME:-GS}] $*" "https://api.telegram.org/bot${TG_TOKEN}/sendMessage?chat_id=${TG_CHATID}&parse_mode=Markdown" | jq '.ok') 19 | [[ $str != "true" ]] && return 255 #ERREXIT 249 "Telegram API failed...." 20 | return 0 21 | } 22 | 23 | # Once 24 | ipt() { 25 | local first=$1 26 | 27 | shift 1 28 | iptables -C "$@" 2>/dev/null && return 29 | iptables "$first" "$@" 30 | } 31 | 32 | tg_msg "GSRND started." 33 | 34 | DEV_GW=$(ip route show | grep default | head -n1 | awk '{print $5;}') 35 | TC_ARGS=() 36 | [[ -n $GS_LIMIT ]] && TC_ARGS+=(bandwidth "$GS_LIMIT") 37 | 38 | tc qdisc del dev "$DEV_GW" root 2>/dev/null 39 | tc qdisc add dev "$DEV_GW" root cake "${TC_ARGS[@]}" "dsthost" 40 | unset TC_ARGS 41 | # tc qdisc add dev "$DEV_GW"root handle 11: sfq 42 | # tc filter add dev "$DEV_GW" parent 11: handle 11 flow hash keys dst divisor 2048 43 | 44 | MEM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2;}') 45 | MEM_P80=$((MEM_KB * 80 / 100 / 4 )) 46 | MEM_P75=$((MEM_KB * 75 / 100 / 4 )) 47 | MEM_P70=$((MEM_KB * 70 / 100 / 4 )) 48 | 49 | echo 1048576 >/proc/sys/net/core/somaxconn 50 | echo 1048576 >/proc/sys/net/ipv4/tcp_max_syn_backlog 51 | 52 | # Disabled by default. We dont use conntracking on gsocket-relay servers. 53 | modprobe nf_conntrack 54 | echo 1048576 >/proc/sys/net/netfilter/nf_conntrack_max 55 | P="$(grep -m1 ^Port /etc/ssh/sshd_config | sed -e 's|Port \(.\)|\1|g')" 56 | P="${P:-64222}" 57 | ipt -A INPUT -p tcp --syn -m multiport ! --dports "22,25,53,67,80,443,7350,${P}" -j DROP 58 | ipt -A INPUT -p tcp --dport "${P}" --syn -m connlimit --connlimit-above 8 -j REJECT --reject-with tcp-reset 59 | # Some bad deployments (early version) start hundrets of gsnc -l. The gsrnd puts those into 60 | # BAD-AUTH queue to stop them from flooding the server with SYN. On IPT -j DROP 61 | # the client will wait 130 seconds before giving up. 62 | # ipt -A INPUT -p tcp --syn -m connlimit --connlimit-above 2048 -j DROP 63 | ipt -A INPUT -p tcp --syn -m connlimit --connlimit-above 1024 -j DROP 64 | 65 | # See https://www.frozentux.net/ipsysctl-tutorial/chunkyhtml/tcpvariables.html 66 | echo 60 >/proc/sys/net/ipv4/tcp_keepalive_time 67 | echo 10 >/proc/sys/net/ipv4/tcp_keepalive_intvl 68 | echo 4 >/proc/sys/net/ipv4/tcp_keepalive_probes 69 | 70 | # Reduce to 2. The CLIENT will re-transmit SYN anyway. 71 | echo 2 >/proc/sys/net/ipv4/tcp_synack_retries 72 | # 7=25.4sec, 8=51sec, 9=102.2sec, 10=204.6sec, 11=324.6sec, 12=444.6sec 73 | echo 8 >/proc/sys/net/ipv4/tcp_retries2 74 | 75 | echo 5 >/proc/sys/net/ipv4/tcp_fin_timeout 76 | echo 2 >/proc/sys/net/ipv4/tcp_tw_reuse 77 | echo 1 >/proc/sys/net/ipv4/tcp_no_metrics_save 78 | 79 | echo 10 >/proc/sys/net/netfilter/nf_conntrack_tcp_timeout_time_wait 80 | echo 10 >/proc/sys/net/netfilter/nf_conntrack_tcp_timeout_syn_recv 81 | echo 10 >/proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close_wait 82 | echo 10 >/proc/sys/net/netfilter/nf_conntrack_tcp_timeout_fin_wait 83 | echo 5 >/proc/sys/net/netfilter/nf_conntrack_tcp_timeout_last_ack 84 | echo 1 >/proc/sys/net/netfilter/nf_conntrack_tcp_timeout_close 85 | 86 | # Decrease orphans - Orphaned TCP connections should be killed fast. 87 | # Each can eat up to 64kB 88 | echo 1024 >/proc/sys/net/ipv4/tcp_max_orphans 89 | echo 2 >/proc/sys/net/ipv4/tcp_orphan_retries 90 | # echo 65535 >/proc/sys/net/ipv4/tcp_max_orphans 91 | 92 | # Set this to 80% of physical memory 93 | # low, pressure, high (man 7 tcp) 94 | echo "${MEM_P70} ${MEM_P75} ${MEM_P80}" >/proc/sys/net/ipv4/tcp_mem 95 | 96 | # 4k per socket min. 97 | # min, default, max 98 | #echo 4096 4096 131072 >/proc/sys/net/ipv4/tcp_rmem 99 | # Note: Normally the packets are read immediately and thus no rmem is needed. 100 | # Start with larger buffer. Will be redued when pressure increases. 101 | echo "4096 16384 32768" >/proc/sys/net/ipv4/tcp_rmem 102 | # wmem defines throughput. On a 200ms link between A and B the max speed thus is: 103 | # 1000 / (200 * 2) * wmem_default 104 | # 105 | # with 131072 buffer and 200ms the user can get 327KBps 106 | # 131072 / (0.2 * 2) / 1024 == 320 107 | # On a 4GB server with 80% for TCP and all buffers 108 | # 4 * 1024 * 1024 * 1024 * 0.8 / 131072 == 26,214 connections 109 | #echo 4096 1048576 1048576 >/proc/sys/net/ipv4/tcp_wmem 110 | echo "4096 65536 1048576" >/proc/sys/net/ipv4/tcp_wmem 111 | 112 | grep . /proc/sys/net/ipv4/tcp*mem 113 | 114 | # NOTE: It's started from systemd via: 115 | # ExecStartPre=/bin/bash /home/gsnet/usr/bin/gsrnd_start.sh 116 | # ExecStart=/home/gsnet/usr/bin/gsrnd -p... 117 | 118 | -------------------------------------------------------------------------------- /src/packet.c: -------------------------------------------------------------------------------- 1 | #if 0 2 | 3 | A packet dispatch stack. 4 | 5 | Most of the time, an application wants to perform certain action when receiving a certain 6 | message. 7 | 8 | The caller can configure the stack to have callbacks called when certain messages 9 | are received. 10 | 11 | Fixed length messages and TLV (Type-Lengt-Value) messages are supported. 12 | 13 | The callbacks are set with PKT_setcb() and removed with PKT_delcb(). 14 | 15 | Type type field is 8 bit and the optional length field is 16 bit (or 0 bit for 16 | fixed length messages). 17 | #endif 18 | 19 | #include "common.h" 20 | #include "utils.h" 21 | #include "packet.h" 22 | #include "gopt.h" 23 | 24 | static void 25 | cb_dummy(struct evbuffer *eb, size_t len, void *arg) 26 | { 27 | #ifdef DEBUG 28 | PKT *pkt = (PKT *)arg; 29 | 30 | DEBUGF_R("Unhandled function for type %u\n", pkt->type); 31 | #endif 32 | evbuffer_drain(eb, len); 33 | } 34 | 35 | 36 | int 37 | PKT_init(PKT *pkt) 38 | { 39 | memset(pkt, 0, sizeof *pkt); 40 | int i; 41 | 42 | for (i = 0; i < sizeof pkt->funcs / sizeof *pkt->funcs; i++) 43 | { 44 | PKT_delcb(pkt, i); 45 | } 46 | 47 | pkt->is_init = 1; 48 | return 0; 49 | } 50 | 51 | // Redirect all packets to nirvana but keeping all type's and length fields intact. 52 | void 53 | PKT_set_void(PKT *pkt) 54 | { 55 | int i; 56 | 57 | for (i = 0; i < sizeof pkt->funcs / sizeof *pkt->funcs; i++) 58 | { 59 | pkt->funcs[i] = NULL; 60 | } 61 | } 62 | 63 | PKT * 64 | PKT_new(void) 65 | { 66 | PKT *pkt; 67 | 68 | pkt = malloc(sizeof *pkt); 69 | if (pkt == NULL) 70 | goto err; 71 | 72 | pkt->is_malloc = 1; 73 | if (PKT_init(pkt) != 0) 74 | goto err; 75 | 76 | return pkt; 77 | err: 78 | XFREE(pkt); 79 | return NULL; 80 | } 81 | 82 | void 83 | PKT_free(PKT *pkt) 84 | { 85 | pkt->is_init = 0; 86 | 87 | if (pkt->is_malloc) 88 | { 89 | free(pkt); 90 | } 91 | } 92 | 93 | // Set a callback for message type. 2 kinds of messages are supported: 94 | // Fixed length messsage (len != 0) or a variable length message (TLV; len == 0). 95 | // The format of the (incoming) messages shall look like this: 96 | // 97 | // A. [ 1 octet type ] [ value ] 98 | // B. [ 1 octet type ] [ 2 octet length ] [ variable lenght value ] 99 | // 100 | // The message is expected to be of len length or of variable length if len is 101 | // set to 0. 102 | void 103 | PKT_setcb(PKT *pkt, uint8_t type, size_t len, pkt_cb_t func, void *arg) 104 | { 105 | if (type == 0) 106 | return; 107 | 108 | pkt->funcs[type] = func; 109 | pkt->args[type] = arg; 110 | pkt->lengths[type] = len; 111 | } 112 | 113 | void 114 | PKT_delcb(PKT *pkt, uint8_t type) 115 | { 116 | PKT_setcb(pkt, type, 0 /*variable*/, cb_dummy, pkt); 117 | } 118 | 119 | // Dispatch a single packet (try if there is sufficient data in eb) 120 | static int 121 | dispatch(PKT *pkt, struct evbuffer *eb) 122 | { 123 | size_t eb_sz; 124 | size_t ex_len = 0; // expected length 125 | eb_sz = evbuffer_get_length(eb); 126 | 127 | if (eb_sz <= 0) 128 | goto more_data; 129 | 130 | if (pkt->type == 0) 131 | { 132 | uint8_t type; 133 | evbuffer_copyout(eb, &type, 1); 134 | 135 | // Set the length we need. This is 0 for packets that have 136 | // variable length and are of format TLV 137 | // [ 1 octet Type | 2 octet length | value ] 138 | ex_len = pkt->lengths[type]; 139 | if (ex_len == 0) 140 | { 141 | // HERE: It's a variable length message (TLV) 142 | if (eb_sz < 3) 143 | goto more_data; 144 | uint16_t len; 145 | #if 0 146 | struct evbuffer_ptr ev_pos; 147 | evbuffer_ptr_set(eb, &ev_pos, 1, EVBUFFER_PTR_SET); 148 | evbuffer_copyout_from(eb, &ev_pos, &len, sizeof len); 149 | #endif 150 | uint8_t buf[3]; 151 | evbuffer_copyout(eb, buf, 3); 152 | memcpy(&len, buf+1, sizeof len); 153 | ex_len = ntohs(len) + 3; 154 | // DEBUGF("type=%u Variable Lenght=%zu\n", type, ex_len); 155 | } 156 | 157 | pkt->type = type; 158 | } 159 | 160 | if (eb_sz < ex_len) 161 | goto more_data; 162 | 163 | // Call Callback 164 | // DEBUGF_G("Calling for type=%u ex_len=%zu\n", pkt->type, ex_len); 165 | if (pkt->funcs[pkt->type] != NULL) 166 | (*pkt->funcs[pkt->type])(eb, ex_len, pkt->args[pkt->type]); 167 | 168 | if (evbuffer_get_length(eb) == eb_sz) 169 | { 170 | // Remove data if caller did not remove the data already. 171 | // (e.g. for dummy calls or if funcs == NULL) 172 | evbuffer_drain(eb, ex_len); 173 | } 174 | pkt->type = 0; 175 | 176 | return 0; 177 | more_data: 178 | 179 | return -1; 180 | } 181 | 182 | void 183 | PKT_dispatch(PKT *pkt, struct evbuffer *eb) 184 | { 185 | int ret; 186 | 187 | // Maybe 188 | while (1) 189 | { 190 | ret = dispatch(pkt, eb); 191 | if (ret != 0) 192 | break; 193 | // The callback might call PKT_free() to stop processing 194 | // packets. This happens when an GS-ACCEPT is received (which is the 195 | // last valid PKT message) but the bufferevent subsystem read part 196 | // client's SRP handshake already...do not process this with PKT_dispatch()... 197 | if (pkt->is_init == 0) 198 | break; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/proto_cli.h: -------------------------------------------------------------------------------- 1 | #ifndef __GSRN_PROTO_CLI_H__ 2 | #define __GSRN_PROTO_CLI_H__ 1 3 | 4 | #include "peer.h" 5 | 6 | struct _cli_hdr_tlv 7 | { 8 | // As per packet.h 9 | uint8_t type; // GSRN_CLI_TYPE_* 10 | uint16_t len; // Only used for variable length 11 | char payload[0]; 12 | } __attribute__((__packed__)); 13 | 14 | struct _cli_hdr_fixed 15 | { 16 | uint8_t type; // GSRN_CLI_TYPE_* 17 | char payload[0]; 18 | } __attribute__((__packed__)); 19 | 20 | struct _cli_msg 21 | { 22 | struct _cli_hdr_tlv hdr; 23 | char msg[0]; 24 | } __attribute__((__packed__)); 25 | 26 | struct _cli_log 27 | { 28 | struct _cli_hdr_tlv hdr; 29 | char msg[0]; 30 | } __attribute__((__packed__)); 31 | 32 | struct _cli_shutdown 33 | { 34 | struct _cli_hdr_fixed hdr; 35 | uint8_t opcode; 36 | uint8_t reserved[3]; 37 | uint32_t timer_sec; 38 | } __attribute__((__packed__)); 39 | 40 | struct _cli_kill 41 | { 42 | struct _cli_hdr_fixed hdr; 43 | uint32_t peer_id; 44 | uint128_t addr; 45 | } __attribute__((__packed__)); 46 | 47 | struct _cli_list 48 | { 49 | struct _cli_hdr_fixed hdr; 50 | uint32_t peer_id; 51 | uint128_t addr; 52 | uint8_t opcode; 53 | } __attribute__((__packed__)); 54 | #define GSRN_CLI_OP_LIST_ESTAB (0x01) 55 | #define GSRN_CLI_OP_LIST_LISTEN (0x02) 56 | #define GSRN_CLI_OP_LIST_BAD (0x04) 57 | 58 | struct _cli_stop 59 | { 60 | struct _cli_hdr_fixed hdr; 61 | uint32_t peer_id; 62 | uint128_t addr; 63 | uint8_t opcode; 64 | } __attribute__((__packed__)); 65 | #define GSRN_CLI_OP_STOP_LISTEN_TCP (0x01) 66 | 67 | struct _cli_set 68 | { 69 | struct _cli_hdr_fixed hdr; 70 | uint32_t peer_id; 71 | uint128_t addr; 72 | uint8_t opcode; 73 | uint8_t opvalue1; 74 | uint8_t opvalue2; // NOT USED 75 | uint8_t version_major; 76 | uint8_t version_minor; 77 | uint16_t port; 78 | } __attribute__((__packed__)); 79 | #define GSRN_CLI_OP_SET_PROTO (0x01) 80 | #define GSRN_CLI_OP_SET_LOG_IP (0x02) 81 | #define GSRN_CLI_OP_SET_PORT_CLI (0x03) 82 | #define GSRN_CLI_OP_SET_LOG_VERBOSITY (0x04) 83 | #define GSRN_CLI_OP_SET_LOGSTREAM (0x05) // option: -l 84 | 85 | struct _cli_stats 86 | { 87 | struct _cli_hdr_fixed hdr; 88 | uint8_t opcode; 89 | uint8_t flags; 90 | } __attribute__((__packed__)); 91 | #define GSRN_CLI_OP_STATS_RESET (0x01) 92 | 93 | struct _cli_logstream 94 | { 95 | struct _cli_hdr_fixed hdr; 96 | uint8_t opcode; 97 | uint128_t addr; 98 | struct sockaddr_in ipa; 99 | struct sockaddr_in ipb; 100 | // union { 101 | // struct { 102 | // uint8_t token[GS_TOKEN_SIZE]; 103 | // } listen; 104 | // struct { 105 | // uint8_t data[GS_TOKEN_SIZE]; 106 | // } raw; 107 | // }; 108 | uint8_t msg[sizeof (struct _gs_listen)]; 109 | } __attribute__((__packed__)); 110 | #define GSRN_CLI_OP_LS_LISTEN (0x01) 111 | #define GSRN_CLI_OP_LS_CONNECT (0x02) 112 | 113 | ///////////// CLI responses 114 | 115 | struct _flagstr 116 | { 117 | uint8_t ssl; 118 | uint8_t x_client_or_server; 119 | uint8_t fast_connect; 120 | uint8_t low_latency; 121 | uint8_t wait; 122 | uint8_t major; 123 | uint8_t minor; 124 | }; 125 | 126 | // 'list' response 127 | struct _cli_list_r 128 | { 129 | struct _cli_hdr_fixed hdr; 130 | uint8_t con_type; // TCP, SSL or CNC 131 | uint16_t port; 132 | 133 | uint32_t ip; 134 | 135 | uint64_t xfer_bytes; 136 | uint128_t addr; 137 | uint8_t gs_id[GS_ID_SIZE]; 138 | uint32_t peer_id; 139 | uint32_t buddy_ip; 140 | uint16_t buddy_port; 141 | uint64_t in_n; // bytes FROM peer 142 | uint64_t out_n; // bytes TO peer 143 | uint32_t idle_sec; // Idle in seconds 144 | peer_l_id_t pl_id; // Current state: LISTENING, WAITING, ... 145 | uint32_t age_sec; // Age in sec in current state (LISTEN, WAITING, CONNECTED etc) 146 | char bps[GS_BPS_MAXSIZE]; 147 | uint8_t flags; // GSRN_FL_CLI_LIST_START 148 | union { 149 | uint8_t flagstr[7]; 150 | struct _flagstr fl; 151 | }; 152 | } __attribute__((__packed__)); 153 | 154 | // 'stats' response 155 | struct _cli_stats_r 156 | { 157 | struct _cli_hdr_fixed hdr; 158 | uint64_t uptime_usec; // GSRND uptime 159 | uint64_t since_reset_usec; // Since last stats reset 160 | uint64_t n_gs_connect; 161 | uint64_t n_gs_listen; 162 | uint64_t n_bad_auth; 163 | uint64_t n_gs_refused; 164 | 165 | uint32_t n_peers_total; 166 | uint32_t n_peers_listening; 167 | uint32_t n_peers_connected; 168 | uint32_t n_peers_badauthwait; 169 | } __attribute__((__packed__)); 170 | 171 | #define GSRN_CLI_HDR_TLV_SIZE (sizeof (struct _cli_hdr_tlv)) 172 | #define GSRN_CLI_PAYLOAD_SIZE(xmsg) (sizeof xmsg - GSRN_CLI_HDR_TLV_SIZE) 173 | 174 | #define GSRN_FL_CLI_LIST_START (0x01) 175 | 176 | ///////////// CLI TYPE definitions 177 | #define GSRN_CLI_TYPE_LIST_RESPONSE (0x01) // s2c - response (to list request) 178 | #define GSRN_CLI_TYPE_LOGSTREAM (0x02) // s2c - log message 179 | #define GSRN_CLI_TYPE_MSG (0x04) // s2c - message (to display) 180 | #define GSRN_CLI_TYPE_STATS_RESPONSE (0x09) // s2c - stats response 181 | 182 | #define GSRN_CLI_TYPE_LIST (0x01) // c2s - list all connected clients 183 | #define GSRN_CLI_TYPE_KILL (0x03) // c2s - Kill a session (by ID or ADDR) 184 | #define GSRN_CLI_TYPE_STOP (0x05) // c2s - stop listening tcp/gsocket 185 | #define GSRN_CLI_TYPE_SET (0x06) // c2s - set a variable (config) 186 | #define GSRN_CLI_TYPE_SHUTDOWN (0x07) // c2s - Shut all GS-Listeners 187 | #define GSRN_CLI_TYPE_STATS (0x08) // c2s - Request stats 188 | 189 | #endif // !__GSRN_PROTO_CLI_H__ 190 | -------------------------------------------------------------------------------- /src/peer.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __GSRN_PEER_H__ 3 | #define __GSRN_PEER_H__ 1 4 | 5 | #include "packet.h" 6 | 7 | // A binary tree contains a Peer-List-Manager (peer_l_mgr) at every node. Each pl-mgr contains 8 | // multiple lists (for listening peers, waiting peers, ...). Each list is rooted at 9 | // peer_l_root. The peer can only be in 1 of the lists at the same time (the same 10 | // TAILQ_ENTRY(_peer, ll) is used for all lists). 11 | // 12 | // Use PEER_L_mv() to move peer to a list (e.g. from 'listening' to connected etc) 13 | 14 | typedef enum 15 | { 16 | PEER_L_LISTENING = 0, // Waiting for client 17 | PEER_L_WAITING = 1, // Client waiting or short-waiting for listening server 18 | PEER_L_WAIT_ACCEPT = 2, // Waiting for peer to send 'ACCEPT' 19 | PEER_L_ACCEPTED = 3, // ACCEPT received. 20 | PEER_L_CONNECTED = 4, 21 | PEER_L_BAD_AUTH = 5 22 | } peer_l_id_t; 23 | #define MAX_LISTS_BY_ADDR (6) 24 | 25 | // A single list 26 | struct _peer_l_root 27 | { 28 | int n_entries; 29 | struct _peer_l_mgr *pl_mgr; 30 | TAILQ_HEAD(_listhead, _peer) head; 31 | }; 32 | 33 | // Multiple lists (multiple _peer_l_root). 34 | struct _peer_l_mgr 35 | { 36 | int n_entries; 37 | uint128_t addr; 38 | struct event *evt_linger; // Timeout to free gsocket-listen block (matching token) 39 | struct event *evt_shortwait; // Delete waiting peers who are FL_PEER_IS_SHORTWAIT 40 | uint64_t last_bad_auth_usec; 41 | uint8_t token[GS_TOKEN_SIZE]; 42 | int flags; 43 | struct _peer_l_root plr[MAX_LISTS_BY_ADDR]; 44 | }; 45 | #define FL_PL_IS_TOKEN_SET (0x01) 46 | 47 | typedef void (*shutdown_complete_func_t)(void *p); 48 | 49 | struct _peer 50 | { 51 | SSL *ssl; 52 | int fd; 53 | uint32_t id; 54 | uint128_t addr; 55 | uint8_t gs_id[GS_ID_SIZE]; 56 | struct bufferevent *bev; 57 | PKT pkt; 58 | int flags; 59 | uint8_t gs_proto_flags; // flags from _gs_connect/_gs_listen 60 | 61 | struct _peer_l_root *plr; 62 | TAILQ_ENTRY(_peer) ll; // linked entries. 63 | 64 | // struct event *evt_peer_shutdown; 65 | struct event *evt_bad_auth_delay; 66 | struct event *evt_tioc; 67 | int tioc_count; 68 | int tioc_delay_ms; 69 | struct event *evt_shutdown_timeout; 70 | shutdown_complete_func_t shutdown_complete_func; 71 | int shutdown_complete_value; 72 | 73 | struct _peer *buddy; 74 | 75 | // For 'stats' and 'cli-list' 76 | struct sockaddr_in addr_in; 77 | uint64_t in_n; // bytes read from peer (after connect) 78 | uint64_t out_n; // bytes sent to peer 79 | uint64_t in_last_usec; 80 | uint64_t out_last_usec; 81 | uint8_t version_major; 82 | uint8_t version_minor; 83 | // BPS buckets 84 | uint32_t bps_last; // 300 => 300 bps 85 | uint32_t bps_max; 86 | uint64_t bps_last_usec; // timestamp when last updated 'bps_last' 87 | uint64_t bps_last_inout; // 88 | 89 | uint64_t state_usec; // timestamp when state changed from LISTEN, WAITING, .. CONNECTED 90 | }; 91 | 92 | typedef void (*walk_peers_func_t)(struct _peer *p, struct _peer_l_root *plr, void *arg); 93 | typedef void (*peer_func_t)(struct _peer *p, void *arg); 94 | 95 | #define FL_PEER_IS_SERVER (0x01) 96 | #define FL_PEER_IS_CLIENT (0x02) 97 | #define FL_PEER_IS_ACCEPT_RECV (0x04) // Peer sent ACCEPTT after _gs_start msg 98 | // #define FL_PEER_IS_GOODBYE (0x08) // PEER_goodbye was called. 99 | #define FL_PEER_IS_EOF_RECEIVED (0x10) 100 | #define FL_PEER_IS_SHORTWAIT (0x20) 101 | #define FL_PEER_IS_SAW_SSL_HELO (0x40) // Detected an TLS Helo 102 | #define FL_PEER_IS_WANT_SEND_SHUT_WR (0x80) 103 | #define FL_PEER_IS_WANT_FREE (0x100) // PEER_free() as soon as all data is written. 104 | #define FL_PEER_IS_SHUT_WR_SENT (0x200) 105 | 106 | #define PEER_IS_SERVER(p) ((p)->flags & FL_PEER_IS_SERVER) 107 | #define PEER_IS_CLIENT(p) ((p)->flags & FL_PEER_IS_CLIENT) 108 | #define PEER_IS_ACCEPT_RECEIVED(p) ((p)->flags & FL_PEER_IS_ACCEPT_RECV) 109 | // #define PEER_IS_GOODBYE(p) ((p)->flags & FL_PEER_IS_GOODBYE) 110 | #define PEER_IS_EOF_RECEIVED(p) ((p)->flags & FL_PEER_IS_EOF_RECEIVED) 111 | #define PEER_IS_SHORTWAIT(p) ((p)->flags & FL_PEER_IS_SHORTWAIT) 112 | #define PEER_IS_WANT_SEND_SHUT_WR(p) ((p)->flags & FL_PEER_IS_WANT_SEND_SHUT_WR) 113 | #define PEER_IS_WANT_FREE(p) ((p)->flags & FL_PEER_IS_WANT_FREE) 114 | #define PEER_IS_SHUT_WR_SENT(p) ((p)->flags & FL_PEER_IS_SHUT_WR_SENT) 115 | 116 | // Return S, C or - for Server, Client or Unknown (used by DEBUGF) 117 | #define IS_CS(p) (p)->flags & FL_PEER_IS_SERVER?'S':(p)->flags & FL_PEER_IS_CLIENT?'C':'#' 118 | 119 | void PEER_conn_refused(struct _peer *p); 120 | void PEER_goodbye(struct _peer *p); 121 | int PEER_add(struct _peer *p, peer_l_id_t pl_id, uint8_t *token); 122 | void PEER_free(struct _peer *p); 123 | void PEER_try_free(struct _peer *p); 124 | void PEER_shutdown(struct _peer *p, shutdown_complete_func_t func); 125 | 126 | struct _peer *PEER_new(int fd, SSL *ssl); 127 | struct _peer_l_mgr *PEER_get_mgr(uint128_t addr); 128 | void PEER_by_addr(uint128_t addr, peer_func_t cb_peer, void *arg); 129 | 130 | void PEERS_walk(walk_peers_func_t func, void *arg); 131 | void PEER_stats_update(struct _peer *p, struct evbuffer *eb); 132 | uint32_t PEER_get_bps(struct _peer *p); 133 | 134 | 135 | #define PLR_L_get_id(plr) (plr - &(plr)->pl_mgr->plr[0]) 136 | #define PEER_L_get_id(p) ((p)->plr - &(p)->plr->pl_mgr->plr[0]) 137 | #define PEER_L_get_mgr(p) (p)->plr->pl_mgr 138 | 139 | 140 | void PEER_L_mv(struct _peer *p, peer_l_id_t pl_id); 141 | 142 | struct _peer *PEER_get(uint128_t addr, peer_l_id_t pl_id, struct _peer_l_mgr **pl_mgr_ptr); 143 | #define PEER_get_listening(addr) PEER_get(addr, PEER_L_LISTENING, NULL) 144 | #define PEER_get_waiting(addr) PEER_get(addr, PEER_L_WAITING, NULL) 145 | 146 | #endif // !__GSRN_PEER_H__ 147 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl Process this File with autoconf to produce a configure script. 2 | AC_PREREQ([2.69]) 3 | AC_INIT([gsocket-relay],[1.0.15]) 4 | AC_CONFIG_AUX_DIR(config) 5 | AC_CANONICAL_TARGET 6 | 7 | dnl we use automake 8 | AM_INIT_AUTOMAKE([foreign]) 9 | AC_CONFIG_HEADERS(config.h) 10 | AM_PROG_AR 11 | 12 | dnl Checks for programs. 13 | AC_PROG_CC 14 | AC_PROG_INSTALL 15 | AC_PROG_RANLIB 16 | 17 | dnl 18 | dnl Use these compiler flags if we have gcc. 19 | dnl 20 | if test $ac_cv_c_compiler_gnu = yes; then 21 | CCOPTS='-O2 -Wall' 22 | CFLAGS="$CCOPTS $CFLAGS" 23 | fi 24 | test "x$prefix" != "xNONE" || prefix="/usr/local" 25 | test "x$exec_prefix" != "xNONE" || exec_prefix="${prefix}" 26 | trydir_i="${prefix}/include /usr/include" 27 | trydir_l="${prefix}/lib /usr/lib" 28 | 29 | if test "${prefix}" != "/usr/local" ; then 30 | trydir_i="${trydir_i} /usr/local/include" 31 | trydir_l="${trydir_l} /usr/local/lib" 32 | fi 33 | 34 | dnl Try include paths (especially on OSX) 35 | trydir_i="${trydir_i} /usr/local/opt/openssl/include /opt/homebrew/opt/openssl/include" 36 | for xincdir in $trydir_i ; do 37 | if test ! -d "$xincdir" ; then 38 | continue; 39 | fi 40 | if test x"${INCLUDES}" = x; then 41 | INCLUDES="-I${xincdir}"; 42 | else 43 | INCLUDES="$INCLUDES -I${xincdir}"; 44 | fi 45 | done 46 | 47 | dnl Try library paths... 48 | trydir_l="${trydir_l} /usr/local/opt/openssl/lib /opt/homebrew/opt/openssl/lib" 49 | for xlibdir in $trydir_l ; do 50 | if test ! -d "$xlibdir" ; then 51 | continue; 52 | fi 53 | if test -f "${xlibdir}/libssl.a"; then 54 | STATIC_LIBSSLDIR="${xlibdir}" 55 | fi 56 | if test x"${LIBDIR}" = x; then 57 | LIBDIR="-L${xlibdir}"; 58 | else 59 | LIBDIR="$LIBDIR -L${xlibdir}"; 60 | fi 61 | done 62 | 63 | dnl if --with-gsocket= is used then try to find include files there 64 | GSOCKET_INC="" 65 | AC_ARG_WITH([gsocket-dir], 66 | [AS_HELP_STRING([--with-gsocket-dir=DIR], 67 | [location of gsocket source])], 68 | [ 69 | 70 | if test -f "${withval}/include/gsocket/gsocket.h"; then 71 | GSOCKET_INC="${withval}/include" 72 | else 73 | AC_MSG_ERROR([Not found: $withval/include/gsocket/gsocket.h]) 74 | fi 75 | LIBGSOCKET_PATH="${withval}/lib" 76 | ], 77 | [ 78 | dnl try ./gsocket/include 79 | dnl systemwide installation is the fallback. 80 | if test -f "${srcdir}/gsocket/include/gsocket/gsocket.h"; then 81 | GSOCKET_INC="${srcdir}/gsocket/include" 82 | LIBGSOCKET_PATH="${srcdir}/gsocket/lib" 83 | fi 84 | ]) 85 | 86 | if test "x$GSOCKET_INC" != x; then 87 | dnl Convert path to absolute path (and without .././ shit) 88 | GSOCKET_INC=$(cd "$GSOCKET_INC"; pwd) 89 | LIBGSOCKET_PATH=$(cd "$LIBGSOCKET_PATH"; pwd) 90 | INCLUDES="-I${GSOCKET_INC} $INCLUDES" 91 | LIBDIR="-L${LIBGSOCKET_PATH} $LIBDIR" 92 | fi 93 | 94 | dnl CPPFLAGS="-I${srcdir}/../include ${INCLUDES} $CPPFLAGS" 95 | CPPFLAGS="${INCLUDES} $CPPFLAGS" 96 | LDFLAGS="${LIBDIR} $LDFLAGS" 97 | 98 | 99 | AC_CHECK_HEADERS(gsocket/gsocket.h) 100 | 101 | dnl default perm of .so is 644 but on cygwin must be 755. 102 | dnl PERM_DSO="644" 103 | case "$host" in 104 | *-cygwin*) 105 | PERM_DSO="755" 106 | ;; 107 | mips-sony-bsd|mips-sony-newsos4) 108 | AC_DEFINE([NEED_SETPGRP], [1], [Need setpgrp to acquire controlling tty]) 109 | ;; 110 | *-*-ultrix*) 111 | AC_DEFINE([NEED_SETPGRP], [1], [Need setpgrp to acquire controlling tty]) 112 | ;; 113 | *-*-darwin*|*-*-*bsd*) 114 | AC_DEFINE([BSD_SCRIPT], [1], [/usr/bin/script is the bsd variant]) 115 | if test x"$(which ar)" != x'/usr/bin/ar'; then 116 | ARDIRWARN=1 117 | fi 118 | ;; 119 | esac 120 | 121 | dnl Checks for header files. 122 | AC_HEADER_SYS_WAIT 123 | AC_CHECK_HEADERS(sys/time.h sys/endian.h unistd.h string.h netinet/in_systm.h inttypes.h stdint.h) 124 | 125 | dnl Checks for typedefs, structures, and compiler characteristics. 126 | AC_C_CONST 127 | AC_TYPE_PID_T 128 | 129 | dnl Checks for library functions. 130 | AC_FUNC_MEMCMP 131 | 132 | dnl If size_t is not defined, define size_t to be unsigned. 133 | AC_TYPE_SIZE_T 134 | dnl If uid_t is not defined, define uid_t to be int and gid_t to be int. 135 | AC_TYPE_UID_T 136 | 137 | 138 | AC_CHECK_TYPES(uint128_t, 139 | [], 140 | AC_CHECK_TYPES(__uint128_t, [], [AC_ERROR("no 128 bit")]) 141 | [ 142 | #ifdef HAVE_INTTYPES_H 143 | # include 144 | #endif 145 | 146 | #ifdef HAVE_STDINT_H 147 | # include 148 | #endif 149 | ] 150 | ) 151 | 152 | AC_ARG_ENABLE(static, 153 | [ --enable-static Compile static binary], 154 | [STATIC="yes"], [STATIC="no"] 155 | ) 156 | 157 | dnl OSX does not support static binaries. 158 | dnl At least staticly include OpenSSL libs 159 | if test x"${STATIC}" = xyes; then 160 | case "$host" in 161 | *-*-darwin*) 162 | LDADD_STATIC="${STATIC_LIBSSLDIR}/libssl.a ${STATIC_LIBSSLDIR}/libcrypto.a" 163 | AC_DEFINE(HAVE_LIBSSL, 1, [Define to 1 if you have the `ssl' library (-lssl)]) 164 | AC_DEFINE(HAVE_LIBCRYPTO, 1, [Define to 1 if you have the `crypto' library (-lcrypto)]) 165 | STATIC_SSL="yes" 166 | ;; 167 | *) 168 | CFLAGS_STATIC="-static " 169 | ;; 170 | esac 171 | fi 172 | 173 | AC_CHECK_LIB(socket, socket) 174 | if test x"${STATIC}" = xno; then 175 | AC_CHECK_LIB(nsl, gethostbyname) 176 | fi 177 | AC_CHECK_LIB(event, event_base_new, [], [AC_MSG_ERROR([libevent not found])]) 178 | AC_CHECK_LIB(event_openssl, bufferevent_openssl_socket_new, [], [AC_MSG_ERROR([libevent_openssl not found])]) 179 | 180 | if test x"$STATIC_SSL" != xyes; then 181 | AC_CHECK_LIB([crypto], [ENGINE_init], [], [AC_MSG_ERROR([libcrypto not found])]) 182 | AC_CHECK_LIB([ssl], [SSL_new], [], [AC_MSG_ERROR([libssl not found])]) 183 | fi 184 | 185 | AC_CHECK_LIB(gsocket, GS_new, [], [AC_MSG_ERROR([libgsocket not found. Compile gsocket in ./gsocket or use --with-gsocket-dir.])]) 186 | AC_CHECK_FUNCS(gettimeofday memcpy strchr strlcat) 187 | 188 | AC_ARG_ENABLE([debug], 189 | AS_HELP_STRING([--enable-debug], [Enable debug information.]), 190 | [debug=true AC_DEFINE(DEBUG, 1, [Debug infos])] 191 | ) 192 | 193 | AC_ARG_ENABLE([tests], 194 | AS_HELP_STRING([--enable-tests], [Enable self-tests.]), 195 | [selftests=true] 196 | ) 197 | 198 | AS_IF([test x$enable_debug = xyes], [selftests=true]) 199 | 200 | AS_IF([test x$selftests = xtrue], AC_DEFINE(SELFTESTS, 1, [Self Tests])) 201 | 202 | AC_ARG_ENABLE(realprefix, 203 | [ --enable-realprefix Set real prefix (for dpkg packaging)], 204 | [REALPREFIX="${enableval}"], [REALPREFIX="${prefix}"] 205 | ) 206 | 207 | AS_IF([test x$selftests = xtrue], AC_SUBST(PROGRAMS_TEST_LIB, "list-test${EXEEXT} event-test${EXEEXT}")) 208 | AS_IF([test x$selftests = xtrue], AC_SUBST(PROGRAMS_TEST_TOOLS, "packet-test${EXEEXT} readline-test${EXEEXT} console_display-test${EXEEXT} filetransfer-test${EXEEXT}")) 209 | 210 | AC_SUBST(LDADD_STATIC, "${LDADD_STATIC}") 211 | AC_SUBST(CFLAGS_STATIC, "${CFLAGS_STATIC}") 212 | AC_SUBST(REALPREFIX, "${REALPREFIX}") 213 | AC_SUBST(LIBGSOCKET_PATH, "${LIBGSOCKET_PATH}") 214 | AC_CONFIG_FILES([Makefile src/Makefile]) 215 | AC_OUTPUT 216 | 217 | if test x"${STATIC}" = xyes; then 218 | case "$host" in 219 | *-*-darwin*) 220 | echo " 221 | *** OSX does not support static binaries. Creating dynamic binaries *** 222 | *** instead and trying our best to included OpenSSL statically. *** 223 | " 224 | ;; 225 | *) 226 | echo " 227 | ********************************** WARNING *********************************** 228 | * Your MUST compile OpenSSL like this: * 229 | * openssl-src> * 230 | * ./Configure --prefix=\$HOME/usr no-dso no-threads no-shared linux-generic64 * 231 | * mkdir -p \$HOME/usr && make all install * 232 | * Only then compile gsocket \(using the same --prefix=\): * 233 | * gsocket-src> ./configure --prefix=\$HOME/usr --enable-static * 234 | * gsocket-src> make all install * 235 | * gsocket-src> export PATH=\$HOME/usr/bin:\$PATH * 236 | ****************************************************************************** 237 | " 238 | ;; 239 | esac 240 | fi 241 | 242 | echo " 243 | ${PACKAGE_NAME}-${PACKAGE_VERSION} has been configured: 244 | 245 | Host..............: ${host} 246 | Compiler..........: ${CC} 247 | Compiler flags....: ${CFLAGS_STATIC}${CFLAGS} 248 | Preprocessor flags: ${CPPFLAGS} 249 | Linker flags......: ${LDFLAGS} 250 | Libraries.........: ${LIBS} 251 | 252 | Configuration complete. Now type: make all install" 253 | if test x"${ARDIRWARN}" = x1; then 254 | AC_MSG_WARN([Build tools seem to be a mix of GNU and Apple.]) 255 | AC_MSG_WARN([Alex, try 'PATH=/usr/bin:\$PATH ./configure'.]) 256 | fi 257 | 258 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | 2 | #include "common.h" 3 | #include "gsrnd.h" 4 | #include "utils.h" 5 | #include "net.h" 6 | #include "engine.h" 7 | #include "gopt.h" 8 | 9 | struct _gopt gopt; 10 | struct _gd gd; 11 | #ifdef DEBUG 12 | struct _g_debug_ctx g_dbg_ctx; 13 | #endif 14 | 15 | static int PORT_add_listen(struct _port *pt, uint32_t ip, event_callback_fn cb_func, void *arg); 16 | static void PORT_close(struct _port *pt); 17 | static void cb_gs_log(struct _gs_log_info *l); 18 | 19 | const char * 20 | strx128(uint128_t x, char *val, size_t sz) 21 | { 22 | if (x >> 64 > 0) 23 | snprintf(val, sz, "%"PRIx64"%"PRIx64, INT128_HIGH(x), INT128_LOW(x)); 24 | else 25 | snprintf(val, sz, "%"PRIx64, (uint64_t)x); 26 | 27 | return val; 28 | } 29 | 30 | const char * 31 | strx128x(uint128_t x) 32 | { 33 | static char valstr[64]; 34 | strx128(x, valstr, sizeof valstr); 35 | 36 | return valstr; 37 | } 38 | 39 | 40 | static void 41 | init_defaults_gsrnd(void) 42 | { 43 | TAILQ_INIT(&gopt.ports_head); 44 | TAILQ_INIT(&gopt.ports_cli_head); 45 | } 46 | 47 | static void 48 | init_defaults_cli() 49 | { 50 | gopt.port_cli = CLI_DEFAULT_PORT; 51 | } 52 | 53 | void 54 | init_defaults(prg_t prg) 55 | { 56 | gopt.prg = prg; 57 | gopt.err_fp = stderr; 58 | gopt.log_fp = stderr; 59 | gopt.ip_cli = ntohl(inet_addr("127.0.0.1")); 60 | // gopt.port_cli = CLI_DEFAULT_PORT; 61 | 62 | // Must ignore SIGPIPE. Epoll may signal that socket is ready for wiriting 63 | // but gets closed by remote before calling write(). 64 | signal(SIGPIPE, SIG_IGN); 65 | 66 | if (prg == PRG_GSRND) 67 | init_defaults_gsrnd(); 68 | else if (prg == PRG_CLI) 69 | init_defaults_cli(); 70 | } 71 | 72 | void 73 | PORTSQ_add(struct _port_listhead *head, int port) 74 | { 75 | struct _port *pt; 76 | 77 | pt = calloc(1, sizeof (struct _port)); 78 | XASSERT(pt != NULL, "calloc() failed\n"); 79 | 80 | pt->port = port; 81 | 82 | TAILQ_INSERT_TAIL(head, pt, ll); 83 | } 84 | 85 | void 86 | PORTSQ_listen(struct _port_listhead *head, uint32_t ip, uint16_t port_default, event_callback_fn cb_func) 87 | { 88 | if (TAILQ_EMPTY(head)) 89 | PORTSQ_add(head, port_default); 90 | 91 | struct _port *pt; 92 | int ret; 93 | TAILQ_FOREACH(pt, head, ll) 94 | { 95 | DEBUGF("port=%u\n", pt->port); 96 | ret = PORT_add_listen(pt, ip, cb_func, pt); 97 | if (ret != 0) 98 | { 99 | GS_LOG("ERR: listen(%d): %s\n", pt->port, strerror(errno)); 100 | DEBUGF("listen(%d): %s\n", pt->port, strerror(errno)); 101 | } 102 | } 103 | } 104 | 105 | static void 106 | PORT_close(struct _port *pt) 107 | { 108 | if (pt->ev == NULL) 109 | return; 110 | 111 | int fd; 112 | fd = event_get_fd(pt->ev); 113 | if (fd < 0) 114 | return; 115 | 116 | event_del(pt->ev); 117 | event_free(pt->ev); 118 | pt->ev = NULL; 119 | 120 | close(fd); 121 | } 122 | 123 | // Close all ports (and free all events). Do not free Q as we later may want 124 | // to cal PORTSQ_listen() again. 125 | void 126 | PORTSQ_close(struct _port_listhead *head) 127 | { 128 | struct _port *pt; 129 | 130 | TAILQ_FOREACH(pt, head, ll) 131 | { 132 | PORT_close(pt); 133 | } 134 | } 135 | 136 | void 137 | PORTSQ_free(struct _port_listhead *head) 138 | { 139 | struct _port *pt; 140 | struct _port *temp_pt; 141 | 142 | TAILQ_FOREACH_SAFE(pt, head, ll, temp_pt) 143 | { 144 | PORT_close(pt); 145 | TAILQ_REMOVE(head, pt, ll); 146 | } 147 | } 148 | 149 | static int 150 | PORT_add_listen(struct _port *pt, uint32_t ip, event_callback_fn cb_func, void *arg) 151 | { 152 | int ls; 153 | int ret; 154 | 155 | ls = fd_new_socket(SOCK_STREAM); 156 | if (ls < 0) 157 | goto err; 158 | ret = fd_net_listen(ls, ip, pt->port); 159 | if (ret < 0) 160 | goto err; 161 | 162 | pt->ev = event_new(gopt.evb, ls, EV_READ|EV_PERSIST, cb_func, arg); 163 | XASSERT(pt->ev != NULL, "event_new() failed\n"); 164 | 165 | event_add(pt->ev, NULL); 166 | return 0; 167 | err: 168 | XCLOSE(ls); 169 | return -1; 170 | } 171 | 172 | void 173 | close_del_ev(struct event **evptr) 174 | { 175 | int fd; 176 | if ((evptr == NULL) || (*evptr == NULL)) 177 | return; 178 | 179 | struct event *ev = *evptr; 180 | 181 | fd = event_get_fd(ev); 182 | if (fd < 0) 183 | return; 184 | event_del(ev); 185 | close(fd); 186 | *evptr = NULL; 187 | } 188 | 189 | void 190 | init_vars(void) 191 | { 192 | GS_library_init(gopt.err_fp, /* Debug Output */ gopt.err_fp, cb_gs_log); 193 | 194 | srandom(GS_usec()); 195 | #if 0 196 | SSL_load_error_strings(); 197 | SSL_library_init(); 198 | gopt.ssl_ctx = SSL_CTX_new(TLS_server_method() /*SSLv23_client_method()*/); 199 | XASSERT(gopt.ssl_ctx != NULL, "Failed creating SSL context\n"); 200 | 201 | // FIXME: What we want is that port 443 supports cleartext and SSL. 202 | if (gopt.port_ssl > 0) 203 | { 204 | // Dont explode when realloc buffers 205 | SSL_CTX_set_mode(gopt.ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); 206 | #ifdef DEBUG 207 | // For testing WANT_READ/WRITE we disable auto-retry. This will trigger 208 | // SSL to return if it wants to write (during SSL_read) even if 209 | // the underlying socket is available for writing. 210 | SSL_CTX_set_mode(gopt.ssl_ctx, SSL_CTX_get_mode(gopt.ssl_ctx) & ~SSL_MODE_AUTO_RETRY); 211 | #endif 212 | 213 | // FIXME: Use certificate in memory if no file specified. 214 | if (!SSL_CTX_use_certificate_file(gopt.ssl_ctx, "server.pem", SSL_FILETYPE_PEM) 215 | || !SSL_CTX_use_PrivateKey_file(gopt.ssl_ctx, "server.pem", SSL_FILETYPE_PEM) 216 | || !SSL_CTX_check_private_key(gopt.ssl_ctx)) 217 | { 218 | fprintf(stderr, "Error setting up SSL_CTX. Try -P 0 to disable SSL support.\n"); 219 | ERR_print_errors_fp(stderr); 220 | exit(1); 221 | } 222 | } 223 | #endif 224 | 225 | gopt.evb = event_base_new(); 226 | XASSERT(gopt.evb != NULL, "Could not initialize libevent!\n"); 227 | 228 | init_engine(); // either in engine_client.c or engine_server.c 229 | } 230 | 231 | static int is_fdlim_init; 232 | 233 | int 234 | fd_limit_init(void) 235 | { 236 | if (is_fdlim_init != 0) 237 | return 0; 238 | 239 | is_fdlim_init = 1; 240 | return getrlimit(RLIMIT_NOFILE, &gopt.rlim_fd); 241 | } 242 | 243 | int 244 | fd_limit_unlimited() 245 | { 246 | struct rlimit *r = &gopt.rlim_fd; 247 | 248 | fd_limit_init(); 249 | 250 | r->rlim_cur = r->rlim_max; 251 | return setrlimit(RLIMIT_NOFILE, r); 252 | } 253 | 254 | int 255 | fd_limit_limited() 256 | { 257 | struct rlimit *r = &gopt.rlim_fd; 258 | 259 | fd_limit_init(); 260 | 261 | r->rlim_cur = r->rlim_max - GSRN_FD_RESERVE; 262 | return setrlimit(RLIMIT_NOFILE, r); 263 | } 264 | 265 | static void 266 | cb_gs_log(struct _gs_log_info *l) 267 | { 268 | if (l == NULL) 269 | return; 270 | 271 | #ifndef DEBUG 272 | // Return if this is _NOT_ a DEBUG-build but we get a TYPE_DEBUG 273 | // (should not happen). 274 | if (l->type == GS_LOG_TYPE_DEBUG) 275 | return; 276 | #endif 277 | 278 | FILE *fp = gopt.log_fp; 279 | if (l->type == GS_LOG_TYPE_ERROR) 280 | fp = gopt.err_fp; 281 | 282 | if (fp == NULL) 283 | return; 284 | 285 | if (l->level > gopt.verbosity) 286 | return; // Not interested. 287 | 288 | fprintf(fp, "%s %s\n", GS_logtime(), l->msg); 289 | fflush(fp); 290 | } 291 | 292 | #define BEV_ERR_ADD(dst, end, rem, what, check) do{ \ 293 | if (!(what & check)) { break; } \ 294 | rem &= ~check; \ 295 | int rv; \ 296 | rv = snprintf(dst, end - dst, "|%s", #check); \ 297 | if (rv <= 0) { break; } \ 298 | dst += rv; \ 299 | } while (0) 300 | 301 | // Return BEV_EVENT_ errors as string 302 | const char * 303 | BEV_strerror(short what) 304 | { 305 | static char err[128]; 306 | char *d = &err[0]; 307 | char *e = d + sizeof err; 308 | short rem = what; // remaining flags 309 | 310 | *d = 0; 311 | 312 | BEV_ERR_ADD(d, e, rem, what, BEV_EVENT_READING); 313 | BEV_ERR_ADD(d, e, rem, what, BEV_EVENT_WRITING); 314 | BEV_ERR_ADD(d, e, rem, what, BEV_EVENT_EOF); 315 | BEV_ERR_ADD(d, e, rem, what, BEV_EVENT_ERROR); 316 | BEV_ERR_ADD(d, e, rem, what, BEV_EVENT_TIMEOUT); 317 | BEV_ERR_ADD(d, e, rem, what, BEV_EVENT_CONNECTED); 318 | 319 | if (rem != 0) 320 | snprintf(d, e - d, "|UNKNOWN-%x", rem); 321 | 322 | return err; 323 | } 324 | 325 | static const char *peer_l_names[MAX_LISTS_BY_ADDR] = { 326 | "LISTEN", 327 | "WAIT ", 328 | "WAIT-A", 329 | "ACCEPT", 330 | "ESTABL", 331 | "B-AUTH" 332 | }; 333 | 334 | 335 | const char * 336 | PEER_L_name(uint8_t pl_id) 337 | { 338 | return peer_l_names[pl_id]; 339 | } 340 | 341 | #pragma GCC diagnostic push 342 | #pragma GCC diagnostic ignored "-Winitializer-overrides" 343 | static const uint8_t hextable[] = { 344 | [0 ... 255] = -1, // bit aligned access into this table is considerably 345 | ['0'] = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // faster for most modern processors, 346 | ['A'] = 10, 11, 12, 13, 14, 15, 347 | ['a'] = 10, 11, 12, 13, 14, 15 348 | }; 349 | #pragma GCC diagnostic pop 350 | 351 | uint32_t 352 | GS_hexto32(const char *hex) { 353 | uint32_t ret = 0; 354 | while (*hex && ret >= 0) { 355 | ret = (ret << 4) | hextable[(unsigned char)*hex++]; 356 | } 357 | return ret; 358 | } 359 | 360 | uint128_t 361 | GS_hexto128(const char *hex) { 362 | uint128_t ret = 0; 363 | while (*hex && ret >= 0) { 364 | ret = (ret << 4) | hextable[(unsigned char)*hex++]; 365 | } 366 | return ret; 367 | } 368 | 369 | char * 370 | GS_addr128hex(char *dst, uint128_t addr) 371 | { 372 | addr = htobe128(addr); 373 | return GS_addr2hex(dst, &addr); 374 | } 375 | 376 | // Do not log ip addresses or port numbers by default. This function 377 | // returns anonymized IP:PORT string unless ip-logging has been enabled. 378 | // 379 | // Convert an IP/PORT in HBO to a string (if logging allows this). 380 | const char * 381 | gs_log_ipport2str_r(char *dst, size_t dsz, uint32_t ip, uint16_t port) 382 | { 383 | if (gd.is_log_ip == 0) 384 | { 385 | snprintf(dst, dsz, "#.#.#.#:%u", port); 386 | return dst; 387 | } 388 | 389 | ip = htonl(ip); 390 | snprintf(dst, dsz, "%s:%u", int_ntoa(ip), port); 391 | 392 | return dst; 393 | } 394 | 395 | // HostByteOrder (HBO) 396 | const char * 397 | gs_log_ipport2str(uint32_t ip, uint16_t port) 398 | { 399 | static char ipport[32]; 400 | 401 | return gs_log_ipport2str_r(ipport, sizeof ipport, ip, port); 402 | } 403 | 404 | const char * 405 | gs_log_in_addr2str_r(char *dst, size_t dsz, struct sockaddr_in *addr_in /*NBO*/) 406 | { 407 | if (addr_in == NULL) 408 | return "#.#.#.#:0"; 409 | 410 | return gs_log_ipport2str_r(dst, dsz, ntohl(addr_in->sin_addr.s_addr), ntohs(addr_in->sin_port)); 411 | } 412 | 413 | const char * 414 | gs_log_in_addr2str(struct sockaddr_in *addr_in /*NBO*/) 415 | { 416 | static char ipport[32]; 417 | 418 | return gs_log_in_addr2str_r(ipport, sizeof ipport, addr_in); 419 | } 420 | 421 | 422 | 423 | 424 | 425 | -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __GSRN_COMMON_H__ 3 | #define __GSRN_COMMON_H__ 1 4 | 5 | #ifdef HAVE_CONFIG_H 6 | # include 7 | #endif 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #ifdef HAVE_SYS_LOADAVG_H 17 | # include // Solaris11 18 | #endif 19 | #ifdef HAVE_SYS_ENDIAN_H 20 | # include 21 | #endif 22 | #include 23 | #ifdef HAVE_NETINET_IN_SYSTM_H 24 | # include 25 | #endif 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #ifdef HAVE_UNISTD_H 34 | # include 35 | #endif 36 | #ifdef HAVE_FNMATCH_H 37 | #include 38 | #endif 39 | #include 40 | #include 41 | #include // Solaris11 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include /* basename() */ 49 | #include 50 | #include 51 | #include 52 | #ifdef HAVE_UTMPX_H 53 | # include 54 | #endif 55 | #ifdef HAVE_UTMP_H 56 | # include 57 | #endif 58 | #ifdef HAVE_LIBUTIL_H 59 | # include /* FreeBSD */ 60 | #endif 61 | #ifdef HAVE_PTY_H 62 | # include 63 | #endif 64 | #ifdef HAVE_UTIL_H 65 | # include /* MacOS */ 66 | #endif 67 | #if defined __sun || defined __hpux /* Solaris, HP-UX */ 68 | # include 69 | #endif 70 | #include // tsearch 71 | #include 72 | #include 73 | #include 74 | #include 75 | // LibEvent 76 | #include 77 | #include 78 | #include 79 | #include 80 | #include 81 | // gsocket 82 | #include 83 | 84 | #define CDR "\033[0;31m" 85 | #define CDG "\033[0;32m" 86 | #define CDY "\033[0;33m" 87 | #define CDB "\033[0;34m" 88 | #define CDM "\033[0;35m" 89 | #define CDC "\033[0;36m" 90 | #define CR "\033[1;31m" 91 | #define CG "\033[1;32m" 92 | #define CY "\033[1;33m" 93 | #define CN "\033[0m" 94 | #define CB "\033[1;34m" 95 | #define CM "\033[1;35m" 96 | #define CC "\033[1;36m" 97 | #define CW "\033[1;37m" 98 | 99 | #ifdef __sun 100 | # ifdef HAVE_OPEN64 101 | # define IS_SOL10 1 // Solaris 10 102 | # else 103 | # define IS_SOL11 1 // Solaris 11 104 | # endif 105 | # define IS_SOLARIS 1 106 | #endif 107 | 108 | #ifndef O_NOCTTY 109 | # warning "O_NOCTTY not defined. Using 0." 110 | # define O_NOCTTY 0 111 | #endif 112 | 113 | // Older fbsd's dont have this defined 114 | #ifndef UT_NAMESIZE 115 | # define UT_NAMESIZE 32 116 | #endif 117 | 118 | // debian-hurd does not define PATH_MAX (and has no limit on filename length) 119 | #ifndef PATH_MAX 120 | # define GS_PATH_MAX 4096 121 | #else 122 | # define GS_PATH_MAX PATH_MAX 123 | #endif 124 | 125 | #ifndef HAVE_UINT128_T 126 | # ifndef HAVE___UINT128_T 127 | # error "Compiler does not know about __uint128_t. Try on a 64-bit system." 128 | # else 129 | typedef __uint128_t uint128_t; 130 | # endif 131 | #endif 132 | 133 | #if defined(__sun) 134 | # if !defined(be64toh) // Solaris11 135 | # define be64toh(x) ntohll(x) 136 | # define htobe64(x) htonll(x) 137 | # endif 138 | # if !defined(htonll) // Solaris10 139 | # if __BIG_ENDIAN__ 140 | # define htonll(x) (x) 141 | # define ntohll(x) (x) 142 | # else 143 | # define htonll(x) ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((uint64_t)(x) >> 32) 144 | # define ntohll(x) ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((uint64_t)(x) >> 32) 145 | # endif 146 | # endif 147 | #endif 148 | 149 | #ifndef htonll 150 | # define htonll(x) htobe64(x) 151 | #endif 152 | #ifndef ntohll 153 | # define ntohll(x) be64toh(x) 154 | #endif 155 | 156 | #if !defined(be128toh) 157 | # define be128toh(x) ((uint128_t)ntohll(x) & 0xFFFFFFFFFFFFFFFF) << 64 | ntohll((uint128_t)(x) >> 64) 158 | # define htobe128(x) ((uint128_t)htonll(x) & 0xFFFFFFFFFFFFFFFF) << 64| htonll((uint128_t)(x) >> 64) 159 | #endif 160 | 161 | #define INT128_HIGH(x) (uint64_t)(x>>64) 162 | #define INT128_LOW(x) (uint64_t)(x) 163 | 164 | // Haha. Linux's TAILQ_FOREACH() is not safe when the element is free'd. 165 | #ifndef TAILQ_FOREACH_SAFE 166 | #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ 167 | for ((var) = TAILQ_FIRST((head)); \ 168 | (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ 169 | (var) = (tvar)) 170 | #endif 171 | 172 | 173 | #define SOMAXCON (64 * 1024) // listen() 174 | 175 | #define EX_EXECFAILED 248 176 | #define EX_NOTREACHED 249 177 | #define EX_BADWRITE 250 // write() failed 178 | #define EX_UNKNWNCMD 251 // Unknown command line parameter 179 | 180 | #define EX_BADSELECT 253 181 | #define EX_SIGTERM 254 182 | #define EX_FATAL 255 183 | #define EX_CONNREFUSED 61 // Used by deploy.sh to verify that server is responding 184 | 185 | #define CLI_DEFAULT_PORT (48001) 186 | #define CLI_DEFAULT_PORT_SSL (48002) 187 | 188 | extern struct _gopt gopt; // declared in utils.c 189 | extern struct _gd gd; 190 | // extern struct _gcli gcli; 191 | extern struct _gstats gstats; // declared in engine_server.c 192 | 193 | #define xfprintf(fp, a...) do {if (fp != NULL) { fprintf(fp, a); fflush(fp); } } while (0) 194 | 195 | #define int_ntoa(x) inet_ntoa(*((struct in_addr *)&x)) 196 | 197 | #ifndef MAX 198 | # define MAX(X, Y) (((X) < (Y)) ? (Y) : (X)) 199 | #endif 200 | 201 | #ifndef MIN 202 | # define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) 203 | #endif 204 | 205 | #define D_RED(a) "\033[0;31m"a"\033[0m" 206 | #define D_GRE(a) "\033[0;32m"a"\033[0m" 207 | #define D_YEL(a) "\033[0;33m"a"\033[0m" 208 | #define D_BLU(a) "\033[0;34m"a"\033[0m" 209 | #define D_MAG(a) "\033[0;35m"a"\033[0m" 210 | #define D_BRED(a) "\033[1;31m"a"\033[0m" 211 | #define D_BGRE(a) "\033[1;32m"a"\033[0m" 212 | #define D_BYEL(a) "\033[1;33m"a"\033[0m" 213 | #define D_BBLU(a) "\033[1;34m"a"\033[0m" 214 | #define D_BMAG(a) "\033[1;35m"a"\033[0m" 215 | 216 | #ifdef DEBUG 217 | struct _g_debug_ctx 218 | { 219 | struct timeval tv_last; 220 | struct timeval tv_now; 221 | }; 222 | 223 | extern struct _g_debug_ctx g_dbg_ctx; // declared in utils.c 224 | 225 | #define DEBUGF_T(xcolor, a...) do { \ 226 | gettimeofday(&g_dbg_ctx.tv_now, NULL); \ 227 | if (g_dbg_ctx.tv_last.tv_sec == 0) { memcpy(&g_dbg_ctx.tv_last, &g_dbg_ctx.tv_now, sizeof g_dbg_ctx.tv_last); } \ 228 | xfprintf(gopt.err_fp, "DEBUG %4"PRIu64" %s:%d %s", GS_TV_TO_MSEC(&g_dbg_ctx.tv_now) - GS_TV_TO_MSEC(&g_dbg_ctx.tv_last), __func__, __LINE__, xcolor?xcolor:""); \ 229 | memcpy(&g_dbg_ctx.tv_last, &g_dbg_ctx.tv_now, sizeof g_dbg_ctx.tv_last); \ 230 | xfprintf(gopt.err_fp, a); \ 231 | if (xcolor) { xfprintf(gopt.err_fp, "\033[0m"); } \ 232 | } while (0) 233 | 234 | # define DEBUGF(a...) do{DEBUGF_T(NULL, a); } while(0) 235 | # define DEBUGF_R(a...) do{DEBUGF_T("\033[1;31m", a); } while(0) 236 | # define DEBUGF_G(a...) do{DEBUGF_T("\033[1;32m", a); } while(0) 237 | # define DEBUGF_B(a...) do{DEBUGF_T("\033[1;34m", a); } while(0) 238 | # define DEBUGF_Y(a...) do{DEBUGF_T("\033[1;33m", a); } while(0) 239 | # define DEBUGF_M(a...) do{DEBUGF_T("\033[1;35m", a); } while(0) 240 | # define DEBUGF_C(a...) do{DEBUGF_T("\033[1;36m", a); } while(0) 241 | # define DEBUGF_W(a...) do{DEBUGF_T("\033[1;37m", a); } while(0) 242 | # define DEBUGF_HALT(a...) do{DEBUGF_R(a); while (1) { sleep(1000000); } } while(0) 243 | #else // DEBUG 244 | # define DEBUGF(a...) 245 | # define DEBUGF_R(a...) 246 | # define DEBUGF_G(a...) 247 | # define DEBUGF_B(a...) 248 | # define DEBUGF_Y(a...) 249 | # define DEBUGF_M(a...) 250 | # define DEBUGF_C(a...) 251 | # define DEBUGF_W(a...) 252 | # define DEBUGF_A(a...) 253 | # define DEBUGF_HALT(a...) 254 | #endif 255 | 256 | 257 | // Increase ptr by number of characters added to ptr. 258 | #define SXPRINTF(ptr, len, a...) do {\ 259 | size_t n = snprintf(ptr, len, a); \ 260 | ptr += MIN(n, len); \ 261 | } while(0) 262 | 263 | // Overcome GCC warning for truncation. Abort() if truncation happen. 264 | #define SNPRINTF_ABORT(...) (snprintf(__VA_ARGS__) < 0 ? abort() : (void)0) 265 | 266 | #define VOUT(level, a...) do { \ 267 | if (level > gopt.verboselevel) \ 268 | break; \ 269 | xfprintf(gopt.out, a); \ 270 | fflush(gopt.out); \ 271 | } while (0) 272 | 273 | #define XFREE(ptr) do{if(ptr) free(ptr); ptr = NULL;}while(0) 274 | #define XBIO_FREE(ptr) do{if(ptr) BIO_free(ptr); ptr = NULL;}while(0) 275 | #define XBEV_FREE(ptr) do{if(ptr) bufferevent_free(ptr); ptr = NULL;}while(0) 276 | #define XEVT_FREE(ptr) do{if(ptr) event_free(ptr); ptr = NULL;}while(0) 277 | 278 | 279 | #ifdef DEBUG 280 | # define ERREXIT(a...) do { \ 281 | xfprintf(gopt.err_fp, "ERROR "); \ 282 | xfprintf(gopt.err_fp, "%s():%d ", __func__, __LINE__); \ 283 | xfprintf(gopt.err_fp, a); \ 284 | exit(255); \ 285 | } while (0) 286 | #else 287 | # define ERREXIT(a...) do { \ 288 | xfprintf(gopt.err_fp, "ERROR: "); \ 289 | xfprintf(gopt.err_fp, a); \ 290 | exit(255); \ 291 | } while (0) 292 | #endif 293 | 294 | #ifndef XASSERT 295 | # define XASSERT(expr, a...) do { \ 296 | if (!(expr)) { \ 297 | xfprintf(gopt.err_fp, "%s:%d:%s() ASSERT(%s) ", __FILE__, __LINE__, __func__, #expr); \ 298 | xfprintf(gopt.err_fp, a); \ 299 | xfprintf(gopt.err_fp, " Exiting...\n"); \ 300 | exit(255); \ 301 | } \ 302 | } while (0) 303 | #endif 304 | 305 | #define XCLOSE(fd) do { \ 306 | if (fd < 0) { DEBUGF_R("*** WARNING *** Closing BAD fd\n"); break; } \ 307 | DEBUGF_W("Closing fd = %d\n", fd); \ 308 | close(fd); \ 309 | fd = -1; \ 310 | } while (0) 311 | 312 | #define XFCLOSE(fp) do { \ 313 | if (fp == NULL) { DEBUGF_R("*** WARNING *** Closing BAD fp\n"); break; } \ 314 | fclose(fp); \ 315 | fp = NULL; \ 316 | } while (0) 317 | 318 | 319 | #define XFD_SET(fd, set) do { \ 320 | if (fd < 0) { DEBUGF_R("WARNING: FD_SET(%d, )\n", fd); break; } \ 321 | FD_SET(fd, set); \ 322 | } while (0) 323 | 324 | #ifdef DEBUG 325 | # define HEXDUMP(a, _len) do { \ 326 | size_t _n = 0; \ 327 | xfprintf(gopt.err_fp, "%s:%d HEX[%zd] ", __FILE__, __LINE__, _len); \ 328 | while (_n < (_len)) xfprintf(gopt.err_fp, "%2.2x", ((unsigned char *)a)[_n++]); \ 329 | xfprintf(gopt.err_fp, "\n"); \ 330 | } while (0) 331 | # define HEXDUMPF(a, len, m...) do{xfprintf(gopt.err_fp, m); HEXDUMP(a, len);}while(0) 332 | #else 333 | # define HEXDUMP(a, len) 334 | # define HEXDUMPF(a, len, m...) 335 | #endif 336 | 337 | #endif /* !__GSRN_COMMON_H__ */ 338 | -------------------------------------------------------------------------------- /src/engine_server.c: -------------------------------------------------------------------------------- 1 | // Only linked against gsrnd 2 | #include "common.h" 3 | #include "gsrnd.h" 4 | #include "utils.h" 5 | #include "cli.h" 6 | #include "net.h" 7 | #include "engine.h" 8 | #include "engine_cli.h" 9 | #include "gopt.h" 10 | 11 | struct _gstats gstats; 12 | extern struct _cli *logstream_cli; 13 | 14 | struct _cli_list_param 15 | { 16 | int n; 17 | uint8_t opcode; 18 | }; 19 | 20 | struct _cli_shutdown_param 21 | { 22 | uint32_t timer_sec; 23 | uint32_t n_shutdown; 24 | uint32_t n_notlistening; 25 | }; 26 | 27 | static void cb_accept_cli(int ls, short ev, void *arg); 28 | static void gsrn_stats_init(void); 29 | static void gsrn_stats_reset(void); 30 | 31 | void 32 | cb_bev_status_cli(struct bufferevent *bev, short what, void *arg) 33 | { 34 | struct _cli *c = (struct _cli *)arg; 35 | 36 | DEBUGF("status=%d (%s)\n", what, BEV_strerror(what)); 37 | 38 | if (what & BEV_EVENT_CONNECTED) 39 | { 40 | c->flags |= FL_CLI_IS_CONNECTED; 41 | bufferevent_enable(bev, EV_READ); 42 | return; 43 | } 44 | 45 | if (what & BEV_EVENT_ERROR) 46 | { 47 | // if (!(c->flags & FL_CLI_IS_CONNECTED)) 48 | // else 49 | } 50 | 51 | if (c == logstream_cli) 52 | logstream_cli = NULL; 53 | 54 | CLI_free(c); 55 | } 56 | 57 | static void 58 | cb_peers_shutdown(struct _peer *p, struct _peer_l_root *plr, void *arg) 59 | { 60 | struct _cli_shutdown_param *param = (struct _cli_shutdown_param *)arg; 61 | if ((p == NULL) || (plr == NULL)) 62 | return; 63 | 64 | peer_l_id_t pl_id = PLR_L_get_id(plr); 65 | if (pl_id != PEER_L_LISTENING) 66 | { 67 | param->n_notlistening += 1; 68 | return; 69 | } 70 | 71 | // HERE: peer is LISTENING 72 | GS_LOG_VV("[%6u] %32s Shutdown", p->id, GS_addr128hex(NULL, p->addr)); 73 | param->n_shutdown += 1; 74 | PEER_goodbye(p); 75 | } 76 | 77 | static void 78 | cb_peers_list(struct _peer *p, struct _peer_l_root *plr, void *arg) 79 | { 80 | struct _cli_list_param *param = (struct _cli_list_param *)arg; 81 | if (p == NULL) 82 | { 83 | if (plr == NULL) 84 | return; 85 | 86 | DEBUGF("List-#%ld(%s) (%d entries):\n", PLR_L_get_id(plr), PEER_L_name(PLR_L_get_id(plr)), plr->n_entries); 87 | return; 88 | } 89 | 90 | DEBUGF(" [%u] %c addr=%s\n", p->id, IS_CS(p), strx128x(p->addr)); 91 | 92 | peer_l_id_t pl_id = PLR_L_get_id(plr); 93 | if (param->opcode == GSRN_CLI_OP_LIST_BAD) 94 | { 95 | if (pl_id != PEER_L_BAD_AUTH) 96 | return; 97 | } else if (param->opcode == GSRN_CLI_OP_LIST_ESTAB) { 98 | if (pl_id != PEER_L_CONNECTED) 99 | return; 100 | if (!PEER_IS_SERVER(p)) 101 | return; 102 | } else if (param->opcode == GSRN_CLI_OP_LIST_LISTEN) { 103 | if (pl_id != PEER_L_LISTENING) 104 | return; 105 | } else if (param->opcode == 0) { 106 | // ALL 107 | if (pl_id == PEER_L_CONNECTED) 108 | { 109 | // Do not show connected client (as we already show the connected buddy) 110 | if (!PEER_IS_SERVER(p)) 111 | return; 112 | } 113 | } else { 114 | DEBUGF("SHOULD NOT HAPPEN\n"); 115 | return; 116 | } 117 | 118 | DEBUGF_W("Sending peer-id %d\n", p->id); 119 | struct _cli_list_r msg; 120 | memset(&msg, 0, sizeof msg); 121 | msg.hdr.type = GSRN_CLI_TYPE_LIST_RESPONSE; 122 | msg.addr = htobe128(p->addr); 123 | msg.pl_id = pl_id; 124 | msg.peer_id = htonl(p->id); 125 | msg.ip = p->addr_in.sin_addr.s_addr; 126 | msg.port = p->addr_in.sin_port; 127 | memcpy(msg.gs_id, p->gs_id, sizeof msg.gs_id); 128 | 129 | msg.in_n = htonll(p->in_n); 130 | msg.out_n = htonll(p->out_n); 131 | 132 | if (p->state_usec != 0) 133 | msg.age_sec = htonl(GS_USEC_TO_SEC(gopt.usec_now - p->state_usec)); 134 | 135 | if (pl_id == PEER_L_CONNECTED) 136 | { 137 | GS_format_bps(msg.bps, sizeof msg.bps, PEER_get_bps(p), NULL); 138 | } 139 | 140 | if (param->n == 0) 141 | { 142 | msg.flags |= GSRN_FL_CLI_LIST_START; 143 | } 144 | param->n += 1; 145 | 146 | memset(&msg.flagstr, '-', sizeof msg.flagstr); 147 | if (p->flags & FL_PEER_IS_SAW_SSL_HELO) 148 | msg.fl.ssl = 'S'; 149 | uint8_t gpflags = p->gs_proto_flags; 150 | msg.fl.major = p->version_major; 151 | msg.fl.minor = p->version_minor; 152 | if (p->buddy != NULL) 153 | { 154 | if (p->buddy->flags & FL_PEER_IS_SAW_SSL_HELO) 155 | msg.fl.ssl = 'S'; 156 | gpflags |= p->buddy->gs_proto_flags; 157 | // Report the lowest of both version numbers to CLI. 158 | if (msg.fl.major > p->buddy->version_major) 159 | { 160 | msg.fl.major = p->buddy->version_major; 161 | msg.fl.minor = p->buddy->version_minor; 162 | } else if (msg.fl.major == p->buddy->version_major) { 163 | msg.fl.minor = MIN(msg.fl.minor, p->buddy->version_minor); 164 | } 165 | } 166 | msg.fl.major += '0'; 167 | msg.fl.minor += '0'; 168 | 169 | if (gpflags & GS_FL_PROTO_WAIT) 170 | msg.fl.wait = 'W'; 171 | if (gpflags & GS_FL_PROTO_CLIENT_OR_SERVER) 172 | msg.fl.x_client_or_server = 'X'; 173 | if (gpflags & GS_FL_PROTO_FAST_CONNECT) 174 | msg.fl.fast_connect = 'F'; 175 | if (gpflags & GS_FL_PROTO_LOW_LATENCY) 176 | msg.fl.low_latency = 'L'; 177 | 178 | uint32_t idle = 0; 179 | if (p->in_last_usec != 0) 180 | idle = MAX(0, GS_USEC_TO_SEC(gopt.usec_now - MAX(p->in_last_usec, p->out_last_usec))); 181 | msg.idle_sec = htonl(idle); 182 | 183 | if (p->buddy != NULL) 184 | { 185 | msg.buddy_ip = p->buddy->addr_in.sin_addr.s_addr; 186 | msg.buddy_port = p->buddy->addr_in.sin_port; 187 | } 188 | 189 | evbuffer_add(gopt.cli_out_evb, &msg, sizeof msg); 190 | } 191 | 192 | 193 | static void 194 | cb_cli_shutdown(struct evbuffer *eb, size_t len, void *arg) 195 | { 196 | struct _cli *c = (struct _cli *)arg; 197 | struct _cli_shutdown msg; 198 | 199 | evbuffer_remove(eb, &msg, sizeof msg); 200 | 201 | DEBUGF_B("CLI Shutdown received (timer=%usec)\n", msg.timer_sec); 202 | 203 | // Disconnect all listening peers. 204 | struct _cli_shutdown_param p; 205 | memset(&p, 0, sizeof p); 206 | p.timer_sec = msg.timer_sec; 207 | PEERS_walk(cb_peers_shutdown, &p); 208 | 209 | // Mark to terminate when last ESTABLISHED GS connction finishes? 210 | // NO: Do not self-terminate. Instead let GSRND linger 211 | // around doing nothing. If we would self-terminate here then systemd would 212 | // try to restart us - which is not what we want. Wait for GSRND to be shut down 213 | // by systemctl instead. 214 | 215 | CLI_printf(c, "%u peers shut down (LISTENING).", p.n_shutdown); 216 | if (p.n_notlistening > 0) 217 | CLI_printf(c, "WARNING: %u peers still connected (not LISTENING)",p.n_notlistening); 218 | } 219 | 220 | static void 221 | cb_cli_list(struct evbuffer *eb, size_t len, void *arg) 222 | { 223 | struct _cli *c = (struct _cli *)arg; 224 | struct _cli_list msg; 225 | 226 | evbuffer_remove(eb, &msg, sizeof msg); 227 | 228 | DEBUGF_B("CLI requested LIST (opcode=%2.2x)\n", msg.opcode); 229 | if (gopt.cli_out_evb == NULL) 230 | { 231 | gopt.cli_out_evb = evbuffer_new(); 232 | if (gopt.cli_out_evb == NULL) 233 | return; 234 | } 235 | 236 | size_t sz = evbuffer_get_length(gopt.cli_out_evb); 237 | if (sz > 0) 238 | DEBUGF_R("Ohh, already %zu bytes in cli_out_evb\n", sz); // CAN NOT HAPPEN 239 | 240 | gopt.usec_now = GS_usec(); 241 | struct _cli_list_param p; 242 | memset(&p, 0, sizeof p); 243 | p.opcode = msg.opcode; 244 | // Gather data (to gopt.evb_cli_out) 245 | PEERS_walk(cb_peers_list, &p); 246 | 247 | CLI_write(c, gopt.cli_out_evb); 248 | // CLI_printf() will also trigger a new prompt on cli side. 249 | CLI_printf(c, "Total %d", p.n); 250 | } 251 | 252 | static void 253 | cb_free(struct _peer *p, void *arg) 254 | { 255 | PEER_free(p); 256 | *(int *)arg += 1; 257 | } 258 | 259 | static void 260 | cb_cli_kill(struct evbuffer *eb, size_t len, void *arg) 261 | { 262 | struct _cli *c = (struct _cli *)arg; 263 | struct _cli_kill msg; 264 | 265 | evbuffer_remove(eb, &msg, sizeof msg); 266 | 267 | msg.addr = be128toh(msg.addr); 268 | msg.peer_id = ntohl(msg.peer_id); 269 | 270 | DEBUGF_B("CLI request KILL (len=%zu, id=%u addr=%s)\n", len, msg.peer_id, strx128x(msg.addr)); 271 | 272 | int killed = 0; 273 | if (msg.peer_id == 0) 274 | { 275 | // HERE: By GS-ADDRESS 276 | PEER_by_addr(msg.addr, cb_free, &killed); 277 | if (killed == 0) 278 | CLI_printf(c, "%s - No such address.", strx128x(msg.addr)); 279 | else 280 | CLI_printf(c, "%d connections terminated.", killed); 281 | } else { 282 | // HERE: By PEER-ID 283 | CLI_printf(c, "ERR: killing by ID not yet supported!"); 284 | 285 | } 286 | } 287 | 288 | static void 289 | cb_cli_stats(struct evbuffer *eb, size_t len, void *arg) 290 | { 291 | struct _cli *c = (struct _cli *)arg; 292 | struct _cli_stats msg; 293 | uint64_t usec_now = GS_usec(); 294 | 295 | DEBUGF("stats request\n"); 296 | evbuffer_remove(eb, &msg, sizeof msg); 297 | 298 | struct _cli_stats_r r; 299 | memset(&r, 0, sizeof r); 300 | 301 | r.hdr.type = GSRN_CLI_TYPE_STATS_RESPONSE; 302 | r.uptime_usec = usec_now - gstats.start_usec; 303 | r.since_reset_usec = usec_now - gstats.reset_usec; 304 | r.n_gs_connect = gstats.n_gs_connect; 305 | r.n_gs_listen = gstats.n_gs_listen; 306 | r.n_bad_auth = gstats.n_bad_auth; 307 | r.n_gs_refused = gstats.n_gs_refused; 308 | 309 | int i; 310 | for (i = 0; i < MAX_LISTS_BY_ADDR; i++) 311 | { 312 | if (i == PEER_L_CONNECTED) 313 | r.n_peers_total += gopt.t_peers.n_entries[i] / 2; 314 | else 315 | r.n_peers_total += gopt.t_peers.n_entries[i]; 316 | } 317 | 318 | // CONNECTED holds 1 server and 1 client but for the purpose of counting 319 | // we treat this as '1 connection'. 320 | r.n_peers_connected = gopt.t_peers.n_entries[PEER_L_CONNECTED] / 2; 321 | r.n_peers_listening = gopt.t_peers.n_entries[PEER_L_LISTENING]; 322 | r.n_peers_badauthwait = gopt.t_peers.n_entries[PEER_L_BAD_AUTH]; 323 | 324 | if (msg.opcode == GSRN_CLI_OP_STATS_RESET) 325 | { 326 | gsrn_stats_reset(); 327 | gstats.reset_usec = usec_now; 328 | } 329 | 330 | DEBUGF("senidng %zd\n", sizeof r); 331 | CLI_msg(c, &r, sizeof r); 332 | } 333 | 334 | #define CLI_BADOPCODE(_xc, _xop) CLI_printf(_xc, "ERR: Unknown opcode (%u)", _xop) 335 | static void 336 | cb_cli_stop(struct evbuffer *eb, size_t len, void *arg) 337 | { 338 | struct _cli *c = (struct _cli *)arg; 339 | struct _cli_stop msg; 340 | 341 | evbuffer_remove(eb, &msg, sizeof msg); 342 | 343 | if (msg.opcode == GSRN_CLI_OP_STOP_LISTEN_TCP) 344 | { 345 | PORTSQ_close(&gopt.ports_head); 346 | 347 | CLI_printf(c, "Stopped TCP port."); 348 | return; 349 | } 350 | 351 | CLI_BADOPCODE(c, msg.opcode); 352 | } 353 | 354 | static void 355 | cb_cli_set(struct evbuffer *eb, size_t len, void *arg) 356 | { 357 | struct _cli *c = (struct _cli *)arg; 358 | struct _cli_set msg; 359 | 360 | evbuffer_remove(eb, &msg, sizeof msg); 361 | 362 | if (msg.opcode == GSRN_CLI_OP_SET_PROTO) 363 | { 364 | gd.min_version_major = msg.version_major; 365 | gd.min_version_minor = msg.version_minor; 366 | 367 | CLI_printf(c, "Minimum Protocol set to %u.%u", gd.min_version_major, gd.min_version_minor); 368 | return; 369 | } 370 | 371 | if (msg.opcode == GSRN_CLI_OP_SET_LOG_IP) 372 | { 373 | const char *str; 374 | gd.is_log_ip = msg.opvalue1; 375 | str = "enabled"; 376 | if (msg.opvalue1 == 0) 377 | str = "disabled"; 378 | 379 | CLI_printf(c, "IP Address logging %s", str); 380 | return; 381 | } 382 | 383 | if (msg.opcode == GSRN_CLI_OP_SET_LOG_VERBOSITY) 384 | { 385 | CLI_printf(c, "Log verbosity set from %d to %d", gopt.verbosity, msg.opvalue1); 386 | gopt.verbosity = msg.opvalue1; 387 | return; 388 | } 389 | 390 | if (msg.opcode == GSRN_CLI_OP_SET_PORT_CLI) 391 | { 392 | DEBUGF("Changing CLI port to %u\n", msg.port); 393 | PORTSQ_close(&gopt.ports_cli_head); 394 | PORTSQ_free(&gopt.ports_cli_head); 395 | PORTSQ_add(&gopt.ports_cli_head, msg.port); 396 | PORTSQ_listen(&gopt.ports_cli_head, gopt.ip_cli, 0 /*not used*/, cb_accept_cli); 397 | 398 | CLI_printf(c, "CLI listening on port %u", msg.port); 399 | return; 400 | } 401 | 402 | if (msg.opcode == GSRN_CLI_OP_SET_LOGSTREAM) { 403 | DEBUGF("LOGSTREAM SET\n"); 404 | c->flags |= FL_CLI_IS_LOGSTREAM; 405 | gd.is_log_ip = 1; 406 | logstream_cli = c; 407 | return; 408 | } 409 | 410 | CLI_BADOPCODE(c, msg.opcode); 411 | } 412 | 413 | // Called by server 414 | static void 415 | cb_accept_cli(int ls, short ev, void *arg) 416 | { 417 | int sox; 418 | 419 | fd_limit_unlimited(); 420 | sox = fd_net_accept(ls); 421 | fd_limit_limited(); 422 | 423 | if (sox < 0) 424 | goto err; 425 | 426 | struct _cli *c; 427 | c = CLI_new(sox, NULL, 1 /*is_server*/); 428 | if (c == NULL) 429 | goto err; 430 | 431 | PKT_setcb(&c->pkt, GSRN_CLI_TYPE_LIST, sizeof (struct _cli_list), cb_cli_list, c); 432 | PKT_setcb(&c->pkt, GSRN_CLI_TYPE_KILL, sizeof (struct _cli_kill), cb_cli_kill, c); 433 | PKT_setcb(&c->pkt, GSRN_CLI_TYPE_STOP, sizeof (struct _cli_stop), cb_cli_stop, c); 434 | PKT_setcb(&c->pkt, GSRN_CLI_TYPE_SET, sizeof (struct _cli_set), cb_cli_set, c); 435 | PKT_setcb(&c->pkt, GSRN_CLI_TYPE_SHUTDOWN, sizeof (struct _cli_shutdown), cb_cli_shutdown, c); 436 | PKT_setcb(&c->pkt, GSRN_CLI_TYPE_STATS, sizeof (struct _cli_stats), cb_cli_stats, c); 437 | bufferevent_enable(c->bev, EV_READ); 438 | 439 | return; 440 | err: 441 | XCLOSE(sox); 442 | } 443 | 444 | static void 445 | gsrn_stats_init(void) 446 | { 447 | memset(&gstats, 0, sizeof gstats); 448 | gstats.start_usec = GS_usec(); 449 | gstats.reset_usec = gstats.start_usec; 450 | } 451 | 452 | static void 453 | gsrn_stats_reset(void) 454 | { 455 | uint64_t usec; 456 | 457 | usec = gstats.start_usec; 458 | memset(&gstats, 0, sizeof gstats); 459 | gstats.start_usec = usec; 460 | gstats.reset_usec = GS_usec(); 461 | } 462 | 463 | void 464 | init_engine(void) 465 | { 466 | gsrn_stats_init(); 467 | 468 | // Raise FD Limit but keep it just below Hard-Limit (ulimit -Hn) and keep 469 | // those few reserved for CLI port connections: 470 | struct rlimit *r = (struct rlimit *)&gopt.rlim_fd; 471 | if (fd_limit_init() != 0) 472 | ERREXIT("getrlimit()=%s\n", strerror(errno)); 473 | if (r->rlim_max <= 1024) 474 | GS_LOG("WARNING: Max fd limit is %lu", r->rlim_max); 475 | GS_LOG("File descriptor limit set to %lu+%d (was %lu)", r->rlim_max - GSRN_FD_RESERVE, GSRN_FD_RESERVE, r->rlim_cur); 476 | if (fd_limit_limited() != 0) 477 | ERREXIT("setrlimit()=%s\n", strerror(errno));; 478 | 479 | // Start listening 480 | PORTSQ_listen(&gopt.ports_head, INADDR_ANY, GSRN_DEFAULT_PORT, cb_accept); 481 | PORTSQ_listen(&gopt.ports_cli_head, gopt.ip_cli, CLI_DEFAULT_PORT, cb_accept_cli); 482 | } -------------------------------------------------------------------------------- /src/engine_client.c: -------------------------------------------------------------------------------- 1 | // Only linked against gsrn_cli 2 | #include "common.h" 3 | #include "utils.h" 4 | #include "engine.h" 5 | #include "engine_cli.h" 6 | #include "net.h" 7 | #include "cli.h" 8 | #include "gopt.h" 9 | 10 | #define PROMPT "gsrn> " 11 | static struct evbuffer *g_evb; 12 | struct _cli *g_cli; 13 | static int g_is_tty; 14 | static struct event *g_ev_stdin; 15 | 16 | typedef void (*dp_func_t)(char *line, char *end); 17 | 18 | static void cmd_nocmd(char *opt, char *end); 19 | 20 | static void cmd_help(char *opt, char *end); 21 | static void cmd_list(char *opt, char *end); 22 | static void cmd_kill(char *opt, char *end); 23 | static void cmd_exit(char *opt, char *end); 24 | static void cmd_stats(char *opt, char *end); 25 | 26 | static void cmd_list_server(char *opt, char *end); 27 | static void cmd_list_client(char *opt, char *end); 28 | static void cmd_list_bad(char *opt, char *end); 29 | 30 | static void cmd_stop(char *opt, char *end); 31 | static void cmd_stop_listen(char *opt, char *end); 32 | static void cmd_stop_listen_tcp(char *opt, char *end); 33 | static void cmd_stop_listen_gsocket(char *opt, char *end); 34 | 35 | static void cmd_set(char *opt, char *end); 36 | static void cmd_set_port_cli(char *opt, char *end); 37 | static void cmd_set_proto(char *opt, char *end); 38 | 39 | static void cmd_set_log_ip(char *opt, char *end); 40 | static void cmd_set_log_verbosity(char *opt, char *end); 41 | 42 | static void cmd_help_list(char *opt, char *end); 43 | static void cmd_help_set(char *opt, char *end); 44 | 45 | static void cmd_shutdown(char *opt, char *end); 46 | 47 | static void print_prompt(void); 48 | 49 | #define NOCMD_PRINT() do {printf("Unknown command. Try 'help'.\n"); print_prompt();} while(0) 50 | 51 | // tuples of 'cmd-string' <=> 'function' 52 | struct dp 53 | { 54 | char *cmd; 55 | dp_func_t func; 56 | }; 57 | 58 | struct dp dps[] = { 59 | { NULL , cmd_nocmd}, // default action if cmd not found 60 | { "help" , cmd_help}, 61 | { "list" , cmd_list}, 62 | { "ls" , cmd_list}, // 'list' can have nested commands 63 | { "kill" , cmd_kill}, 64 | { "stop" , cmd_stop}, 65 | { "set" , cmd_set}, 66 | { "shutdown", cmd_shutdown}, 67 | { "stats" , cmd_stats}, 68 | { "status" , cmd_stats}, 69 | { "exit" , cmd_exit}, 70 | { "quit" , cmd_exit} 71 | }; 72 | 73 | // nested 'list' commands 74 | struct dp dp_list[] = { 75 | { NULL , cmd_nocmd}, // default action if cmd not found 76 | { "all" , cmd_list}, 77 | { "server" , cmd_list_server}, 78 | { "established" , cmd_list_client}, 79 | { "client" , cmd_list_client}, 80 | { "bad" , cmd_list_bad} 81 | }; 82 | 83 | struct dp dp_stop[] = { 84 | { NULL , cmd_nocmd /*stop_default*/}, 85 | { "listen" , cmd_stop_listen} 86 | }; 87 | 88 | struct dp dp_stop_listen[] = { 89 | { NULL , cmd_nocmd /*stop_listen_default*/}, 90 | { "tcp" , cmd_stop_listen_tcp}, 91 | { "gsocket" , cmd_stop_listen_gsocket} 92 | }; 93 | 94 | struct dp dp_set[] = { 95 | { NULL , cmd_nocmd}, 96 | { "protocol" , cmd_set_proto}, 97 | { "port.cli" , cmd_set_port_cli}, 98 | { "log.verbosity", cmd_set_log_verbosity}, 99 | { "log.verbose" , cmd_set_log_verbosity}, 100 | { "log.ip" , cmd_set_log_ip} 101 | }; 102 | 103 | struct dp dp_help[] = { 104 | { NULL , cmd_nocmd}, 105 | { "list" , cmd_help_list}, 106 | { "set" , cmd_help_set} 107 | }; 108 | 109 | void 110 | cb_bev_status_cli(struct bufferevent *bev, short what, void *arg) 111 | { 112 | struct _cli *c = (struct _cli *)arg; 113 | 114 | // DEBUGF("status=%d (%s)\n", what, BEV_strerror(what)); 115 | 116 | if (what & BEV_EVENT_CONNECTED) 117 | { 118 | c->flags |= FL_CLI_IS_CONNECTED; 119 | bufferevent_enable(bev, EV_READ); 120 | return; 121 | } 122 | 123 | if (what & BEV_EVENT_ERROR) 124 | { 125 | uint32_t ip = htonl(gopt.ip_cli); 126 | if (!(c->flags & FL_CLI_IS_CONNECTED)) 127 | fprintf(stderr, "\nERROR: connect(%s:%d): %s\n", int_ntoa(ip), gopt.port_cli, strerror(errno)); 128 | else 129 | fprintf(stderr, "\n%s\n", strerror(errno)); 130 | } 131 | 132 | if (what & BEV_EVENT_EOF) 133 | { 134 | fprintf(stderr, "ERROR: Connection closed by foreign host.\n"); 135 | } 136 | fflush(stdout); 137 | 138 | CLI_free(c); 139 | exit(127); 140 | } 141 | 142 | // Dispatch commands such as 'lis serv more options here' would call 143 | // cmd_list_server "more options here" 144 | static void 145 | dispatch(char *l, char *end, struct dp *d, int n) 146 | { 147 | int i; 148 | char *lend = end; 149 | 150 | if (end - l <= 0) 151 | return; 152 | 153 | // Example: 'lis serv more options here' 154 | char *opt = strchr(l, ' '); 155 | if (opt != NULL) 156 | { 157 | lend = opt; 158 | *opt = '\0'; 159 | opt += 1; 160 | while (*opt == ' ') 161 | opt += 1; 162 | } else { 163 | opt = end; 164 | } 165 | 166 | for (i = 1; i < n; i++) 167 | { 168 | if (memcmp(l, d[i].cmd, MIN(strlen(d[i].cmd), lend - l)) != 0) 169 | continue; 170 | 171 | (*d[i].func)(opt, end); 172 | return; 173 | } 174 | 175 | // Execute default function 176 | (*d[0].func)(opt, end); 177 | } 178 | 179 | #define DP_TRYCALL_NESTED(xopt, xend, _xdpl) do{ \ 180 | if (xend - xopt <= 0) \ 181 | break; \ 182 | dispatch(xopt, xend, _xdpl, sizeof _xdpl / sizeof *_xdpl); \ 183 | return; \ 184 | } while (0) 185 | 186 | static void 187 | cmd_stats(char *opt, char *end) 188 | { 189 | struct _cli_stats msg; 190 | memset(&msg, 0, sizeof msg); 191 | msg.hdr.type = GSRN_CLI_TYPE_STATS; 192 | 193 | if (*opt == 'r') 194 | msg.opcode = GSRN_CLI_OP_STATS_RESET; 195 | 196 | CLI_msg(g_cli, &msg, sizeof msg); 197 | } 198 | 199 | ////////////////// DP-LIST 200 | 201 | static void 202 | cmd_list_msg(uint8_t opcode) 203 | { 204 | struct _cli_list msg; 205 | memset(&msg, 0, sizeof msg); 206 | msg.hdr.type = GSRN_CLI_TYPE_LIST; 207 | 208 | msg.opcode = opcode; 209 | 210 | CLI_msg(g_cli, &msg, sizeof msg); 211 | } 212 | 213 | static void 214 | cmd_list_server(char *opt, char *end) 215 | { 216 | cmd_list_msg(GSRN_CLI_OP_LIST_LISTEN); 217 | } 218 | 219 | static void 220 | cmd_list_client(char *opt, char *end) 221 | { 222 | cmd_list_msg(GSRN_CLI_OP_LIST_ESTAB); 223 | } 224 | 225 | static void 226 | cmd_list_bad(char *opt, char *end) 227 | { 228 | cmd_list_msg(GSRN_CLI_OP_LIST_BAD); 229 | } 230 | 231 | static void 232 | cmd_list(char *opt, char *end) 233 | { 234 | DP_TRYCALL_NESTED(opt, end, dp_list); 235 | 236 | struct _cli_list msg; 237 | memset(&msg, 0, sizeof msg); 238 | msg.hdr.type = GSRN_CLI_TYPE_LIST; 239 | 240 | CLI_msg(g_cli, &msg, sizeof msg); 241 | } 242 | 243 | /////////////////// DP-STOP 244 | static void 245 | cmd_stop(char *opt, char *end) 246 | { 247 | DP_TRYCALL_NESTED(opt, end, dp_stop); 248 | 249 | NOCMD_PRINT(); 250 | } 251 | 252 | static void 253 | cmd_stop_listen(char *opt, char *end) 254 | { 255 | DP_TRYCALL_NESTED(opt, end, dp_stop_listen); 256 | 257 | NOCMD_PRINT(); 258 | } 259 | 260 | static void 261 | cmd_stop_listen_tcp(char *opt, char *end) 262 | { 263 | struct _cli_stop msg; 264 | 265 | memset(&msg, 0, sizeof msg); 266 | 267 | msg.hdr.type = GSRN_CLI_TYPE_STOP; 268 | msg.opcode = GSRN_CLI_OP_STOP_LISTEN_TCP; 269 | 270 | CLI_msg(g_cli, &msg, sizeof msg); 271 | } 272 | 273 | static void 274 | cmd_stop_listen_gsocket(char *opt, char *end) 275 | { 276 | printf("Not yet implemented\n"); fflush(stdout); 277 | } 278 | 279 | ///////////////// SHUTDOWN 280 | static void 281 | cmd_shutdown(char *opt, char *end) 282 | { 283 | struct _cli_shutdown msg; 284 | memset(&msg, 0, sizeof msg); 285 | 286 | msg.hdr.type = GSRN_CLI_TYPE_SHUTDOWN; 287 | 288 | CLI_msg(g_cli, &msg, sizeof msg); 289 | } 290 | 291 | //////////////// DP-SET 292 | static void 293 | cmd_set(char *opt, char *end) 294 | { 295 | DP_TRYCALL_NESTED(opt, end, dp_set); 296 | 297 | NOCMD_PRINT(); 298 | } 299 | 300 | static void 301 | cmd_set_msg(uint8_t opcode, uint8_t op1, uint8_t vmajor, uint8_t vminor, uint16_t port) 302 | { 303 | struct _cli_set msg; 304 | memset(&msg, 0, sizeof msg); 305 | msg.hdr.type = GSRN_CLI_TYPE_SET; 306 | 307 | msg.opcode = opcode; 308 | msg.opvalue1 = op1; 309 | msg.version_major = vmajor; 310 | msg.version_minor = vminor; 311 | msg.port = port; 312 | 313 | CLI_msg(g_cli, &msg, sizeof msg); 314 | } 315 | static void 316 | cmd_set_proto(char *opt, char *end) 317 | { 318 | char *str; 319 | str = strchr(opt, '.'); 320 | if (str == NULL) 321 | goto err; 322 | 323 | cmd_set_msg(GSRN_CLI_OP_SET_PROTO, 0, atoi(opt), atoi(str + 1), 0); 324 | return; 325 | err: 326 | NOCMD_PRINT(); 327 | } 328 | 329 | static void 330 | cmd_set_port_cli(char *opt, char *end) 331 | { 332 | cmd_set_msg(GSRN_CLI_OP_SET_PORT_CLI, 0, 0, 0, atoi(opt)); 333 | } 334 | 335 | static void 336 | cmd_set_log_ip(char *opt, char *end) 337 | { 338 | cmd_set_msg(GSRN_CLI_OP_SET_LOG_IP, atoi(opt), 0, 0, 0); 339 | } 340 | 341 | static void 342 | cmd_set_log_verbosity(char *opt, char *end) 343 | { 344 | cmd_set_msg(GSRN_CLI_OP_SET_LOG_VERBOSITY, atoi(opt), 0, 0, 0); 345 | } 346 | 347 | static void 348 | cmd_kill(char *opt, char *end) 349 | { 350 | struct _cli_kill msg; 351 | 352 | memset(&msg, 0, sizeof msg); 353 | // STOP HERE: converting to strx128 deadbeef and back does not always give 16 char hex string (which it should!) 354 | // and also could implement 'kill address id' 355 | msg.hdr.type = GSRN_CLI_TYPE_KILL; 356 | if (end - opt <= 10) 357 | msg.peer_id = htonl(atoi(opt)); // It's a PEER-ID 358 | else // ..or a hex address 359 | msg.addr = htobe128(GS_hexto128(opt)); // FIXME, convert hex to addr 360 | 361 | CLI_msg(g_cli, &msg, sizeof msg); 362 | } 363 | 364 | ///////////////// DP 365 | 366 | static void 367 | cmd_nocmd(char *opt, char *end) 368 | { 369 | NOCMD_PRINT(); 370 | } 371 | 372 | static void 373 | cmd_exit(char *opt_notused, char *end) 374 | { 375 | printf("[Bye]\n"); 376 | exit(0); 377 | } 378 | 379 | static void 380 | print_prompt(void) 381 | { 382 | if (g_is_tty) 383 | printf(D_RED(PROMPT)); 384 | 385 | fflush(stdout); 386 | } 387 | 388 | static void 389 | cmd_help_list(char *opt, char *end) 390 | { 391 | // for i in 6a 6b 6c 6d 6e 71 74 75 76 77 78; do printf "0x$i \x$i \x1b(0\x$i\x1b(B\n"; done 392 | printf("" 393 | "all - list all connections\n" 394 | "server - list listening servers\n" 395 | "client - list connected clients\n" 396 | "bad - list bad peers\n" 397 | "Note: HSXFLWAI\n" 398 | " Domain prefix ┘││││││└─ Minor Version\n" 399 | " SRP enabled ─┘││││└── Major Version\n" 400 | " Client or Server ──┘││└─── Client Waiting\n" 401 | " Fast Connect ───┘└──── Low Latency (interactive)\n" 402 | ""); 403 | 404 | print_prompt(); 405 | } 406 | 407 | static void 408 | cmd_help_set(char *opt, char *end) 409 | { 410 | printf("" 411 | "proto - Set minimum accepted protocol version\n" 412 | "log.ip [0/1] - Toggle IP logging\n" 413 | "log.verbose [012] - Set verbosity level\n" 414 | "port.cli - Change CLI listening Port\n" 415 | ""); 416 | 417 | print_prompt(); 418 | } 419 | 420 | static void 421 | cmd_help(char *opt, char *end) 422 | { 423 | DP_TRYCALL_NESTED(opt, end, dp_help); 424 | 425 | printf("" 426 | "help - this help. Try 'help [command]' for details.\n" 427 | "stats - Show statistics.\n" 428 | "list - list all peers\n" 429 | "stop listen tcp - Stop accepting GSRN connections (TCP & SSL)\n" 430 | "kill - Disconnect peer by id or address\n" 431 | "set - Toggle settings\n" 432 | "shutdown [timer] - Disconnect GS-listeners\n" 433 | "quit - Quit\n" 434 | ""); 435 | 436 | print_prompt(); 437 | } 438 | 439 | static void 440 | cb_read_stdin(int fd, short what, void *arg) 441 | { 442 | char *l; 443 | size_t len; 444 | int ret; 445 | 446 | ret = evbuffer_read(g_evb, fd, -1 /*all available*/); 447 | if (ret <= 0) 448 | { 449 | if (!g_is_tty) 450 | exit(0); 451 | ERREXIT("read(%d)=%d: %s\n", fd, ret, strerror(errno)); 452 | } 453 | 454 | while (1) 455 | { 456 | l = evbuffer_readln(g_evb, &len, EVBUFFER_EOL_CRLF); 457 | if (l == NULL) 458 | break; 459 | 460 | if (*l == '\0') 461 | print_prompt(); 462 | // DEBUGF("line='%s'\n", l); 463 | dispatch(l, l+len, dps, sizeof dps / sizeof *dps); 464 | free(l); 465 | } 466 | } 467 | 468 | // Remove trailing zeros from message. 469 | static size_t 470 | ls_trim_msg(void *ptr, size_t len) { 471 | uint8_t *msg = (uint8_t *)ptr; 472 | uint8_t *end = (uint8_t *)msg + len; 473 | while (end > msg && *(end - 1) == '\0') 474 | end--; 475 | 476 | return end - msg; 477 | } 478 | 479 | static void 480 | ls_listen_print(struct _cli_logstream *ls) { 481 | struct _gs_listen *msg = (struct _gs_listen *)ls->msg; 482 | char hex[sizeof *msg * 2 + 1]; 483 | char *str = NULL; 484 | 485 | if (gopt.verbosity > 0) 486 | str = GS_bin2hex(hex, sizeof hex, msg, ls_trim_msg(msg, sizeof *msg)); 487 | char hex2[GS_ID_SIZE * 2 + 1]; 488 | printf("%ld LISTEN %32s 0x%16s %s:%u [%s]\n", time(NULL), GS_addr128hex(NULL, ls->addr), GS_bin2hex(hex2, sizeof hex2, msg->id, sizeof msg->id), inet_ntoa(ls->ipa.sin_addr), ntohs(ls->ipa.sin_port), str?:""); 489 | } 490 | 491 | static void 492 | ls_connect_print(struct _cli_logstream *ls) { 493 | struct _gs_connect *msg = (struct _gs_connect *)ls->msg; 494 | char hex[sizeof *msg * 2 + 1]; 495 | char ipstr[64]; 496 | char *str = NULL; 497 | 498 | snprintf(ipstr, sizeof ipstr, "%s", inet_ntoa(ls->ipb.sin_addr)); 499 | if (gopt.verbosity > 0) 500 | str = GS_bin2hex(hex, sizeof hex, msg, ls_trim_msg(msg, sizeof *msg)); 501 | printf("CONNECT %32s %s:%u -> %s:%u [%s]\n", GS_addr128hex(NULL, ls->addr), ipstr, ntohs(ls->ipb.sin_port), inet_ntoa(ls->ipa.sin_addr), ntohs(ls->ipa.sin_port), str?:""); 502 | } 503 | 504 | static void 505 | cb_cli_logstream(struct evbuffer *eb, size_t len, void *arg) 506 | { 507 | struct _cli_logstream msg; 508 | 509 | evbuffer_remove(eb, &msg, sizeof msg); 510 | if (msg.opcode == GSRN_CLI_OP_LS_LISTEN) 511 | ls_listen_print(&msg); 512 | else if (msg.opcode == GSRN_CLI_OP_LS_CONNECT) 513 | ls_connect_print(&msg); 514 | else 515 | printf("Unknown LOGSTREAM message (opcode=%u)\n", msg.opcode); 516 | #if 0 517 | struct _cli_msg msg; 518 | char buf[len - sizeof msg + 1]; 519 | 520 | evbuffer_remove(eb, &msg, GSRN_CLI_HDR_TLV_SIZE); 521 | evbuffer_remove(eb, buf, len - GSRN_CLI_HDR_TLV_SIZE); 522 | buf[sizeof buf - 1] = '\0'; 523 | 524 | // An empty string triggers just the prompt but no output or \n 525 | if (buf[0] == '\0') 526 | return; 527 | 528 | printf("%s\n", buf); 529 | #endif 530 | } 531 | 532 | static void 533 | cb_cli_list_r(struct evbuffer *eb, size_t len, void *arg) 534 | { 535 | struct _cli_list_r msg; 536 | 537 | evbuffer_remove(eb, &msg, sizeof msg); 538 | 539 | if (msg.flags & GSRN_FL_CLI_LIST_START) 540 | printf("[ ID] Address GS-ID HSXFLWAI State Age Server Address - Client Address ( idle) Traffic [ bps]\n"); 541 | // [ 4] 435701b27bf6e7467bef67fb3a4f2c17 a-----11 LISTEN 2s 10.0.2.2:521 - 10.0.2.2:526 ( 2s) 2.6KB [ 1.3KB/s] 542 | // [ 4] 435701b27bf6e7467bef67fb3a4f2c17 zSXFLW12 ESTABL 99h05m 123.456.789.123:65123 - 111.222.333.444:64567 (99h03m) 2.6KB [ 1.3KB/s] 543 | 544 | uint8_t hostname_id; 545 | hostname_id = GS_ADDR_get_hostname_id((uint8_t *)&msg.addr); // Network Byte Order 546 | msg.peer_id = ntohl(msg.peer_id); 547 | msg.in_n = ntohll(msg.in_n); 548 | msg.out_n = ntohll(msg.out_n); 549 | 550 | uint32_t age_sec = htonl(msg.age_sec); 551 | 552 | char traffic[16]; 553 | GS_format_bps(traffic, sizeof traffic, msg.in_n + msg.out_n, NULL); 554 | 555 | msg.bps[sizeof msg.bps - 1] = '\0'; 556 | 557 | char since[GS_SINCE_MAXSIZE]; 558 | GS_format_since(since, sizeof since, age_sec); 559 | 560 | char idle[GS_SINCE_MAXSIZE]; 561 | GS_format_since(idle, sizeof idle, ntohl(msg.idle_sec)); 562 | 563 | char ipport[32]; 564 | uint32_t ip; 565 | memcpy(&ip, &msg.ip, sizeof ip); 566 | snprintf(ipport, sizeof ipport, "%s:%u", int_ntoa(ip), ntohs(msg.port)); 567 | 568 | char hex[GS_ID_SIZE * 2 + 1]; 569 | printf("[%9u] %32s 0x%16s %c%7.7s %s %*s %-21s", msg.peer_id, GS_addr2hex(NULL, &msg.addr), GS_bin2hex(hex, sizeof hex, msg.gs_id, sizeof msg.gs_id), 'a'+hostname_id, msg.flagstr, PEER_L_name(msg.pl_id), GS_SINCE_MAXSIZE - 1, since, ipport); 570 | 571 | if (msg.buddy_port != 0) 572 | { 573 | memcpy(&ip, &msg.buddy_ip, sizeof ip); // align 574 | snprintf(ipport, sizeof ipport, "%s:%u", int_ntoa(ip), ntohs(msg.buddy_port)); 575 | printf(" - %-21s (%*s) %s [%s/s] \n", ipport, GS_SINCE_MAXSIZE - 1, idle, traffic, msg.bps); 576 | } else { 577 | printf("\n"); 578 | } 579 | } 580 | 581 | static void 582 | printt(const char *prefix, uint64_t usec) 583 | { 584 | char buf[64]; 585 | time_t t = time(NULL); 586 | time_t sec = GS_USEC_TO_SEC(usec); 587 | time_t msec = GS_USEC_TO_MSEC(usec); 588 | 589 | t = t - sec; 590 | 591 | char *cstr = asctime(gmtime(&t)); 592 | cstr[strcspn(cstr, "\r\n")] = 0; 593 | 594 | printf("%s%s UTC; %s; %.03f sec\n", prefix, cstr, GS_format_since(buf, sizeof buf, sec), (float)msec / 1000); 595 | } 596 | 597 | static void 598 | cb_cli_stats_r(struct evbuffer *eb, size_t len, void *arg) 599 | { 600 | struct _cli_stats_r msg; 601 | 602 | evbuffer_remove(eb, &msg, sizeof msg); 603 | 604 | printt("Uptime : ", msg.uptime_usec); 605 | printt("Period : ", msg.since_reset_usec); 606 | printf("GS-Listen : %"PRIu64"\n", msg.n_gs_listen); 607 | printf("GS-Bad Auth : %"PRIu64"\n", msg.n_bad_auth); 608 | printf("GS-Connect : %"PRIu64"\n", msg.n_gs_connect); 609 | printf("GS-Refused : %"PRIu64"\n", msg.n_gs_refused); 610 | printf("Listening : %"PRIu32"\n", msg.n_peers_listening); 611 | printf("Connected : %"PRIu32"\n", msg.n_peers_connected); 612 | printf("BadAuthWait : %"PRIu32"\n", msg.n_peers_badauthwait); 613 | printf("Waiting : %"PRId32"\n", msg.n_peers_total - (msg.n_peers_listening + msg.n_peers_connected + msg.n_peers_badauthwait)); 614 | 615 | print_prompt(); 616 | } 617 | 618 | // Variable length 619 | static void 620 | cb_cli_msg(struct evbuffer *eb, size_t len, void *arg) 621 | { 622 | struct _cli_msg msg; 623 | char buf[len - sizeof msg + 1]; 624 | 625 | evbuffer_remove(eb, &msg, GSRN_CLI_HDR_TLV_SIZE); 626 | evbuffer_remove(eb, buf, len - GSRN_CLI_HDR_TLV_SIZE); 627 | buf[sizeof buf - 1] = '\0'; 628 | 629 | // An empty string triggers just the prompt but no output or \n 630 | if (buf[0] != '\0') 631 | printf("%s\n", buf); 632 | 633 | if (gopt.flags & GSR_FL_LOGSTREAM) 634 | return; 635 | 636 | print_prompt(); 637 | } 638 | 639 | void 640 | init_engine(void) 641 | { 642 | struct sockaddr_in addr; 643 | memset(&addr, 0, sizeof addr); 644 | addr.sin_family = AF_INET; 645 | addr.sin_addr.s_addr = htonl(gopt.ip_cli); 646 | addr.sin_port = htons(gopt.port_cli); 647 | 648 | g_is_tty = isatty(STDIN_FILENO); 649 | g_cli = CLI_new(-1, NULL, 0 /*is_server*/); 650 | bufferevent_socket_connect(g_cli->bev, (struct sockaddr *)&addr, sizeof addr); 651 | 652 | PKT_setcb(&g_cli->pkt, GSRN_CLI_TYPE_LOGSTREAM, sizeof (struct _cli_logstream), cb_cli_logstream, g_cli); 653 | PKT_setcb(&g_cli->pkt, GSRN_CLI_TYPE_MSG, 0 /*variable length*/, cb_cli_msg, g_cli); 654 | // Logstream reads no STDIN 655 | if (gopt.flags & GSR_FL_LOGSTREAM) { 656 | cmd_set_msg(GSRN_CLI_OP_SET_LOGSTREAM, 1, 0, 0, 0); 657 | return; 658 | } 659 | 660 | PKT_setcb(&g_cli->pkt, GSRN_CLI_TYPE_LIST_RESPONSE, sizeof (struct _cli_list_r), cb_cli_list_r, g_cli); 661 | PKT_setcb(&g_cli->pkt, GSRN_CLI_TYPE_STATS_RESPONSE, sizeof (struct _cli_stats_r), cb_cli_stats_r, g_cli); 662 | 663 | g_evb = evbuffer_new(); 664 | g_ev_stdin = event_new(gopt.evb, STDIN_FILENO, EV_READ | EV_PERSIST, cb_read_stdin, NULL); 665 | event_add(g_ev_stdin, NULL); 666 | 667 | 668 | print_prompt(); 669 | } 670 | -------------------------------------------------------------------------------- /src/engine.c: -------------------------------------------------------------------------------- 1 | 2 | #if 0 3 | openssl s_client -quiet -connect 127.1:443 /dev/null 4 | 5 | SETUP 6 | ====== 7 | echo 4096 >/proc/sys/net/core/rmem_default 8 | echo 8196 >/proc/sys/net/core/wmem_default 9 | 10 | DEFAULTS 11 | ======== 12 | echo 212992 >/proc/sys/net/core/rmem_default 13 | echo 212992 >/proc/sys/net/core/wmem_default 14 | 15 | #endif 16 | 17 | #include "common.h" 18 | #include "utils.h" 19 | #include "net.h" 20 | #include "packet.h" 21 | #include "peer.h" 22 | #include "cli.h" 23 | #include "gsrnd.h" 24 | #include "protocol.h" 25 | #include "engine.h" 26 | #include "gopt.h" 27 | 28 | static void cb_bev_relay_read(struct bufferevent *bev, void *arg); 29 | static void buddy_up(struct _peer *server, struct _peer *client); 30 | 31 | extern struct _cli *logstream_cli; 32 | 33 | static void 34 | logstream_msg(struct _cli_logstream *ls, uint8_t opcode, struct _peer *p) { 35 | ls->hdr.type = GSRN_CLI_TYPE_LOGSTREAM; 36 | ls->opcode = opcode; 37 | if (p != NULL) 38 | memcpy(&ls->addr, &p->addr, sizeof ls->addr); 39 | CLI_msg(logstream_cli, ls, sizeof *ls); 40 | } 41 | 42 | // Send log file to cli (if logstream is enabled) 43 | static void 44 | logstream_listen(struct _peer *p, struct _gs_listen *msg) { 45 | if (logstream_cli == NULL) 46 | return; 47 | 48 | // Log only the _first_ LISTEN: The client immediately establishes another 49 | // LISTEN() connection after the first connection. Dont log in. 50 | // Get total number of all PEERS of this ADDRESS 51 | // Subtract all that are in a BAD state 52 | int n; 53 | n = p->plr->pl_mgr->n_entries - p->plr->pl_mgr->plr[PEER_L_BAD_AUTH].n_entries;; 54 | if (n != 1) 55 | return; 56 | 57 | // First ENTRY. Nobody else in ACCEPT or CONNECT state. 58 | struct _cli_logstream ls; 59 | memcpy(&ls.ipa, &p->addr_in, sizeof ls.ipa); 60 | if (msg != NULL) 61 | memcpy(&ls.msg, msg, sizeof ls.msg); 62 | else 63 | memset(&ls.msg, 0, sizeof ls.msg); 64 | 65 | logstream_msg(&ls, GSRN_CLI_OP_LS_LISTEN, p); 66 | } 67 | 68 | 69 | // #if sizeof (struct _gs_connect) != sizeof (struct _gs_listen) 70 | // # error "Sizeof _gs_connect and _gs_listen not the same" 71 | // #endif 72 | static void 73 | logstream_connect(struct _peer *p, struct _gs_connect *msg) 74 | { 75 | if (logstream_cli == NULL) 76 | return; 77 | 78 | struct _cli_logstream ls; 79 | if (p->flags & FL_PEER_IS_SERVER) { 80 | memcpy(&ls.ipa, &p->addr_in, sizeof ls.ipa); 81 | memcpy(&ls.ipb, &p->buddy->addr_in, sizeof ls.ipb); 82 | } else { 83 | memcpy(&ls.ipa, &p->buddy->addr_in, sizeof ls.ipa); 84 | memcpy(&ls.ipb, &p->addr_in, sizeof ls.ipb); 85 | } 86 | 87 | if (msg != NULL) 88 | memcpy(&ls.msg, msg, sizeof ls.msg); 89 | else 90 | memset(&ls.msg, 0, sizeof ls.msg); 91 | 92 | logstream_msg(&ls, GSRN_CLI_OP_LS_CONNECT, p); 93 | } 94 | 95 | 96 | void 97 | cb_gsrn_protocol_error(struct evbuffer *eb, size_t len, void *arg) 98 | { 99 | struct _peer *p = (struct _peer *)arg; 100 | 101 | GSRN_send_status_fatal(p, GS_STATUS_CODE_PROTOERROR, "Protocol error"); 102 | PEER_goodbye(p); 103 | } 104 | 105 | static void 106 | cb_evt_bad_auth_delay(int fd_notusec, short event, void *arg) 107 | { 108 | struct _peer *p = (struct _peer *)arg; 109 | 110 | DEBUGF_W("[%6u] {fd=%d} Sending delayed BAD_AUTH\n", p->id, p->fd); 111 | GSRN_send_status_fatal(p, GS_STATUS_CODE_BAD_AUTH, NULL); 112 | PEER_goodbye(p); 113 | } 114 | 115 | // Check if DISCONNECT/ERROR should be delayed (return 0) or immediately. 116 | // return -1 (immediately / error) 117 | // Called from gsrn_listen() 118 | static int 119 | gsrn_bad_auth_delay(struct _peer *p) 120 | { 121 | struct _peer_l_mgr *pl_mgr = NULL; 122 | pl_mgr = PEER_get_mgr(p->addr); 123 | if (pl_mgr == NULL) 124 | return -1; 125 | 126 | uint64_t last_bad_auth_usec = pl_mgr->last_bad_auth_usec; 127 | pl_mgr->last_bad_auth_usec = gopt.usec_now; 128 | if (last_bad_auth_usec <= 0) 129 | return -1; // First time we see BAD_AUTH. Disconnect immediately. 130 | if (last_bad_auth_usec + GS_SEC_TO_USEC(GSRN_BAD_AUTH_WINDOW) < gopt.usec_now) 131 | return -1; // No BAD_AUTH for a while. Disconnect immediately. 132 | 133 | DEBUGF_W("Delaying BAD_AUTH (last: %0.03fsec ago)\n", (float)(gopt.usec_now - last_bad_auth_usec) / 1000 / 1000); 134 | // HERE: Rapid LISTEN requests with BAD-AUTH. 135 | // Delay the error and disconnect. 136 | p->evt_bad_auth_delay = evtimer_new(gopt.evb, cb_evt_bad_auth_delay, p); 137 | uint64_t msec = (GSRN_BAD_AUTH_DELAY + random() % GSRN_BAD_AUTH_JITTER) * 1000 + random() % 1000; 138 | DEBUGF("DELAY=%.03f sec\n", (float)msec/1000); 139 | evtimer_add(p->evt_bad_auth_delay, TVMSEC(msec)); 140 | 141 | return 0; 142 | } 143 | 144 | // NOTE: msg might be NULL if called from cb_connect() and the client request to 145 | // go into "listen()" if no server is connected. 146 | static int 147 | gsrn_listen(struct _peer *p, uint8_t *token, struct _gs_listen *msg /* MIGHT BE NULL */) 148 | { 149 | // Adjust timeout 150 | bufferevent_set_timeouts(p->bev, TVSEC(GSRN_MSG_TIMEOUT), NULL); 151 | GSRN_change_state(p, GSRN_STATE_LISTEN); 152 | 153 | // Check if addr is already listening 154 | // The binary tree contains a double-linked list of peers. 155 | // Most of the time there will be just 1 (listening) peer per address 156 | // unless the caller specified GS_LISTEN(n>1). 157 | int ret; 158 | ret = PEER_add(p, PEER_L_LISTENING, token); 159 | if (ret != 0) 160 | { 161 | // BAD AUTH TOKEN (another peer of same addr is already listening) 162 | gstats.n_bad_auth += 1; 163 | GSRN_change_state(p, GSRN_STATE_FINISHED); 164 | if (gsrn_bad_auth_delay(p) == 0) 165 | { 166 | PEER_L_mv(p, PEER_L_BAD_AUTH); 167 | return 0; // Trigger cb_evt_bad_auth_delay() 168 | } 169 | 170 | GSRN_send_status_fatal(p, GS_STATUS_CODE_BAD_AUTH, NULL); 171 | return -1; 172 | } 173 | 174 | if (msg != NULL) { 175 | memcpy(p->gs_id, msg->id, sizeof p->gs_id); 176 | logstream_listen(p, msg); 177 | } 178 | 179 | struct _peer *buddy = PEER_get(p->addr, PEER_L_WAITING, NULL); 180 | if (buddy != NULL) { 181 | // There was a client waiting (-w). Connect immediately. 182 | buddy_up(p /*server*/ , buddy /*client*/); 183 | } else { 184 | if ((msg != NULL) && (msg->flags & GS_FL_PROTO_BUDDY_CHECK)) { 185 | GSRN_send_status_warn(p, GS_STATUS_CODE_BUDDY_NOK, NULL); 186 | // Client wants GSRN to close the connection. 187 | if (msg->flags & GS_FL_PROTO_CONN_CLOSE) 188 | return -1; // Trigger PEER_goodbye() 189 | } 190 | } 191 | 192 | gstats.n_gs_listen += 1; 193 | return 0; 194 | } 195 | 196 | // Set PEER information from _gs_listen/_gs_connect message 197 | static void 198 | peer_set_gs_info(struct _peer *p, struct _gs_hdr_lc *msg) 199 | { 200 | uint128_t addr; 201 | memcpy(&addr, msg->addr, sizeof addr); 202 | addr = be128toh(addr); 203 | 204 | p->addr = addr; 205 | p->version_major = msg->version_major; 206 | p->version_minor = msg->version_minor; 207 | p->gs_proto_flags = msg->flags; 208 | if (p->gs_proto_flags & GS_FL_PROTO_WAIT) 209 | DEBUGF_C("WAITING\n"); 210 | } 211 | 212 | 213 | 214 | #define GS_STR_OBSOLETE_CLIENT "Obsolete client detected." 215 | void 216 | cb_gsrn_listen(struct evbuffer *eb, size_t len, void *arg) 217 | { 218 | struct _peer *p = (struct _peer *)arg; 219 | struct _gs_listen msg; 220 | 221 | evbuffer_remove(eb, &msg, sizeof msg); 222 | peer_set_gs_info(p, &msg.hdr); 223 | // memcpy(&p->token, msg.token, sizeof p->token); 224 | DEBUGF_G("{fd=%d} LISTEN received (addr=0x%s)\n", p->fd, GS_addr128hex(NULL, p->addr)); 225 | 226 | if ((msg.version_major < gd.min_version_major) || ((msg.version_major == gd.min_version_major) && (msg.version_minor < gd.min_version_minor))) 227 | { 228 | GS_LOG_V("[%6u] %32s OBSOLETE CLIENT (listen) %s v%u.%u", p->id, GS_addr128hex(NULL, p->addr), gs_log_in_addr2str(&p->addr_in), p->version_major, p->version_minor); 229 | GSRN_send_status_fatal(p, GS_STATUS_CODE_NEEDUPDATE, GS_STR_OBSOLETE_CLIENT); 230 | goto err; 231 | } 232 | char hex[GS_ID_SIZE * 2 + 1]; 233 | GS_LOG_V("[%6u] %32s LISTEN %s %s v%u.%u", p->id, GS_addr128hex(NULL, p->addr), GS_bin2hex(hex, sizeof hex, msg.id, sizeof msg.id), gs_log_in_addr2str(&p->addr_in), p->version_major, p->version_minor); 234 | 235 | if (gsrn_listen(p, msg.token, &msg) != 0) 236 | goto err; 237 | 238 | // HERE: Can be SUCCESS or delayed BAD-AUTH. 239 | return; 240 | err: 241 | PEER_goodbye(p); 242 | } 243 | 244 | static void 245 | buddy_up(struct _peer *server, struct _peer *client) 246 | { 247 | server->buddy = client; 248 | client->buddy = server; 249 | 250 | server->flags |= FL_PEER_IS_SERVER; 251 | client->flags |= FL_PEER_IS_CLIENT; 252 | 253 | PEER_L_mv(server, PEER_L_WAIT_ACCEPT); 254 | PEER_L_mv(client, PEER_L_WAIT_ACCEPT); 255 | 256 | GSRN_send_start(server, GS_FL_PROTO_START_SERVER); 257 | GSRN_send_start(client, GS_FL_PROTO_START_CLIENT); 258 | 259 | GSRN_change_state(server, GSRN_STATE_BUDDY_UP); 260 | GSRN_change_state(client, GSRN_STATE_BUDDY_UP); 261 | 262 | // Adjust timeout 263 | bufferevent_set_timeouts(server->bev, TVSEC(GSRN_ACCEPT_TIMEOUT), NULL); 264 | bufferevent_set_timeouts(client->bev, TVSEC(GSRN_ACCEPT_TIMEOUT), NULL); 265 | 266 | char s_ipport[32]; 267 | snprintf(s_ipport, sizeof s_ipport, "%s", gs_log_in_addr2str(&server->addr_in)); 268 | 269 | GS_LOG_V("[%6u] %32s CONNECT v%u.%u %s->%s", client->id, GS_addr128hex(NULL, client->addr), client->version_major, client->version_minor, gs_log_in_addr2str(&client->addr_in), s_ipport); 270 | 271 | DEBUGF_G("%c Server-%d fd=%d bev=%p\n", IS_CS(server), server->id, bufferevent_getfd(server->bev), server->bev); 272 | DEBUGF_G("%c Client-%d fd=%d bev=%p\n", IS_CS(client), client->id, bufferevent_getfd(client->bev), client->bev); 273 | client->bps_last_usec = client->state_usec; 274 | server->bps_last_usec = server->state_usec; 275 | 276 | gstats.n_gs_connect += 1; 277 | } 278 | 279 | void 280 | cb_gsrn_connect(struct evbuffer *eb, size_t len, void *arg) 281 | { 282 | struct _peer *p = (struct _peer *)arg; 283 | struct _gs_connect msg; 284 | 285 | evbuffer_remove(eb, &msg, sizeof msg); 286 | peer_set_gs_info(p, &msg.hdr); 287 | 288 | if ((msg.version_major < gd.min_version_major) || ((msg.version_major == gd.min_version_major) && (msg.version_minor < gd.min_version_minor))) 289 | { 290 | GS_LOG_V("[%6u] %32s OBSOLETE CLIENT (connect) %s v%u.%u", p->id, GS_addr128hex(NULL, p->addr), gs_log_in_addr2str(&p->addr_in), p->version_major, p->version_minor); 291 | GSRN_send_status_fatal(p, GS_STATUS_CODE_NEEDUPDATE, GS_STR_OBSOLETE_CLIENT); 292 | goto err; 293 | } 294 | 295 | DEBUGF_Y("msg.flags=%x\n", msg.flags); 296 | // IGNORE any further LISTEN/CONNECT messages 297 | GSRN_change_state(p, GSRN_STATE_CONNECT); 298 | 299 | struct _peer_l_mgr *bmgr; 300 | struct _peer *buddy = PEER_get(p->addr, PEER_L_LISTENING, &bmgr); 301 | if (buddy == NULL) 302 | { 303 | // HERE: No server listening. 304 | if (msg.flags & GS_FL_PROTO_CLIENT_OR_SERVER) 305 | { 306 | // HERE: peer is allowed to become a server 307 | // -A flag. When no server listening. 308 | DEBUGF_G("CONNECT but becoming a listening server instead (-A is set)\n"); 309 | if (gsrn_listen(p, NULL, NULL /*&msg*/) != 0) 310 | goto err; 311 | 312 | // HERE: Can be SUCCESS or delayed BAD-AUTH. 313 | return; 314 | } 315 | 316 | // HERE: Client not allowed to become a server. 317 | if (!(msg.flags & GS_FL_PROTO_WAIT)) // FALSE 318 | { 319 | // Check if a listening server was recently available and 320 | // let this client wait for a bit with the hope of server to 321 | // open another listening connection. 322 | if ((bmgr != NULL) && (evtimer_pending(bmgr->evt_shortwait, NULL))) 323 | { 324 | p->flags |= FL_PEER_IS_SHORTWAIT; 325 | } else { 326 | PEER_conn_refused(p); 327 | return; 328 | } 329 | } 330 | 331 | int ret; 332 | ret = PEER_add(p, PEER_L_WAITING, NULL); 333 | if (ret != 0) 334 | { 335 | GS_LOG_V("[%6u] %32s CON-DENIED %s v%u.%u", p->id, GS_addr128hex(NULL, p->addr), gs_log_in_addr2str(&p->addr_in), p->version_major, p->version_minor); 336 | 337 | GSRN_send_status_fatal(p, GS_STATUS_CODE_CONNDENIED, "Not allowed to connect."); 338 | goto err; 339 | } 340 | 341 | // Waiting clients will send PINGs. 342 | bufferevent_set_timeouts(p->bev, TVSEC(GSRN_MSG_TIMEOUT), NULL); 343 | return; 344 | } 345 | 346 | if (msg.flags & GS_FL_PROTO_BUDDY_CHECK) 347 | { 348 | // HERE: Server is listening but we only want to check. 349 | DEBUGF_Y("FL_PROTO_BUDDY_CHECK is set. Server is ok\n"); 350 | GSRN_send_status_fatal(p, GS_STATUS_CODE_SERVER_OK, NULL); 351 | goto err; 352 | } 353 | 354 | // HERE: Buddy found. Connect them. 355 | buddy_up(buddy /*server*/, p /*client*/); 356 | 357 | DEBUGF_Y("%c connect received\n", IS_CS(p)); 358 | logstream_connect(p, &msg); 359 | 360 | 361 | return; 362 | err: 363 | PEER_goodbye(p); 364 | } 365 | 366 | static void 367 | flush_relay(struct _peer *p) 368 | { 369 | struct evbuffer *evb = bufferevent_get_input(p->bev); 370 | 371 | if (evb == NULL) 372 | return; 373 | 374 | size_t sz = evbuffer_get_length(evb); 375 | DEBUGF("Flushing %zu to %c\n", sz, IS_CS(p->buddy)); 376 | if (sz > 0) 377 | { 378 | PEER_stats_update(p, evb); 379 | bufferevent_write_buffer(p->buddy->bev, evb); 380 | } 381 | } 382 | 383 | void 384 | cb_gsrn_accept(struct evbuffer *eb, size_t len, void *arg) 385 | { 386 | struct _peer *p = (struct _peer *)arg; 387 | struct _gs_accept msg; 388 | 389 | DEBUGF_Y("%c GSRN_ACCEPT\n", IS_CS(p)); 390 | evbuffer_remove(eb, &msg, sizeof msg); 391 | p->flags |= FL_PEER_IS_ACCEPT_RECV; 392 | 393 | // Adjust timeout 394 | bufferevent_set_timeouts(p->bev, TVSEC(GSRN_IDLE_TIMEOUT), NULL); 395 | // IGNORE any further ACCEPT messages 396 | GSRN_change_state(p, GSRN_STATE_ACCEPT); 397 | 398 | struct _peer *buddy = p->buddy; 399 | if (buddy == NULL) 400 | { 401 | DEBUGF_R("ACCEPT but buddy is NULL\n"); 402 | return; // CAN NOT HAPPEN 403 | } 404 | 405 | bufferevent_setcb(p->bev, cb_bev_relay_read, cb_bev_write, cb_bev_status, p); 406 | PKT_free(&p->pkt); // stop processing packets. Might still be more data in input buffer... 407 | PEER_L_mv(p, PEER_L_CONNECTED); 408 | 409 | // PKT_dispatch may have received a GS-ACCEPT (and called this function) but there 410 | // is more data in this peer's in-buffer. Copy it to the buddy's output buffer 411 | flush_relay(p); 412 | // And start reading again (all data will be appended to buddy's out-buffer 413 | // and if that gets to large then it will stop...) 414 | bufferevent_enable(p->bev, EV_READ); 415 | 416 | if (!PEER_IS_ACCEPT_RECEIVED(buddy)) // FALSE 417 | { 418 | PEER_L_mv(buddy, PEER_L_ACCEPTED); 419 | DEBUGF_G("Waiting for ACCEPT from %c.\n", IS_CS(buddy)); 420 | return; 421 | } 422 | 423 | // Enable EV_READ in case it was disabled by cb_bev_read(). This may have happened 424 | // before the GS-ACCEPT was received and while there was still data that needed to 425 | // be send to this peer. 426 | bufferevent_enable(p->buddy->bev, EV_READ); 427 | 428 | DEBUGF_Y("%c CONNECTED fd=%d\n", IS_CS(p), bufferevent_getfd(p->bev)); 429 | } 430 | 431 | void 432 | cb_gsrn_ping(struct evbuffer *eb, size_t len, void *arg) 433 | { 434 | struct _peer *p = (struct _peer *)arg; 435 | struct _gs_ping msg; 436 | 437 | evbuffer_remove(eb, &msg, sizeof msg); 438 | 439 | // DEBUGF_G("%c PING received fd=%d\n", IS_CS(p), p->fd); 440 | GSRN_send_pong(p, &msg.payload[0]); 441 | } 442 | 443 | 444 | void 445 | cb_shutdown_complete(void *arg) 446 | { 447 | struct _peer *p = (struct _peer *)arg; 448 | struct _peer *buddy = p->buddy; 449 | 450 | DEBUGF("[%6u] {fd=%d} %c SHUTDOWN-COMPLETE (IS_SHUT_WR_SENT(buddy)=%s)\n", p->id, p->fd, IS_CS(p), buddy==NULL?"NULL":PEER_IS_SHUT_WR_SENT(buddy)?"true":"false"); 451 | 452 | if (buddy == NULL) 453 | { 454 | PEER_free(p); 455 | return; 456 | } 457 | 458 | if (PEER_IS_SHUT_WR_SENT(buddy)) 459 | { 460 | PEER_free(p); 461 | PEER_free(buddy); 462 | return; 463 | } 464 | 465 | // HERE: buddy has not called sys_shutdown() yet. 466 | // 1. Buddy sent EOF. Peer can still write to buddy 467 | // 2. Write to peer causes SIGPIPE 468 | #ifdef DEBUG 469 | int ret; 470 | int value; 471 | socklen_t len = sizeof (value); 472 | ret = getsockopt(buddy->fd, SOL_SOCKET, SO_ERROR, &value, &len); 473 | #endif 474 | 475 | DEBUGF("[%6u] {fd=%d} %c Waiting for buddy...(half-close).[%6u] {fd=%d} has %zd bytes in output buffer, sockopt=%d, so_error=%d])\n", p->id, p->fd, IS_CS(p), buddy->id, buddy->fd, evbuffer_get_length(bufferevent_get_output(buddy->bev)), ret, value); 476 | } 477 | 478 | void 479 | cb_bev_status(struct bufferevent *bev, short what, void *arg) 480 | { 481 | struct _peer *p = (struct _peer *)arg; 482 | struct _peer *buddy = p->buddy; 483 | 484 | DEBUGF_Y("[%6u] {fd=%d} %c peer=%p bev=%p status event=%d (%s)\n", p->id, p->fd, IS_CS(p), p, bev, what, BEV_strerror(what)); 485 | if (what & BEV_EVENT_CONNECTED) 486 | { 487 | DEBUGF_Y("Connected\n"); 488 | bufferevent_enable(bev, EV_READ); 489 | return; 490 | } 491 | 492 | if (what & BEV_EVENT_TIMEOUT) 493 | { 494 | DEBUGF_C("[%6u] %c ***TIMEOUT***\n", p->id, IS_CS(p)); 495 | // We sent SHUT_WR to this peer but are not receiving any data from this peer. 496 | PEER_free(p); 497 | if (buddy) 498 | PEER_free(buddy); 499 | return; 500 | } 501 | 502 | // This can happen: BEV_EVENT_READING|BEV_EVENT_WRITING|BEV_EVENT_EOF|BEV_EVENT_ERROR 503 | // Can not WRITE anymore. 504 | // if ((what & (BEV_EVENT_WRITING | BEV_EVENT_ERROR)) == (BEV_EVENT_WRITING | BEV_EVENT_ERROR)) { 505 | // Cant recover. This peer is dead. 506 | if (what & BEV_EVENT_ERROR) { 507 | PEER_free(p); 508 | if (buddy) 509 | PEER_shutdown(buddy, cb_shutdown_complete); // calls PEER_free() when done. 510 | return; 511 | } 512 | 513 | // Waiting/Listening peer disconnects (before GS-CONNECT) 514 | if (buddy == NULL) 515 | { 516 | DEBUGF_R("NO BUDDY\n"); 517 | PEER_shutdown(p, cb_shutdown_complete); 518 | return; 519 | } 520 | 521 | if (what & BEV_EVENT_EOF) 522 | { 523 | if (PEER_IS_EOF_RECEIVED(p)) { 524 | DEBUGF_R("EVENT_EOF received 2nd time!\n"); 525 | // FIXME: Odd, EV_READ gets disabled (see below) but libevent invokes this 526 | // twice every once in a while... 527 | // PEER_free(p); // pointer may have gotten freed already. 528 | return; 529 | } 530 | p->flags |= FL_PEER_IS_EOF_RECEIVED; 531 | 532 | // EOF received. Stop reading 533 | DEBUGF_W("%c Stopping EV_READ\n", IS_CS(p)); 534 | bufferevent_disable(p->bev, EV_READ); 535 | 536 | if (!PEER_IS_EOF_RECEIVED(buddy)) 537 | { 538 | DEBUGF_W("[%6u] %c fd=%d Setting write shutdown-idle-timeout\n", p->id, IS_CS(p), p->fd); 539 | // This connection is half-dead. Free if buddy is not sending data...(READ-timeout) 540 | bufferevent_set_timeouts(buddy->bev, TVSEC(GSRN_SHUTDOWN_IDLE_TIMEOUT), NULL); 541 | } 542 | 543 | // Forward SHUT_WR to buddy (We wont send any more data) 544 | // MIGHT FREE BUDDY and PEER. 545 | PEER_shutdown(buddy, cb_shutdown_complete); 546 | 547 | return; 548 | } 549 | 550 | // Any other error-event is bad (disconnect hard) 551 | // Broken Pipe, Connection reset by peer 552 | // GS_LOG("ODD-ERROR: [%6u] %c fd=%d, event=%d", p->id, IS_CS(p), p->fd, what); 553 | PEER_free(p); 554 | if (buddy) 555 | PEER_free(buddy); 556 | } 557 | 558 | void 559 | cb_bev_write(struct bufferevent *bev, void *arg) 560 | { 561 | struct _peer *p = (struct _peer *)arg; 562 | struct _peer *buddy = (struct _peer *)p->buddy; 563 | 564 | // DEBUGF("%c write done peer=%p buddy=%p\n", IS_CS(p), p, buddy); 565 | // All data written. Enable reading again. 566 | 567 | if (PEER_IS_WANT_SEND_SHUT_WR(p)) 568 | { 569 | PEER_shutdown(p, NULL /* already set*/); 570 | return; 571 | } 572 | 573 | if ((buddy != NULL) && PEER_IS_ACCEPT_RECEIVED(p)) 574 | { 575 | bufferevent_enable(buddy->bev, EV_READ); 576 | return; 577 | } 578 | 579 | // Or if GS-ACCEPT has not been received yet then write 580 | // completed and reading from this peer's input should continue 581 | // (until GS-ACCEPT is received). 582 | bufferevent_enable(p->bev, EV_READ); 583 | } 584 | 585 | 586 | static void 587 | cb_bev_relay_read(struct bufferevent *bev, void *arg) 588 | { 589 | struct _peer *p = (struct _peer *)arg; 590 | struct evbuffer *in = bufferevent_get_input(bev); 591 | struct _peer *buddy = p->buddy; 592 | size_t in_sz = evbuffer_get_length(in); 593 | 594 | PEER_stats_update(p, in); 595 | 596 | // DEBUGF("%c in_sz=%zd\n", IS_CS(p), in_sz); 597 | 598 | bufferevent_write_buffer(buddy->bev, in); 599 | 600 | size_t bsz = evbuffer_get_length(bufferevent_get_output(buddy->bev)); 601 | 602 | if (bsz >= MAX(in_sz, 4096) * 4) 603 | { 604 | DEBUGF_R("[%6u] %c Still data in %c's output buffer (%zu). Stop reading..\n", p->id, IS_CS(p), IS_CS(buddy), bsz); 605 | bufferevent_disable(bev, EV_READ); 606 | } 607 | } 608 | 609 | // Read data from peer. May add data to p->bev=>out and may stop reading 610 | // if there is still data to send to itself (e.g. replies). 611 | // Special care needs to be taken when moving to CONNECT state and when 612 | // this peer's bev=>in is written to buddy's bev=>out: Data left here in the out-buffer 613 | // need to be flushed to the buddy. 614 | void 615 | cb_bev_read(struct bufferevent *bev, void *arg) 616 | { 617 | struct _peer *p = (struct _peer *)arg; 618 | 619 | struct evbuffer *in = bufferevent_get_input(bev); 620 | struct evbuffer *out = bufferevent_get_output(bev); 621 | size_t out_sz = evbuffer_get_length(out); 622 | 623 | if (out_sz > 0) 624 | { 625 | // HERE: Only happens when PKT_dispatch() adds a message to the out-buffer 626 | // and it hasnt been send yet to _this_ peer (not the buddy). 627 | DEBUGF_R("%c Still data in output buffer (%zu). Stop reading..\n", IS_CS(p), out_sz); 628 | bufferevent_disable(bev, EV_READ); 629 | } 630 | 631 | gopt.usec_now = GS_usec(); 632 | // Dispatch protocol message 633 | PKT_dispatch(&p->pkt, in); 634 | // May have enabled EV_READ (if a gs-accept was received). 635 | // HERE: PKT_dispatch() may have added data to _this_ peer's out-buffer 636 | // and may have enable EV_READ (for example when buddy got connected and all 637 | // further in-data should be send to the peer's buddy out-buffer). 638 | 639 | // DEBUGF("Input buffer size=%zu after PKT_dispatch()\n", evbuffer_get_length(in)); 640 | // DEBUGF("Output Buffer size=%zu after PKT_dispatch()\n", evbuffer_get_length(out)); 641 | } 642 | 643 | // Assign fd to bio and create peer and events for this peer. 644 | static int 645 | accept_ssl(int ls, SSL *ssl) 646 | { 647 | int sox; 648 | 649 | sox = fd_net_accept(ls); 650 | if (sox < 0) 651 | { 652 | GS_LOG_V("[%6u] ERROR: accept(%d)=%s", ls, strerror(errno)); 653 | // Likely ran out of FD's. We should not exit 654 | // but instead increase FD limit, accept fd, disconnect FD 655 | // and the set FD limit again. 656 | fd_limit_unlimited(); 657 | sox = fd_net_accept(ls); 658 | fd_limit_limited(); 659 | 660 | // Catch-all total fuckup: wait... 661 | if (sox < 0) 662 | usleep(100 * 1000); 663 | goto err; // Will close() the accepted socket. 664 | } 665 | 666 | // Create peer 667 | struct _peer *p; 668 | p = PEER_new(sox, ssl); 669 | if (p == NULL) 670 | goto err; 671 | 672 | return 0; 673 | err: 674 | DEBUGF_R("ERROR %s ls=%d, sox=%d\n", strerror(errno), ls, sox); 675 | XCLOSE(sox); 676 | return -1; 677 | } 678 | 679 | void 680 | cb_accept(int ls, short ev, void *arg) 681 | { 682 | // struct event *ev = (struct event *)arg; 683 | // BIO *bio = BIO_new(BIO_s_socket()); 684 | 685 | if (accept_ssl(ls, NULL) != 0) 686 | goto err; 687 | 688 | return; 689 | err: 690 | return; 691 | } 692 | 693 | void 694 | cb_accept_ssl(int ls, short ev, void *arg) 695 | { 696 | // BIO *bio = BIO_new(BIO_f_ssl()); // alternative 697 | // SSL *ssl = SSL_new(gopt.ssl_ctx); // alternative 698 | // BIO_set_ssl(bio, ssl, BIO_NOCLOSE); // alternative 699 | // BIO_set_ssl_mode(bio, 0 /*server*/); // alternative 700 | // BIO *bio = BIO_new_ssl(gopt.ssl_ctx, 0 /*server*/); 701 | 702 | SSL *ssl = SSL_new(gopt.ssl_ctx); 703 | if (accept_ssl(ls, ssl) != 0) 704 | goto err; 705 | 706 | return; 707 | err: 708 | SSL_free(ssl); 709 | // XBIO_FREE(bio); 710 | } 711 | 712 | // Accept TCP for CNC 713 | void 714 | cb_accept_cnc(int fd, short ev, void *arg) 715 | { 716 | accept_ssl(fd, NULL); 717 | // FIXME: pass which msg dispatcher function should be called for message types. 718 | 719 | // FIXME: add event for reading and writing. 720 | } 721 | 722 | -------------------------------------------------------------------------------- /src/peer.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "gsrnd.h" 3 | #include "engine.h" 4 | #include "packet.h" 5 | #include "utils.h" 6 | #include "protocol.h" 7 | #include "peer.h" 8 | #include "gopt.h" 9 | 10 | 11 | static void cb_evt_linger(int fd_notused, short event, void *arg); 12 | static void cb_evt_shortwait(int fd_notused, short event, void *arg); 13 | static void peer_stats_update_bps(struct _peer *p); 14 | 15 | #ifdef DEBUG 16 | static void 17 | tree_stats(void) 18 | { 19 | #if 0 20 | int i; 21 | DEBUGF("Tree Stats (%d nodes)\n", gopt.t_peers.n_nodes); 22 | for (i = 0; i < MAX_LISTS_BY_ADDR; i++) 23 | { 24 | DEBUGF("LIST-ID=#%d(%s) Entries=%d Unique=%d\n", i, PEER_L_name(i), gopt.t_peers.n_entries[i], gopt.t_peers.n_uniq[i]); 25 | } 26 | #endif 27 | } 28 | 29 | static void 30 | cb_peers_printall(struct _peer *p, struct _peer_l_root *plr, void *arg) 31 | { 32 | if (p == NULL) 33 | { 34 | if (plr == NULL) 35 | return; 36 | 37 | DEBUGF("List-#%ld(%s) (%d entries):\n", PLR_L_get_id(plr), PEER_L_name(PLR_L_get_id(plr)), plr->n_entries); 38 | return; 39 | } 40 | 41 | DEBUGF(" [%u] %c addr=%s\n", p->id, IS_CS(p), strx128x(p->addr)); 42 | } 43 | #endif 44 | 45 | static struct _peer_l_mgr * 46 | t_peer_get_mgr(const void *nodep, const VISIT which) 47 | { 48 | if ((which == preorder) || (which == endorder)) 49 | return NULL; 50 | 51 | if (nodep == NULL) 52 | return NULL; 53 | 54 | return *(struct _peer_l_mgr **)nodep; 55 | } 56 | 57 | void 58 | cb_t_peers_walk(const void *nodep, const VISIT which, const int depth) 59 | { 60 | struct _peer_l_mgr *pl_mgr = t_peer_get_mgr(nodep, which); 61 | if (pl_mgr == NULL) 62 | return; 63 | 64 | int i; 65 | for (i = 0; i < MAX_LISTS_BY_ADDR; i++) 66 | { 67 | // Call *func for each new list but with peer set to NULL. 68 | (*gopt.t_peers.walk_peers_func)(NULL, &pl_mgr->plr[i], gopt.t_peers.walk_peers_func_arg); 69 | struct _peer *p; 70 | struct _peer *temp_p = NULL; 71 | TAILQ_FOREACH_SAFE(p, &pl_mgr->plr[i].head, ll, temp_p) 72 | (*gopt.t_peers.walk_peers_func)(p, &pl_mgr->plr[i], gopt.t_peers.walk_peers_func_arg); 73 | } 74 | } 75 | 76 | // Call *func(peer, plr, arg) for each peer in list. 77 | // *func is called with peer set to NULL for each list and then for each list 78 | // entry peer is set to the peer in that list. This allows *func to be called 79 | // even if the list is empty. 80 | // A good example how to use PEER_walk() is in "cb_peers_printall" or "cb_cli_list". 81 | void 82 | PEERS_walk(walk_peers_func_t func, void *arg) 83 | { 84 | gopt.t_peers.walk_peers_func = func; 85 | gopt.t_peers.walk_peers_func_arg = arg; 86 | 87 | twalk(gopt.t_peers.tree, cb_t_peers_walk); 88 | } 89 | 90 | 91 | // Each leaf is a double linked list of peer entries. 92 | int 93 | cb_t_peer_by_addr(const void *a, const void *b) 94 | { 95 | struct _peer_l_mgr *pla = (struct _peer_l_mgr *)a; 96 | struct _peer_l_mgr *plb = (struct _peer_l_mgr *)b; 97 | 98 | if (pla->addr < plb->addr) 99 | return -1; 100 | 101 | if (pla->addr > plb->addr) 102 | return 1; // tree right 103 | 104 | return 0; // (found/exists) 105 | } 106 | 107 | // A little hack to make insertion faster when list 108 | // does not exists. 109 | int 110 | cb_t_peer_find(const void *needle_a, const void *stack_b) 111 | { 112 | struct _peer_l_mgr *pl_mgr_b = (struct _peer_l_mgr *)stack_b; 113 | uint128_t *addr_a = (uint128_t *)needle_a; 114 | 115 | // DEBUGF_C("looking for %s @ %p\n", strx128x(*addr_a), stack_b); 116 | 117 | if (*addr_a < pl_mgr_b->addr) 118 | return -1; 119 | 120 | if (*addr_a > pl_mgr_b->addr) 121 | return 1; // tree right 122 | 123 | return 0; // (found/exists) 124 | 125 | } 126 | 127 | static int 128 | pl_link(struct _peer_l_mgr *pl_mgr, struct _peer *p, peer_l_id_t pl_id, uint8_t *token) 129 | { 130 | static uint8_t token_zero[GS_TOKEN_SIZE]; // ZERO 131 | struct _peer_l_root *plr = &pl_mgr->plr[pl_id]; 132 | 133 | if (pl_id == PEER_L_LISTENING) 134 | { 135 | // DEBUGF_C("[%6u] LISTEN TOKEN=%s\n", p->id, GS_token2hex(NULL, token)); 136 | if (pl_mgr->flags & FL_PL_IS_TOKEN_SET) 137 | { 138 | // HERE: Token is set. Check token from peer. 139 | if ((token == NULL) || (memcmp(pl_mgr->token, token, sizeof pl_mgr->token) != 0)) 140 | { 141 | char hextoken[GS_TOKEN_SIZE * 2 + 1]; 142 | GS_LOG_V("[%6u] %32s "CDR"BAD AUTH TOKEN"CN" (%s, was %s)", p->id, GS_addr128hex(NULL, p->addr), GS_token2hex(NULL, token), GS_token2hex(hextoken, pl_mgr->token)); 143 | return -1; // Bad Token 144 | } 145 | evtimer_del(pl_mgr->evt_linger); 146 | } else { 147 | // HERE: Token is not set for this manager 148 | if ((token != NULL) && (memcmp(token, token_zero, sizeof token_zero) != 0)) 149 | { 150 | // Peer's token is set (not 0x000...00). Save it. 151 | pl_mgr->flags |= FL_PL_IS_TOKEN_SET; 152 | memcpy(pl_mgr->token, token, sizeof pl_mgr->token); 153 | if (pl_mgr->evt_linger == NULL) 154 | pl_mgr->evt_linger = evtimer_new(gopt.evb, cb_evt_linger, pl_mgr); 155 | } 156 | } 157 | if (pl_mgr->evt_shortwait != NULL) 158 | evtimer_del(pl_mgr->evt_shortwait); 159 | } 160 | 161 | // First time we add something to this list... 162 | if (TAILQ_EMPTY(&plr->head)) 163 | gopt.t_peers.n_uniq[pl_id] += 1; 164 | 165 | TAILQ_INSERT_HEAD(&plr->head, p, ll); 166 | plr->n_entries += 1; 167 | pl_mgr->n_entries += 1; 168 | gopt.t_peers.n_entries[pl_id] += 1; 169 | 170 | p->plr = plr; 171 | 172 | p->state_usec = GS_usec(); 173 | 174 | return 0; 175 | } 176 | 177 | 178 | struct _peer_l_mgr * 179 | PEER_get_mgr(uint128_t addr) 180 | { 181 | void *vptr; 182 | 183 | vptr = tfind(&addr, &gopt.t_peers.tree, cb_t_peer_find); 184 | if (vptr == NULL) 185 | return NULL; 186 | 187 | return *(struct _peer_l_mgr **)vptr; 188 | } 189 | 190 | void 191 | PEER_by_addr(uint128_t addr, peer_func_t cb_peer, void *arg) 192 | { 193 | struct _peer_l_mgr *pl_mgr; 194 | 195 | pl_mgr = PEER_get_mgr(addr); 196 | if (pl_mgr == NULL) 197 | return; 198 | 199 | // For each peer under this address call the callback.... 200 | int i; 201 | int n; 202 | for (i = 0; i < MAX_LISTS_BY_ADDR; i++) 203 | { 204 | // The callback (cb_peer) might call PEER_free() which may in turn 205 | // free the tree node (pl_mgr). On return from cb_peer the tree node (pl_mgr) 206 | // might thus no longer be valid. 207 | // Solved by: Check n_entries and break the loop if only 1 entry was left 208 | // and dont touch pl_mgr thereafter (it might unallocated memory). 209 | struct _peer_l_root *plr = &pl_mgr->plr[i]; 210 | if (TAILQ_EMPTY(&plr->head)) 211 | continue; 212 | 213 | struct _peer *p; 214 | struct _peer *temp_p = NULL; 215 | TAILQ_FOREACH_SAFE(p, &plr->head, ll, temp_p) 216 | { 217 | n = pl_mgr->n_entries; 218 | (*cb_peer)(p, arg); 219 | // WARNING: pl_mgr might have been freed. 220 | if (n <= 1) 221 | break; // This was the last entry 222 | } 223 | if (n <= 1) 224 | break; 225 | } 226 | } 227 | 228 | static struct _peer_l_root * 229 | peer_find_root(uint128_t addr, peer_l_id_t pl_id, struct _peer_l_mgr **pl_mgr_ptr) 230 | { 231 | struct _peer_l_mgr *pl_mgr; 232 | 233 | pl_mgr = PEER_get_mgr(addr); 234 | if (pl_mgr_ptr != NULL) 235 | *pl_mgr_ptr = pl_mgr; // NULL or pl_mgr 236 | 237 | if (pl_mgr == NULL) 238 | return NULL; 239 | 240 | struct _peer_l_root *plr; 241 | plr = &pl_mgr->plr[pl_id]; 242 | if (TAILQ_EMPTY(&plr->head)) 243 | return NULL; 244 | 245 | return plr; 246 | } 247 | 248 | // Add a Peer by peer->addr to a double-linked list which is 249 | // linked to a binary tree 250 | int 251 | PEER_add(struct _peer *p, peer_l_id_t pl_id, uint8_t *token) 252 | { 253 | struct _peer_l_mgr *pl_mgr = NULL; 254 | 255 | XASSERT(p->plr == NULL, "Oops. Peer already in a linked list\n"); 256 | pl_mgr = PEER_get_mgr(p->addr); 257 | 258 | if (pl_mgr == NULL) 259 | { 260 | // HERE: New leaf in binary tree 261 | DEBUGF("{fd=%d} Creating a new linked list. (list-mgr=%p, list=%d) tree=%p\n", p->fd, pl_mgr, pl_id, gopt.t_peers.tree); 262 | 263 | pl_mgr = calloc(1, sizeof (struct _peer_l_mgr)); 264 | pl_mgr->addr = p->addr; 265 | pl_mgr->evt_shortwait = evtimer_new(gopt.evb, cb_evt_shortwait, pl_mgr); 266 | 267 | int i; 268 | for (i = 0; i < MAX_LISTS_BY_ADDR; i++) 269 | { 270 | TAILQ_INIT(&pl_mgr->plr[i].head); 271 | pl_mgr->plr[i].pl_mgr = pl_mgr; 272 | } 273 | 274 | tsearch(pl_mgr, &gopt.t_peers.tree, cb_t_peer_by_addr); // Add to binary tree 275 | gopt.t_peers.n_nodes += 1; 276 | } else { 277 | // HERE: Existing linked list (peer with same addr already exists) 278 | struct _peer_l_root *plr = &pl_mgr->plr[pl_id]; 279 | DEBUGF_W("{fd=%d} Using existing list-mgr=%p. Already has %d entries, [pl_id=%d] has %d\n", p->fd, pl_mgr, pl_mgr->n_entries, pl_id, plr->n_entries); 280 | } 281 | int ret; 282 | ret = pl_link(pl_mgr, p, pl_id, token); 283 | if (ret != 0) 284 | return ret; // Bad Token; 285 | 286 | return 0; 287 | } 288 | 289 | // Unlink from list (but do not remove binary tree entry 290 | static void 291 | pl_unlink(struct _peer *p) 292 | { 293 | peer_l_id_t pl_id = PEER_L_get_id(p); 294 | struct _peer_l_mgr *pl_mgr = PEER_L_get_mgr(p); 295 | p->plr->n_entries -= 1; 296 | TAILQ_REMOVE(&p->plr->head, p, ll); 297 | 298 | if (TAILQ_EMPTY(&p->plr->head)) 299 | { 300 | gopt.t_peers.n_uniq[pl_id] -= 1; 301 | if (pl_id == PEER_L_LISTENING) 302 | { 303 | // Give listening gsocket time to re-connect and delay client 304 | // disconnects. We start the timer when the _last_ listening 305 | // server is no longer listening (e.g disconnects or is moved into 306 | // another state). Meanwhile if any clients connect while no 307 | // server is listening and withitn SHORTWAIT_TIMEOUT then we put 308 | // those clients into a short-wait holding pattern before disconnecting them. 309 | evtimer_add(pl_mgr->evt_shortwait, TVSEC(GSRN_SHORTWAIT_TIMEOUT)); 310 | 311 | // This was the last listening server. Prevent others from impersonating 312 | // this server for a while and allow the original server to connect again 313 | // to register another listening gsocket. 314 | if (pl_mgr->flags & FL_PL_IS_TOKEN_SET) 315 | { 316 | DEBUGF_C("Last listening peer. Token was set...\n"); 317 | evtimer_add(pl_mgr->evt_linger, TVSEC(GSRN_TOKEN_LINGER_SEC)); 318 | } 319 | } 320 | } 321 | 322 | pl_mgr->n_entries -= 1; 323 | 324 | gopt.t_peers.n_entries[pl_id] -= 1; 325 | 326 | } 327 | 328 | // Move from 1 list to another or add if currently in no list. 329 | void 330 | PEER_L_mv(struct _peer *p, peer_l_id_t pl_id) 331 | { 332 | if (p->plr == NULL) 333 | { 334 | DEBUGF_R("WARN: Peer is not in any list yet. Adding to %d...\n", pl_id); 335 | PEER_add(p, pl_id, NULL); 336 | return; 337 | } 338 | 339 | // Check if already in correct list. 340 | if (PEER_L_get_id(p) == pl_id) 341 | return; 342 | 343 | // Remove from current list. 344 | pl_unlink(p); 345 | 346 | // Add to new list 347 | pl_link(p->plr->pl_mgr, p, pl_id, NULL); 348 | } 349 | 350 | // Return oldest listening peer. 351 | struct _peer * 352 | PEER_get(uint128_t addr, peer_l_id_t pl_id, struct _peer_l_mgr **pl_mgr_ptr) 353 | { 354 | struct _peer_l_root *plr; 355 | 356 | plr = peer_find_root(addr, pl_id, pl_mgr_ptr); 357 | if (plr == NULL) 358 | return NULL; 359 | 360 | return (struct _peer *)TAILQ_LAST(&plr->head, _listhead); // Oldest from the tailq 361 | } 362 | 363 | static void 364 | pl_mgr_t_free(struct _peer_l_mgr *pl_mgr) 365 | { 366 | if (pl_mgr->n_entries > 0) 367 | DEBUGF_R("WARN: tree-node still has %d entries\n", pl_mgr->n_entries); 368 | 369 | XEVT_FREE(pl_mgr->evt_linger); 370 | XEVT_FREE(pl_mgr->evt_shortwait); 371 | 372 | tdelete(pl_mgr, &gopt.t_peers.tree, cb_t_peer_by_addr); 373 | gopt.t_peers.n_nodes -= 1; 374 | free(pl_mgr); 375 | } 376 | 377 | // The MGR for the gsocket addr had no listening gsockets and no new 378 | // listening gsockets were created within the timeout period. 379 | // => Remove the MGR. This will allow clients with different tokens 380 | // to create a listening gsocket using the same address (e.g. impersonate 381 | // the old listening server). 382 | static void 383 | cb_evt_linger(int fd_notused, short event, void *arg) 384 | { 385 | struct _peer_l_mgr *pl_mgr = (struct _peer_l_mgr *)arg; 386 | 387 | DEBUGF_C("addr=%s Timeout(%d sec). No listening socket created. Deleting token.\n", strx128x(pl_mgr->addr), GSRN_TOKEN_LINGER_SEC); 388 | // Allow others to use the this addr to create listening gsockets 389 | pl_mgr->flags &= ~FL_PL_IS_TOKEN_SET; 390 | 391 | if (pl_mgr->n_entries <= 0) 392 | { 393 | pl_mgr_t_free(pl_mgr); 394 | pl_mgr = NULL; 395 | return; 396 | } 397 | 398 | // Check if there are ANY in BAD-AUTH state and if so then disconnect the first one. 399 | // This will allow a system that tries to connect with the same SECRET 400 | // but multiple instances of gs-netcat to re-connect with one gs-netcat 401 | // immediately. Trigger p->evt_bad_auth_delay on any peer that has this != NULL 402 | struct _peer_l_root *plr = &pl_mgr->plr[PEER_L_BAD_AUTH]; 403 | if (TAILQ_EMPTY(&plr->head)) 404 | return; 405 | 406 | struct _peer *p = (struct _peer *)TAILQ_FIRST(&plr->head); 407 | if (p == NULL) 408 | { 409 | DEBUGF_C("No peers in BAD-AUTH waiting...\n"); 410 | return; 411 | } 412 | 413 | if (p->evt_bad_auth_delay == NULL) 414 | { 415 | GS_LOG("ODD-ERROR: [%6u] %c fd=%d. auth_delay is NULL", p->id, IS_CS(p), p->fd); 416 | return; 417 | } 418 | 419 | DEBUGF_C("[%6u] %c {fd=%d} Sending CLOSE to _one_ peer waiting in BAD-AUTH queue (total in BAD_AUTH: %d)\n", p->id, IS_CS(p), p->fd, plr->n_entries); 420 | // FIXME: Could we move it from BAD-AUTH to listen instead? 421 | GSRN_send_status_fatal(p, GS_STATUS_CODE_BAD_AUTH, NULL); 422 | PEER_goodbye(p); 423 | } 424 | 425 | void 426 | PEER_conn_refused(struct _peer *p) 427 | { 428 | gstats.n_gs_refused += 1; 429 | GS_LOG_V("[%6u] %32s CON-REFUSED %s v%u.%u", p->id, GS_addr128hex(NULL, p->addr), gs_log_in_addr2str(&p->addr_in), p->version_major, p->version_minor); 430 | GSRN_send_status_fatal(p, GS_STATUS_CODE_CONNREFUSED, NULL); 431 | PEER_goodbye(p); 432 | } 433 | 434 | static void 435 | cb_evt_shortwait(int fd_notused, short event, void *arg) 436 | { 437 | struct _peer_l_mgr *pl_mgr = (struct _peer_l_mgr *)arg; 438 | struct _peer_l_root *plr = &pl_mgr->plr[PEER_L_WAITING]; 439 | 440 | if (plr == NULL) 441 | return; 442 | 443 | if (plr->n_entries <= 0) 444 | return; 445 | 446 | DEBUGF_W("addr=%s Timeout(%d sec). No server connected. %d clients in PEER_L_WAITING.\n", strx128x(pl_mgr->addr), GSRN_SHORTWAIT_TIMEOUT, plr->n_entries); 447 | 448 | struct _peer *p; 449 | struct _peer *temp_p = NULL; 450 | TAILQ_FOREACH_SAFE(p, &plr->head, ll, temp_p) 451 | { 452 | if (!(PEER_IS_SHORTWAIT(p))) 453 | continue; 454 | DEBUGF_W(" [%u] Disconnecting\n", p->id); 455 | PEER_conn_refused(p); // WARN: Might free(p) 456 | } 457 | } 458 | 459 | // Remove peer from double linked list and if this is the last peer then also 460 | // remove the list-root from the binary tree. 461 | static void 462 | peer_t_del(struct _peer *p) 463 | { 464 | if ((p == NULL) || (p->plr == NULL)) 465 | return; // Not in any list 466 | 467 | pl_unlink(p); 468 | struct _peer_l_mgr *pl_mgr = PEER_L_get_mgr(p); 469 | 470 | if ((pl_mgr->evt_linger != NULL) && (evtimer_pending(pl_mgr->evt_linger, NULL))) 471 | goto done; 472 | 473 | if (pl_mgr->n_entries <= 0) 474 | { 475 | DEBUGF_C("list=%ld This was the last peer with addr=%s\n", PEER_L_get_id(p), strx128x(p->addr)); 476 | pl_mgr_t_free(pl_mgr); 477 | pl_mgr = NULL; 478 | } 479 | 480 | done: 481 | p->plr = NULL; 482 | } 483 | 484 | static void 485 | peer_sys_shutdown(struct _peer *p) 486 | { 487 | if (p == NULL) 488 | goto err; 489 | if (p->shutdown_complete_func == NULL) 490 | goto err; 491 | DEBUGF("sys.shutdown(%d, SHUT_WR)\n", p->fd); 492 | shutdown(p->fd, SHUT_WR); 493 | p->flags &= ~FL_PEER_IS_WANT_SEND_SHUT_WR; 494 | p->flags |= FL_PEER_IS_SHUT_WR_SENT; 495 | // Always calls cb_shutdown_complete() 496 | (*p->shutdown_complete_func)(p); 497 | return; 498 | err: 499 | DEBUGF_R("CANT HAPPEN (peer=%p)\n", p); 500 | } 501 | 502 | static int 503 | tioc(struct _peer *p) 504 | { 505 | int ret; 506 | int value = 0; 507 | 508 | ret = ioctl(p->fd, TIOCOUTQ, &value); 509 | if (ret != 0) 510 | return -1; 511 | 512 | p->tioc_count += 1; 513 | DEBUGF("[%6u] %c fd=%d count=%6d tioc=%d eof=%d\n", p->id, IS_CS(p), p->fd, p->tioc_count, value, PEER_IS_EOF_RECEIVED(p)?1:0); 514 | 515 | // No data in kernel's output buffer. 516 | if (value == 0) 517 | return 0; 518 | 519 | // Linux (tested on localhost): 520 | // 1. Peer app exits & sends TCP-FIN to gsrnd. 521 | // 2. GSRND still has data for peer in TIOC-OUT-Q 522 | // 3. GSRND sends data to Peer. 523 | // 4. Peer sends TCP-RST to GSRND. 524 | // => GSRND wont get this error because Q never empties and never calls read() 525 | // on the socket. 526 | // The only solution is to poll on getsockopt() to see if an error occured. 527 | socklen_t len = sizeof (value); 528 | ret = getsockopt (p->fd, SOL_SOCKET, SO_ERROR, &value, &len); 529 | if ((ret != 0) || (value == EPIPE)) 530 | { 531 | DEBUGF_R("[%6u] %c fd=%d getsockopt()=%d err=%d (%s)\n", p->id, IS_CS(p), p->fd, ret, value, strerror(value)); 532 | return -1; 533 | } 534 | 535 | return value; 536 | } 537 | 538 | static void 539 | cb_evt_tioc(int fd, short what, void *arg) 540 | { 541 | struct _peer *p = (struct _peer *)arg; 542 | 543 | if (tioc(p) > 0) 544 | { 545 | if (p->tioc_delay_ms < 100) 546 | p->tioc_delay_ms += 20; 547 | 548 | // Wait again... 549 | evtimer_add(p->evt_tioc, TVMSEC(p->tioc_delay_ms)); 550 | return; 551 | } 552 | 553 | // XEVT_FREE(p->evt_tioc); // Not needed. FIXME-2024 554 | peer_sys_shutdown(p); 555 | } 556 | 557 | static void 558 | cb_evt_shutdown_timeout(int fd, short what, void *arg) 559 | { 560 | struct _peer *p = (struct _peer *)arg; 561 | DEBUGF_R("SHUTDOWN-TIMEOUT. Forcing shutdown()\n"); 562 | peer_sys_shutdown(p); 563 | } 564 | 565 | // Call shutdown() after all pending data (bev & kernel queue) has been send. 566 | // 1. Wait for bev buffer to empty 567 | // 2. For for kernel buffer to empty. 568 | // 3. Call shutdown() & call func 569 | void 570 | PEER_shutdown(struct _peer *p, shutdown_complete_func_t func) 571 | { 572 | // func is only ever NULL when EV_WRITE is triggered and there is no 573 | // data in the output buffer. 574 | if (func != NULL) 575 | { 576 | p->shutdown_complete_func = func; 577 | 578 | size_t out_sz = evbuffer_get_length(bufferevent_get_output(p->bev)); 579 | if (out_sz > 0) 580 | { 581 | DEBUGF_R("[%6u] DELAYING SHUT_WR fd=%d (%c) because %zd left in output buffer\n", p->id, p->fd, IS_CS(p), out_sz); 582 | // Make cb_bev_write() call PEER_shutdown(p) when data got written. 583 | p->flags |= FL_PEER_IS_WANT_SEND_SHUT_WR; 584 | // Set a timeout if we fail to write all data. 585 | if (p->evt_shutdown_timeout == NULL) 586 | p->evt_shutdown_timeout = evtimer_new(gopt.evb, cb_evt_shutdown_timeout, p); 587 | evtimer_add(p->evt_shutdown_timeout, TVSEC(GSRN_SHUTDOWN_IDLE_TIMEOUT * 4) /*seconds*/); 588 | return; 589 | } 590 | } 591 | 592 | // HERE: All data from bufferevent has been send. 593 | 594 | if (p->evt_tioc != NULL) 595 | { 596 | DEBUGF_R("[%6u] %c event timer TIOC already running (func=%p) fd=%d!\n", p->id, IS_CS(p), func, p->fd); 597 | return; 598 | } 599 | 600 | if (tioc(p) > 0) 601 | { 602 | // create timer to check kernel buffer again... 603 | p->evt_tioc = evtimer_new(gopt.evb, cb_evt_tioc, p); 604 | evtimer_add(p->evt_tioc, TVMSEC(10) /*milli-seconds*/); 605 | 606 | // Kernel buffer may never free (for example when peer has stopped reading). 607 | // Thus we force call to 'shutdown()' even if there is data left in 608 | // the output kernel buffer. 609 | if (p->evt_shutdown_timeout == NULL) 610 | p->evt_shutdown_timeout = evtimer_new(gopt.evb, cb_evt_shutdown_timeout, p); 611 | evtimer_add(p->evt_shutdown_timeout, TVSEC(3) /*seconds*/); 612 | 613 | return; 614 | } 615 | 616 | peer_sys_shutdown(p); 617 | } 618 | 619 | // When there are no pending data in the 'out' buffer then free the peer now. 620 | // Otherwise give I/O time to send the data to the peer and free peer when 621 | // a. all data has been written (in cb_bev_write) 622 | // b. the write timeout has expired (in cb_bev_status) 623 | void 624 | PEER_goodbye(struct _peer *p) 625 | { 626 | DEBUGF_G("[%6u] {fd=%d} %c PEER-GOODBYE\n", p->id, p->fd, IS_CS(p)); 627 | // Remove myself from lists. No longer available to any GSRN services (e.g. gs-connect()) 628 | peer_t_del(p); 629 | 630 | PEER_shutdown(p, cb_shutdown_complete); 631 | 632 | PKT_set_void(&p->pkt); 633 | } 634 | 635 | // Free a peer 636 | void 637 | PEER_free(struct _peer *p) 638 | { 639 | DEBUGF_G("[%6u] %c %s peer=%p, bev=%p\n", p->id, IS_CS(p), __func__, p, p->bev); 640 | 641 | if (PEER_IS_CLIENT(p)) 642 | { 643 | char since[GS_SINCE_MAXSIZE]; 644 | GS_format_since(since, sizeof since, GS_USEC_TO_SEC(gopt.usec_now - p->state_usec)); 645 | char traffic[GS_BPS_MAXSIZE]; 646 | GS_format_bps(traffic, sizeof traffic, p->in_n + p->out_n, NULL); 647 | 648 | peer_stats_update_bps(p); 649 | char bps_max[GS_BPS_MAXSIZE + 2]; // + /s 650 | GS_format_bps(bps_max, sizeof bps_max, p->bps_max, "/s"); 651 | 652 | GS_LOG_V("[%6u] %32s DISCON %s %*s %s %s", p->id, GS_addr128hex(NULL, p->addr), gs_log_in_addr2str(&p->addr_in), GS_SINCE_MAXSIZE -1, since, traffic, bps_max); 653 | } 654 | 655 | #ifdef DEBUG 656 | tree_stats(); 657 | // PEERS_walk(cb_peers_printall, NULL); 658 | #endif 659 | 660 | // Remove myself from lists 661 | peer_t_del(p); 662 | 663 | // size_t sz = p->out_pending; 664 | size_t sz = evbuffer_get_length(bufferevent_get_output(p->bev)); 665 | if (sz > 0) 666 | { 667 | DEBUGF_R("WARN: Free'ing peer with %zu left in output buffer\n", sz); 668 | // size_t hlen = MIN(1024, sz); 669 | // char buf[hlen]; 670 | // ssize_t rsz = evbuffer_copyout(bufferevent_get_output(p->bev), buf, hlen); 671 | // HEXDUMP(buf, hlen); 672 | // DEBUGF_R("rsz=%zd\n", rsz); 673 | } 674 | if (p->bev) 675 | { 676 | bufferevent_disable(p->bev, EV_READ); 677 | bufferevent_disable(p->bev, EV_WRITE); 678 | } 679 | 680 | XEVT_FREE(p->evt_shutdown_timeout); 681 | XEVT_FREE(p->evt_tioc); 682 | XEVT_FREE(p->evt_bad_auth_delay); 683 | XBEV_FREE(p->bev); 684 | 685 | // Unlink myself from my buddy 686 | if (p->buddy != NULL) 687 | { 688 | p->buddy->buddy = NULL; // unlink myself from my buddy. 689 | } 690 | 691 | XCLOSE(p->fd); 692 | XFREE(p); 693 | } 694 | 695 | 696 | #define GSRN_BPS_WINDOW GS_SEC_TO_USEC(10) 697 | 698 | // Return an estimation of Bytes/Second that is not to jumpy. 699 | // The 'old' recorded value (before current 10 seconds) 700 | // Bytes are the number of bytes transfered within current period of length usec. 701 | static uint32_t 702 | bps_calc(uint32_t old, uint64_t bytes, uint64_t usec) 703 | { 704 | uint32_t bps = 0; 705 | 706 | if (usec > 0) 707 | bps = (bytes * 1000000) / usec; 708 | 709 | if ((usec >= GSRN_BPS_WINDOW) || (old == 0)) 710 | return bps; 711 | 712 | // Calculate how much the current bytes / usec are relevant. 713 | // For example after 0.1 seconds they only have 1% relevance 714 | // (assuming a 10 second measuement interval) but after 9 seconds 715 | // they are 90% relevant! 716 | return old * (GS_SEC_TO_USEC(10) - usec) / GS_SEC_TO_USEC(10) + (bps * usec / GS_SEC_TO_USEC(10)); 717 | } 718 | 719 | uint32_t 720 | PEER_get_bps(struct _peer *p) 721 | { 722 | return bps_calc(p->bps_last, (p->in_n + p->out_n) - p->bps_last_inout, gopt.usec_now - p->bps_last_usec); 723 | } 724 | 725 | static void 726 | peer_stats_update_bps(struct _peer *p) 727 | { 728 | p->bps_last = bps_calc(p->bps_last, (p->in_n + p->out_n) - p->bps_last_inout, gopt.usec_now - p->bps_last_usec); 729 | p->bps_max = MAX(p->bps_max, p->bps_last); // record fastest bps 730 | p->bps_last_usec = gopt.usec_now; 731 | p->bps_last_inout = p->in_n + p->out_n; 732 | } 733 | 734 | void 735 | PEER_stats_update(struct _peer *p, struct evbuffer *eb) 736 | { 737 | size_t len = evbuffer_get_length(eb); 738 | gopt.usec_now = GS_usec(); 739 | 740 | // For stats try to figure out if peers negotiate an SSL connection 741 | if (p->in_n == 0) 742 | { 743 | // Check for 0x16 (22 ClientHelo & ServerHelo) being the first octet. 744 | // TLS 1.2/1.2 = 16 03 03 ... 745 | uint8_t c; 746 | evbuffer_copyout(eb, &c, 1); 747 | if (c == 0x16) 748 | { 749 | DEBUGF_Y("%c SSL-Helo received\n", IS_CS(p)); 750 | p->flags |= FL_PEER_IS_SAW_SSL_HELO; 751 | } 752 | } 753 | // #ifdef DEBUG 754 | // if (p->in_n == 0) 755 | // { 756 | // uint8_t buf[MIN(128, len)]; 757 | // evbuffer_copyout(eb, buf, MIN(128, len)); 758 | // DEBUGF("%c has %zd bytes in input buffer\n", IS_CS(p), len); 759 | // HEXDUMP(buf, MIN(128, len)); 760 | // } 761 | // #endif 762 | p->in_n += len; 763 | p->in_last_usec = gopt.usec_now; 764 | 765 | // -----BEGIN BPS STATS----- 766 | if (gopt.usec_now - p->bps_last_usec >= GSRN_BPS_WINDOW) 767 | { 768 | peer_stats_update_bps(p); 769 | } 770 | // -----END BPS STATS----- 771 | 772 | if (p->buddy == NULL) 773 | return; 774 | 775 | p->buddy->out_n += len; 776 | p->buddy->out_last_usec = p->in_last_usec; 777 | } 778 | 779 | struct _peer * 780 | PEER_new(int fd, SSL *ssl) 781 | { 782 | struct _peer *p; 783 | 784 | p = calloc(1, sizeof *p); 785 | if (p == NULL) 786 | { 787 | // FIXME: Log this failure 788 | return NULL; 789 | } 790 | 791 | PKT_init(&p->pkt); 792 | 793 | // Assign a uniq PEER id to each instance (start with 1) 794 | gd.peer_id += 1; 795 | p->id = gd.peer_id; 796 | p->fd = fd; 797 | 798 | socklen_t slen = sizeof (struct sockaddr_in); 799 | getpeername(fd, (struct sockaddr *)&p->addr_in, &slen); 800 | DEBUGF_B("[%6u] peer=%p fd=%d ip=%s:%u\n", p->id, p, fd, int_ntoa(p->addr_in.sin_addr.s_addr), ntohs(p->addr_in.sin_port)); 801 | 802 | int ev_opt = BEV_OPT_DEFER_CALLBACKS; 803 | if (ssl != NULL) 804 | { 805 | p->ssl = ssl; 806 | SSL_set_fd(p->ssl, fd); 807 | p->bev = bufferevent_openssl_socket_new(gopt.evb, -1, p->ssl, BUFFEREVENT_SSL_ACCEPTING, ev_opt); 808 | } else { 809 | p->bev = bufferevent_socket_new(gopt.evb, fd, ev_opt); 810 | } 811 | 812 | bufferevent_setcb(p->bev, cb_bev_read, cb_bev_write, cb_bev_status, p); 813 | bufferevent_set_timeouts(p->bev, TVSEC(GSRN_1STMSG_TIMEOUT) /*read*/, NULL /*write*/); 814 | 815 | if (ssl == NULL) 816 | cb_bev_status(p->bev, BEV_EVENT_CONNECTED, p); // Immediately go into 'connected' state 817 | 818 | GSRN_change_state(p, GSRN_STATE_INIT); 819 | 820 | if (ssl != NULL) 821 | { 822 | // When using SSL then also allow auth 823 | // PKT_setcb(&p->pkt, GS_PKT_TYPE_AUTH, 0 /*variable length*/, p); 824 | } 825 | 826 | return p; 827 | } 828 | 829 | 830 | --------------------------------------------------------------------------------