Repository: ToyoDAdoubiBackup/ServerStatus-Toyo Branch: master Commit: a653403f14e7 Files: 28 Total size: 202.5 KB Directory structure: gitextract_au8hq0df/ ├── LICENSE ├── README.md ├── clients/ │ ├── client-linux.py │ └── status-client.py ├── server/ │ ├── Makefile │ ├── config.json │ ├── include/ │ │ ├── argparse.h │ │ ├── detect.h │ │ ├── json.h │ │ └── system.h │ ├── obj/ │ │ └── .gitignore │ └── src/ │ ├── argparse.c │ ├── json.c │ ├── main.cpp │ ├── main.h │ ├── netban.cpp │ ├── netban.h │ ├── network.cpp │ ├── network.h │ ├── network_client.cpp │ ├── server.cpp │ ├── server.h │ └── system.c └── web/ ├── css/ │ └── light.css ├── index.html ├── js/ │ └── serverstatus.js ├── json/ │ └── .gitignore └── robots.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Toyo Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # ServerStatus-Toyo: ![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg) * ServerStatus-Toyo版是一个酷炫高逼格的云探针、云监控、服务器云监控、多服务器探针~,该云监控(云探针)是ServerStatus( https://github.com/tenyue/ServerStatus )项目的优化/修改版。 * 在线演示:https://tz.toyoo.pw * 我的博客:https://doub.io/shell-jc3/ # 目录介绍: * clients 客户端文件 * server 服务端文件 * web 网站文件 # 更新说明: * 2018.08.21, 修改新样式,效果见 https://tz.toyoo.pw * 2017.10.12, 负载Load 优化,并且支持CentOS6系统 * 2017.10.10, 修改负载 Load 的值为:当前服务器上链接SSR等软件的IP总数(只要软件监听IPv6那么就能统计,例如SSH) * 2017.04.30, 优化手机显示式样 * 2017.04.29, 去除主机名设定 * 2017.04.27, 增加一键部署脚本 # 安装教程: 执行下面的代码下载并运行脚本。 ``` bash wget -N --no-check-certificate https://raw.githubusercontent.com/ToyoDAdoubiBackup/doubi/master/status.sh && chmod +x status.sh ``` 下载脚本后,根据需要安装客户端或者服务端: ``` bash # 显示客户端管理菜单 bash status.sh c # 显示服务端管理菜单 bash status.sh s ``` 运行脚本后会出现脚本操作菜单,选择并输入` 1 `就会开始安装。 一开始会提示你输入 网站服务器的域名和端口,如果没有域名可以直接回车代表使用` 本机IP:8888` ## 简单步骤: 首先安装服务端,安装过程中会提示: ``` bash 是否由脚本自动配置HTTP服务(服务端的在线监控网站)[Y/n] # 如果你不懂,那就直接回车,如果你想用其他的HTTP服务自己配置,那么请输入 n 并回车。 # 注意,当你曾经安装过 服务端,同时没有卸载Caddy(HTTP服务),那么重新安装服务端的时候,请输入 n 并回车。 ``` 然后 添加或修改 初始示例的节点配置,注意用户名每个节点配置都不能重复,其他的参数都无所谓了。 然后安装客户端,根据提示填写 服务端的IP 和前面添加/修改 对应的 节点用户名和密码(用于和服务端验证),然后启动就好了,有问题请贴出 详细步骤+日志(如果有)联系我。 # 使用说明: 进入下载脚本的目录并运行脚本: ``` bash # 客户端管理菜单 ./status.sh c # 服务端管理菜单 ./status.sh s ``` 然后选择你要执行的选项即可。 ``` bash ServerStatus 一键安装管理脚本 [vx.x.x] -- Toyo | doub.io/shell-jc3 -- 0. 升级脚本 ———————————— 1. 安装 服务端 2. 卸载 服务端 ———————————— 3. 启动 服务端 4. 停止 服务端 5. 重启 服务端 ———————————— 6. 设置 服务端配置 7. 查看 服务端信息 8. 查看 服务端日志 ———————————— 9. 切换为 客户端菜单 当前状态: 服务端 已安装 并 已启动 请输入数字 [0-9]: ``` # 其他操作 ### 客户端: 启动:service status-client start 停止:service status-client stop 重启:service status-client restart 查看状态:service status-client status ### 服务端: 启动:service status-server start 停止:service status-server stop 重启:service status-server restart 查看状态:service status-server status ### Caddy(HTTP服务): 启动:service caddy start 停止:service caddy stop 重启:service caddy restart 查看状态:service caddy status Caddy配置文件:/usr/local/caddy/caddy 默认脚本只能一开始安装的时候设置配置文件,更多的Caddy使用方法,可以参考这些教程:https://doub.io/search/caddy —————————————————————————————————————— 安装目录:/usr/local/ServerStatus 网页文件:/usr/local/ServerStatus/web 配置文件:/usr/local/ServerStatus/server/config.json 客户端查看日志:tail -f tmp/serverstatus_client.log 服务端查看日志:tail -f /tmp/serverstatus_server.log # 其他说明 网络实时流量单位为:G=GB/s,M=MB/s,K=KB/s 服务器总流量单位为:T=TB,G=GB,M=MB,K=KB ### CentOS7系统 负载显示异常的问题 CentOS7系统 默认可能没有安装 netstat 依赖,所以会造成IP检测(负载)出错,手动安装即可: `yum install net-tools -y ` # 相关开源项目,感谢: * ServerStatus:https://github.com/BotoX/ServerStatus * mojeda: https://github.com/mojeda * mojeda's ServerStatus: https://github.com/mojeda/ServerStatus * BlueVM's project: http://www.lowendtalk.com/discussion/comment/169690#Comment_169690 ================================================ FILE: clients/client-linux.py ================================================ # -*- coding: utf-8 -*- # Update by : https://github.com/tenyue/ServerStatus # 支持Python版本:2.6 to 3.5 # 支持操作系统: Linux, OSX, FreeBSD, OpenBSD and NetBSD, both 32-bit and 64-bit architectures SERVER = "127.0.0.1" PORT = 35601 USER = "USER" PASSWORD = "USER_PASSWORD" INTERVAL = 1 #更新间隔 import socket import time import string import math import re import os import json import subprocess import collections import platform def get_uptime(): f = open('/proc/uptime', 'r') uptime = f.readline() f.close() uptime = uptime.split('.', 2) time = int(uptime[0]) return int(time) def get_memory(): re_parser = re.compile(r'^(?P\S*):\s*(?P\d*)\s*kB') result = dict() for line in open('/proc/meminfo'): match = re_parser.match(line) if not match: continue; key, value = match.groups(['key', 'value']) result[key] = int(value) MemTotal = float(result['MemTotal']) MemFree = float(result['MemFree']) Cached = float(result['Cached']) MemUsed = MemTotal - (Cached + MemFree) SwapTotal = float(result['SwapTotal']) SwapFree = float(result['SwapFree']) return int(MemTotal), int(MemUsed), int(SwapTotal), int(SwapFree) def get_hdd(): p = subprocess.check_output(['df', '-Tlm', '--total', '-t', 'ext4', '-t', 'ext3', '-t', 'ext2', '-t', 'reiserfs', '-t', 'jfs', '-t', 'ntfs', '-t', 'fat32', '-t', 'btrfs', '-t', 'fuseblk', '-t', 'zfs', '-t', 'simfs', '-t', 'xfs']).decode("Utf-8") total = p.splitlines()[-1] used = total.split()[3] size = total.split()[2] return int(size), int(used) def get_load(): system = platform.linux_distribution() if system[0][:6] == "CentOS": if system[1][0] == "6": tmp_load = os.popen("netstat -anp |grep ESTABLISHED |grep tcp |grep '::ffff:' |awk '{print $5}' |awk -F ':' '{print $4}' |sort -u |grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}' |wc -l").read() else: tmp_load = os.popen("netstat -anp |grep ESTABLISHED |grep tcp6 |awk '{print $5}' |awk -F ':' '{print $1}' |sort -u |grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}' |wc -l").read() else: tmp_load = os.popen("netstat -anp |grep ESTABLISHED |grep tcp6 |awk '{print $5}' |awk -F ':' '{print $1}' |sort -u |grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}' |wc -l").read() return float(tmp_load) #return os.getloadavg()[0] def get_time(): stat_file = file("/proc/stat", "r") time_list = stat_file.readline().split(' ')[2:6] stat_file.close() for i in range(len(time_list)) : time_list[i] = int(time_list[i]) return time_list def delta_time(): x = get_time() time.sleep(INTERVAL) y = get_time() for i in range(len(x)): y[i]-=x[i] return y def get_cpu(): t = delta_time() st = sum(t) if st == 0: st = 1 result = 100-(t[len(t)-1]*100.00/st) return round(result) class Traffic: def __init__(self): self.rx = collections.deque(maxlen=10) self.tx = collections.deque(maxlen=10) def get(self): f = open('/proc/net/dev', 'r') net_dev = f.readlines() f.close() avgrx = 0; avgtx = 0 for dev in net_dev[2:]: dev = dev.split(':') if dev[0].strip() == "lo" or dev[0].find("tun") > -1: continue dev = dev[1].split() avgrx += int(dev[0]) avgtx += int(dev[8]) self.rx.append(avgrx) self.tx.append(avgtx) avgrx = 0; avgtx = 0 l = len(self.rx) for x in range(l - 1): avgrx += self.rx[x+1] - self.rx[x] avgtx += self.tx[x+1] - self.tx[x] avgrx = int(avgrx / l / INTERVAL) avgtx = int(avgtx / l / INTERVAL) return avgrx, avgtx def liuliang(): NET_IN = 0 NET_OUT = 0 with open('/proc/net/dev') as f: for line in f.readlines(): netinfo = re.findall('([^\s]+):[\s]{0,}(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)', line) if netinfo: if netinfo[0][0] == 'lo' or 'tun' in netinfo[0][0] or netinfo[0][1]=='0' or netinfo[0][9]=='0': continue else: NET_IN += int(netinfo[0][1]) NET_OUT += int(netinfo[0][9]) return NET_IN, NET_OUT def get_network(ip_version): if(ip_version == 4): HOST = "ipv4.google.com" elif(ip_version == 6): HOST = "ipv6.google.com" try: s = socket.create_connection((HOST, 80), 2) return True except: pass return False if __name__ == '__main__': socket.setdefaulttimeout(30) while 1: try: print("Connecting...") s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((SERVER, PORT)) data = s.recv(1024) if data.find("Authentication required") > -1: s.send(USER + ':' + PASSWORD + '\n') data = s.recv(1024) if data.find("Authentication successful") < 0: print(data) raise socket.error else: print(data) raise socket.error print(data) data = s.recv(1024) print(data) timer = 0 check_ip = 0 if data.find("IPv4") > -1: check_ip = 6 elif data.find("IPv6") > -1: check_ip = 4 else: print(data) raise socket.error traffic = Traffic() traffic.get() while 1: CPU = get_cpu() NetRx, NetTx = traffic.get() NET_IN, NET_OUT = liuliang() Uptime = get_uptime() Load = get_load() MemoryTotal, MemoryUsed, SwapTotal, SwapFree = get_memory() HDDTotal, HDDUsed = get_hdd() array = {} if not timer: array['online' + str(check_ip)] = get_network(check_ip) timer = 10 else: timer -= 1*INTERVAL array['uptime'] = Uptime array['load'] = Load array['memory_total'] = MemoryTotal array['memory_used'] = MemoryUsed array['swap_total'] = SwapTotal array['swap_used'] = SwapTotal - SwapFree array['hdd_total'] = HDDTotal array['hdd_used'] = HDDUsed array['cpu'] = CPU array['network_rx'] = NetRx array['network_tx'] = NetTx array['network_in'] = NET_IN array['network_out'] = NET_OUT s.send("update " + json.dumps(array) + "\n") except KeyboardInterrupt: raise except socket.error: print("Disconnected...") # keep on trying after a disconnect s.close() time.sleep(3) except Exception as e: print("Caught Exception:", e) s.close() time.sleep(3) ================================================ FILE: clients/status-client.py ================================================ # -*- coding: utf-8 -*- SERVER = "127.0.0.1" PORT = PORT USER = "USER" PASSWORD = "USER_PASSWORD" INTERVAL = 1 #更新间隔,单位:秒 import socket import time import string import math import re import os import json import subprocess import collections import platform def get_uptime(): f = open('/proc/uptime', 'r') uptime = f.readline() f.close() uptime = uptime.split('.', 2) time = int(uptime[0]) return int(time) def get_memory(): re_parser = re.compile(r'^(?P\S*):\s*(?P\d*)\s*kB') result = dict() for line in open('/proc/meminfo'): match = re_parser.match(line) if not match: continue; key, value = match.groups(['key', 'value']) result[key] = int(value) MemTotal = float(result['MemTotal']) MemFree = float(result['MemFree']) Cached = float(result['Cached']) MemUsed = MemTotal - (Cached + MemFree) SwapTotal = float(result['SwapTotal']) SwapFree = float(result['SwapFree']) return int(MemTotal), int(MemUsed), int(SwapTotal), int(SwapFree) def get_hdd(): p = subprocess.check_output(['df', '-Tlm', '--total', '-t', 'ext4', '-t', 'ext3', '-t', 'ext2', '-t', 'reiserfs', '-t', 'jfs', '-t', 'ntfs', '-t', 'fat32', '-t', 'btrfs', '-t', 'fuseblk', '-t', 'zfs', '-t', 'simfs', '-t', 'xfs']).decode("Utf-8") total = p.splitlines()[-1] used = total.split()[3] size = total.split()[2] return int(size), int(used) def get_load(): system = platform.linux_distribution() if system[0][:6] == "CentOS": if system[1][0] == "6": tmp_load = os.popen("netstat -anp |grep ESTABLISHED |grep tcp |grep '::ffff:' |awk '{print $5}' |awk -F ':' '{print $4}' |sort -u |grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}' |wc -l").read() else: tmp_load = os.popen("netstat -anp |grep ESTABLISHED |grep tcp6 |awk '{print $5}' |awk -F ':' '{print $1}' |sort -u |grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}' |wc -l").read() else: tmp_load = os.popen("netstat -anp |grep ESTABLISHED |grep tcp6 |awk '{print $5}' |awk -F ':' '{print $1}' |sort -u |grep -E -o '([0-9]{1,3}[\.]){3}[0-9]{1,3}' |wc -l").read() return float(tmp_load) #return os.getloadavg()[0] def get_time(): stat_file = file("/proc/stat", "r") time_list = stat_file.readline().split(' ')[2:6] stat_file.close() for i in range(len(time_list)) : time_list[i] = int(time_list[i]) return time_list def delta_time(): x = get_time() time.sleep(INTERVAL) y = get_time() for i in range(len(x)): y[i]-=x[i] return y def get_cpu(): t = delta_time() st = sum(t) if st == 0: st = 1 result = 100-(t[len(t)-1]*100.00/st) return round(result) class Traffic: def __init__(self): self.rx = collections.deque(maxlen=10) self.tx = collections.deque(maxlen=10) def get(self): f = open('/proc/net/dev', 'r') net_dev = f.readlines() f.close() avgrx = 0; avgtx = 0 for dev in net_dev[2:]: dev = dev.split(':') if dev[0].strip() == "lo" or dev[0].find("tun") > -1: continue dev = dev[1].split() avgrx += int(dev[0]) avgtx += int(dev[8]) self.rx.append(avgrx) self.tx.append(avgtx) avgrx = 0; avgtx = 0 l = len(self.rx) for x in range(l - 1): avgrx += self.rx[x+1] - self.rx[x] avgtx += self.tx[x+1] - self.tx[x] avgrx = int(avgrx / l / INTERVAL) avgtx = int(avgtx / l / INTERVAL) return avgrx, avgtx def liuliang(): NET_IN = 0 NET_OUT = 0 with open('/proc/net/dev') as f: for line in f.readlines(): netinfo = re.findall('([^\s]+):[\s]{0,}(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)', line) if netinfo: if netinfo[0][0] == 'lo' or 'tun' in netinfo[0][0] or netinfo[0][1]=='0' or netinfo[0][9]=='0': continue else: NET_IN += int(netinfo[0][1]) NET_OUT += int(netinfo[0][9]) return NET_IN, NET_OUT def get_network(ip_version): if(ip_version == 4): HOST = "ipv4.google.com" elif(ip_version == 6): HOST = "ipv6.google.com" try: s = socket.create_connection((HOST, 80), 2) return True except: pass return False if __name__ == '__main__': socket.setdefaulttimeout(30) while 1: try: print("Connecting...") s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((SERVER, PORT)) data = s.recv(1024) if data.find("Authentication required") > -1: s.send(USER + ':' + PASSWORD + '\n') data = s.recv(1024) if data.find("Authentication successful") < 0: print(data) raise socket.error else: print(data) raise socket.error print(data) data = s.recv(1024) print(data) timer = 0 check_ip = 0 if data.find("IPv4") > -1: check_ip = 6 elif data.find("IPv6") > -1: check_ip = 4 else: print(data) raise socket.error traffic = Traffic() traffic.get() while 1: CPU = get_cpu() NetRx, NetTx = traffic.get() NET_IN, NET_OUT = liuliang() Uptime = get_uptime() Load = get_load() MemoryTotal, MemoryUsed, SwapTotal, SwapFree = get_memory() HDDTotal, HDDUsed = get_hdd() array = {} if not timer: array['online' + str(check_ip)] = get_network(check_ip) timer = 10 else: timer -= 1*INTERVAL array['uptime'] = Uptime array['load'] = Load array['memory_total'] = MemoryTotal array['memory_used'] = MemoryUsed array['swap_total'] = SwapTotal array['swap_used'] = SwapTotal - SwapFree array['hdd_total'] = HDDTotal array['hdd_used'] = HDDUsed array['cpu'] = CPU array['network_rx'] = NetRx array['network_tx'] = NetTx array['network_in'] = NET_IN array['network_out'] = NET_OUT s.send("update " + json.dumps(array) + "\n") except KeyboardInterrupt: raise except socket.error: print("Disconnected...") # keep on trying after a disconnect s.close() time.sleep(3) except Exception as e: print("Caught Exception:", e) s.close() time.sleep(3) ================================================ FILE: server/Makefile ================================================ OUT = sergate #CC = clang CC = gcc CFLAGS = -Wall -O2 #CXX = clang++ CXX = g++ CXXFLAGS = -Wall -O2 ODIR = obj SDIR = src LIBS = -pthread -lm INC = -Iinclude C_SRCS := $(wildcard $(SDIR)/*.c) CXX_SRCS := $(wildcard $(SDIR)/*.cpp) C_OBJS := $(patsubst $(SDIR)/%.c,$(ODIR)/%.o,$(C_SRCS)) CXX_OBJS := $(patsubst $(SDIR)/%.cpp,$(ODIR)/%.o,$(CXX_SRCS)) OBJS := $(C_OBJS) $(CXX_OBJS) $(ODIR)/%.o: $(SDIR)/%.c $(CC) -c $(INC) $(CFLAGS) $< -o $@ $(ODIR)/%.o: $(SDIR)/%.cpp $(CXX) -c $(INC) $(CXXFLAGS) $< -o $@ $(OUT): $(OBJS) $(CXX) $(LIBS) $^ -o $(OUT) .PHONY: clean clean: rm -f $(ODIR)/*.o $(OUT) ================================================ FILE: server/config.json ================================================ {"servers": [ { "username": "s01", "password": "password", "name": "Mainserver 1", "type": "Dedicated Server", "host": "No", "location": "Austria", "disabled": false }, { "username": "bs01", "password": "password", "name": "Backupserver 1", "type": "Virtual Server", "host": "No", "location": "Switzerland", "disabled": false }, { "username": "hidden", "password": "password", "name": "Secret", "type": "Nothing", "host": "No", "location": "Nowhere", "disabled": true }, { "username": "butt", "password": "password", "name": "Butt", "type": "Cloud Server", "host": "No", "location": "Heaven??!", "disabled": false } ] } ================================================ FILE: server/include/argparse.h ================================================ #ifndef ARGPARSE_H #define ARGPARSE_H /** * Command-line arguments parsing library. * * This module is inspired by parse-options.c (git) and python's argparse * module. * * Arguments parsing is common task in cli program, but traditional `getopt` * libraries are not easy to use. This library provides high-level arguments * parsing solutions. * * The program defines what arguments it requires, and `argparse` will figure * out how to parse those out of `argc` and `argv`, it also automatically * generates help and usage messages and issues errors when users give the * program invalid arguments. * * Reserved namespaces: * argparse * OPT * Author: Yecheng Fu */ #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif struct argparse; struct argparse_option; typedef int argparse_callback(struct argparse *this_, const struct argparse_option *option); enum argparse_flag { ARGPARSE_STOP_AT_NON_OPTION = 1, }; enum argparse_option_type { /* special */ ARGPARSE_OPT_END, /* options with no arguments */ ARGPARSE_OPT_BOOLEAN, ARGPARSE_OPT_BIT, /* options with arguments (optional or required) */ ARGPARSE_OPT_INTEGER, ARGPARSE_OPT_STRING, }; enum argparse_option_flags { OPT_NONEG = 1, /* Negation disabled. */ }; /* * Argparse option struct. * * `type`: * holds the type of the option, you must have an ARGPARSE_OPT_END last in your * array. * * `short_name`: * the character to use as a short option name, '\0' if none. * * `long_name`: * the long option name, without the leading dash, NULL if none. * * `value`: * stores pointer to the value to be filled. * * `help`: * the short help message associated to what the option does. * Must never be NULL (except for ARGPARSE_OPT_END). * * `callback`: * function is called when corresponding argument is parsed. * * `data`: * associated data. Callbacks can use it like they want. * * `flags`: * option flags. * */ struct argparse_option { enum argparse_option_type type; const char short_name; const char *long_name; void *value; const char *help; argparse_callback *callback; intptr_t data; int flags; }; /* * argpparse */ struct argparse { // user supplied const struct argparse_option *options; const char *usage; int flags; // internal context int argc; const char **argv; const char **out; int cpidx; const char *optvalue; // current option value }; // builtin callbacks int argparse_help_cb(struct argparse *this_, const struct argparse_option *option); // builtin option macros #define OPT_END() { ARGPARSE_OPT_END, 0 } #define OPT_BOOLEAN(...) { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ } #define OPT_BIT(...) { ARGPARSE_OPT_BIT, __VA_ARGS__ } #define OPT_INTEGER(...) { ARGPARSE_OPT_INTEGER, __VA_ARGS__ } #define OPT_STRING(...) { ARGPARSE_OPT_STRING, __VA_ARGS__ } #define OPT_HELP() OPT_BOOLEAN('h', "help", 0, "Show this help message and exit", argparse_help_cb) int argparse_init(struct argparse *this_, struct argparse_option *options, const char *usage, int flags); int argparse_parse(struct argparse *this_, int argc, const char **argv); void argparse_usage(struct argparse *this_); #ifdef __cplusplus } #endif #endif ================================================ FILE: server/include/detect.h ================================================ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #ifndef BASE_DETECT_H #define BASE_DETECT_H /* this file detected the family, platform and architecture to compile for. */ /* platforms */ /* windows Family */ #if defined(WIN64) || defined(_WIN64) /* Hmm, is this IA64 or x86-64? */ #define CONF_FAMILY_WINDOWS 1 #define CONF_FAMILY_STRING "windows" #define CONF_PLATFORM_WIN64 1 #define CONF_PLATFORM_STRING "win64" #elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) #define CONF_FAMILY_WINDOWS 1 #define CONF_FAMILY_STRING "windows" #define CONF_PLATFORM_WIN32 1 #define CONF_PLATFORM_STRING "win32" #endif /* unix family */ #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #define CONF_FAMILY_UNIX 1 #define CONF_FAMILY_STRING "unix" #define CONF_PLATFORM_FREEBSD 1 #define CONF_PLATFORM_STRING "freebsd" #endif #if defined(__OpenBSD__) #define CONF_FAMILY_UNIX 1 #define CONF_FAMILY_STRING "unix" #define CONF_PLATFORM_OPENBSD 1 #define CONF_PLATFORM_STRING "openbsd" #endif #if defined(__LINUX__) || defined(__linux__) #define CONF_FAMILY_UNIX 1 #define CONF_FAMILY_STRING "unix" #define CONF_PLATFORM_LINUX 1 #define CONF_PLATFORM_STRING "linux" #endif #if defined(__GNU__) || defined(__gnu__) #define CONF_FAMILY_UNIX 1 #define CONF_FAMILY_STRING "unix" #define CONF_PLATFORM_HURD 1 #define CONF_PLATFORM_STRING "gnu" #endif #if defined(MACOSX) || defined(__APPLE__) || defined(__DARWIN__) #define CONF_FAMILY_UNIX 1 #define CONF_FAMILY_STRING "unix" #define CONF_PLATFORM_MACOSX 1 #define CONF_PLATFORM_STRING "macosx" #endif #if defined(__sun) #define CONF_FAMILY_UNIX 1 #define CONF_FAMILY_STRING "unix" #define CONF_PLATFORM_SOLARIS 1 #define CONF_PLATFORM_STRING "solaris" #endif /* beos family */ #if defined(__BeOS) || defined(__BEOS__) #define CONF_FAMILY_BEOS 1 #define CONF_FAMILY_STRING "beos" #define CONF_PLATFORM_BEOS 1 #define CONF_PLATFORM_STRING "beos" #endif /* use gcc endianness definitions when available */ #if defined(__GNUC__) && !defined(__APPLE__) && !defined(__MINGW32__) && !defined(__sun) #if defined(__FreeBSD__) || defined(__OpenBSD__) #include #else #include #endif #if __BYTE_ORDER == __LITTLE_ENDIAN #define CONF_ARCH_ENDIAN_LITTLE 1 #elif __BYTE_ORDER == __BIG_ENDIAN #define CONF_ARCH_ENDIAN_BIG 1 #endif #endif /* architectures */ #if defined(i386) || defined(__i386__) || defined(__x86__) || defined(CONF_PLATFORM_WIN32) #define CONF_ARCH_IA32 1 #define CONF_ARCH_STRING "ia32" #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) #define CONF_ARCH_ENDIAN_LITTLE 1 #endif #endif #if defined(__ia64__) || defined(_M_IA64) #define CONF_ARCH_IA64 1 #define CONF_ARCH_STRING "ia64" #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) #define CONF_ARCH_ENDIAN_LITTLE 1 #endif #endif #if defined(__amd64__) || defined(__x86_64__) || defined(_M_X64) #define CONF_ARCH_AMD64 1 #define CONF_ARCH_STRING "amd64" #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) #define CONF_ARCH_ENDIAN_LITTLE 1 #endif #endif #if defined(__powerpc__) || defined(__ppc__) #define CONF_ARCH_PPC 1 #define CONF_ARCH_STRING "ppc" #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) #define CONF_ARCH_ENDIAN_BIG 1 #endif #endif #if defined(__sparc__) #define CONF_ARCH_SPARC 1 #define CONF_ARCH_STRING "sparc" #if !defined(CONF_ARCH_ENDIAN_LITTLE) && !defined(CONF_ARCH_ENDIAN_BIG) #define CONF_ARCH_ENDIAN_BIG 1 #endif #endif #ifndef CONF_FAMILY_STRING #define CONF_FAMILY_STRING "unknown" #endif #ifndef CONF_PLATFORM_STRING #define CONF_PLATFORM_STRING "unknown" #endif #ifndef CONF_ARCH_STRING #define CONF_ARCH_STRING "unknown" #endif #endif ================================================ FILE: server/include/json.h ================================================ /* vim: set et ts=3 sw=3 sts=3 ft=c: * * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. * https://github.com/udp/json-parser * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _JSON_H #define _JSON_H #ifndef json_char #define json_char char #endif #ifndef json_int_t #ifndef _MSC_VER #include #define json_int_t int64_t #else #define json_int_t __int64 #endif #endif #include #ifdef __cplusplus #include extern "C" { #endif typedef struct { unsigned long max_memory; int settings; /* Custom allocator support (leave null to use malloc/free) */ void * (* mem_alloc) (size_t, int zero, void * user_data); void (* mem_free) (void *, void * user_data); void * user_data; /* will be passed to mem_alloc and mem_free */ } json_settings; #define json_enable_comments 0x01 typedef enum { json_none, json_object, json_array, json_integer, json_double, json_string, json_boolean, json_null } json_type; extern const struct _json_value json_value_none; typedef struct _json_value { struct _json_value * parent; json_type type; union { int boolean; json_int_t integer; double dbl; struct { unsigned int length; json_char * ptr; /* null terminated */ } string; struct { unsigned int length; struct { json_char * name; unsigned int name_length; struct _json_value * value; } * values; #if defined(__cplusplus) && __cplusplus >= 201103L decltype(values) begin () const { return values; } decltype(values) end () const { return values + length; } #endif } object; struct { unsigned int length; struct _json_value ** values; #if defined(__cplusplus) && __cplusplus >= 201103L decltype(values) begin () const { return values; } decltype(values) end () const { return values + length; } #endif } array; } u; union { struct _json_value * next_alloc; void * object_mem; } _reserved; /* Some C++ operator sugar */ #ifdef __cplusplus public: inline _json_value () { memset (this, 0, sizeof (_json_value)); } inline const struct _json_value &operator [] (int index) const { if (type != json_array || index < 0 || ((unsigned int) index) >= u.array.length) { return json_value_none; } return *u.array.values [index]; } inline const struct _json_value &operator [] (const char * index) const { if (type != json_object) return json_value_none; for (unsigned int i = 0; i < u.object.length; ++ i) if (!strcmp (u.object.values [i].name, index)) return *u.object.values [i].value; return json_value_none; } inline operator const char * () const { switch (type) { case json_string: return u.string.ptr; default: return ""; }; } inline operator json_int_t () const { switch (type) { case json_integer: return u.integer; case json_double: return (json_int_t) u.dbl; default: return 0; }; } inline operator bool () const { if (type != json_boolean) return false; return u.boolean != 0; } inline operator double () const { switch (type) { case json_integer: return (double) u.integer; case json_double: return u.dbl; default: return 0; }; } #endif } json_value; json_value * json_parse (const json_char * json, size_t length); #define json_error_max 128 json_value * json_parse_ex (json_settings * settings, const json_char * json, size_t length, char * error); void json_value_free (json_value *); /* Not usually necessary, unless you used a custom mem_alloc and now want to * use a custom mem_free. */ void json_value_free_ex (json_settings * settings, json_value *); #ifdef __cplusplus } /* extern "C" */ #endif #endif ================================================ FILE: server/include/system.h ================================================ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ /* Title: OS Abstraction */ #ifndef BASE_SYSTEM_H #define BASE_SYSTEM_H #include "detect.h" #ifdef __cplusplus extern "C" { #endif /* Group: Debug */ /* Function: dbg_assert Breaks into the debugger based on a test. Parameters: test - Result of the test. msg - Message that should be printed if the test fails. Remarks: Does nothing in release version of the library. See Also: */ void dbg_assert(int test, const char *msg); #define dbg_assert(test,msg) dbg_assert_imp(__FILE__, __LINE__, test, msg) void dbg_assert_imp(const char *filename, int line, int test, const char *msg); #ifdef __clang_analyzer__ #include #undef dbg_assert #define dbg_assert(test,msg) assert(test) #endif /* Function: dbg_break Breaks into the debugger. Remarks: Does nothing in release version of the library. See Also: */ void dbg_break(); /* Function: dbg_msg Prints a debug message. Parameters: sys - A string that describes what system the message belongs to fmt - A printf styled format string. Remarks: Does nothing in release version of the library. See Also: */ void dbg_msg(const char *sys, const char *fmt, ...); /* Group: Memory */ /* Function: mem_alloc Allocates memory. Parameters: size - Size of the needed block. alignment - Alignment for the block. Returns: Returns a pointer to the newly allocated block. Returns a null pointer if the memory couldn't be allocated. Remarks: - Passing 0 to size will allocated the smallest amount possible and return a unique pointer. See Also: */ void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment); #define mem_alloc(s,a) mem_alloc_debug(__FILE__, __LINE__, (s), (a)) /* Function: mem_free Frees a block allocated through . Remarks: - In the debug version of the library the function will assert if a non-valid block is passed, like a null pointer or a block that isn't allocated. See Also: */ void mem_free(void *block); /* Function: mem_copy Copies a a memory block. Parameters: dest - Destination. source - Source to copy. size - Size of the block to copy. Remarks: - This functions DOES NOT handles cases where source and destination is overlapping. See Also: */ void mem_copy(void *dest, const void *source, unsigned size); /* Function: mem_move Copies a a memory block Parameters: dest - Destination source - Source to copy size - Size of the block to copy Remarks: - This functions handles cases where source and destination is overlapping See Also: */ void mem_move(void *dest, const void *source, unsigned size); /* Function: mem_zero Sets a complete memory block to 0 Parameters: block - Pointer to the block to zero out size - Size of the block */ void mem_zero(void *block, unsigned size); /* Function: mem_comp Compares two blocks of memory Parameters: a - First block of data b - Second block of data size - Size of the data to compare Returns: <0 - Block a is lesser then block b 0 - Block a is equal to block b >0 - Block a is greater then block b */ int mem_comp(const void *a, const void *b, int size); /* Function: mem_check Validates the heap Will trigger a assert if memory has failed. */ int mem_check_imp(); #define mem_check() dbg_assert_imp(__FILE__, __LINE__, mem_check_imp(), "Memory check failed") /* Group: File IO */ enum { IOFLAG_READ = 1, IOFLAG_WRITE = 2, IOFLAG_RANDOM = 4, IOSEEK_START = 0, IOSEEK_CUR = 1, IOSEEK_END = 2 }; typedef struct IOINTERNAL *IOHANDLE; /* Function: io_open Opens a file. Parameters: filename - File to open. flags - A set of flags. IOFLAG_READ, IOFLAG_WRITE, IOFLAG_RANDOM. Returns: Returns a handle to the file on success and 0 on failure. */ IOHANDLE io_open(const char *filename, int flags); /* Function: io_read Reads data into a buffer from a file. Parameters: io - Handle to the file to read data from. buffer - Pointer to the buffer that will recive the data. size - Number of bytes to read from the file. Returns: Number of bytes read. */ unsigned io_read(IOHANDLE io, void *buffer, unsigned size); /* Function: io_skip Skips data in a file. Parameters: io - Handle to the file. size - Number of bytes to skip. Returns: Number of bytes skipped. */ unsigned io_skip(IOHANDLE io, int size); /* Function: io_write Writes data from a buffer to file. Parameters: io - Handle to the file. buffer - Pointer to the data that should be written. size - Number of bytes to write. Returns: Number of bytes written. */ unsigned io_write(IOHANDLE io, const void *buffer, unsigned size); /* Function: io_write_newline Writes newline to file. Parameters: io - Handle to the file. Returns: Number of bytes written. */ unsigned io_write_newline(IOHANDLE io); /* Function: io_seek Seeks to a specified offset in the file. Parameters: io - Handle to the file. offset - Offset from pos to stop. origin - Position to start searching from. Returns: Returns 0 on success. */ int io_seek(IOHANDLE io, int offset, int origin); /* Function: io_tell Gets the current position in the file. Parameters: io - Handle to the file. Returns: Returns the current position. -1L if an error occured. */ long int io_tell(IOHANDLE io); /* Function: io_length Gets the total length of the file. Resetting cursor to the beginning Parameters: io - Handle to the file. Returns: Returns the total size. -1L if an error occured. */ long int io_length(IOHANDLE io); /* Function: io_close Closes a file. Parameters: io - Handle to the file. Returns: Returns 0 on success. */ int io_close(IOHANDLE io); /* Function: io_flush Empties all buffers and writes all pending data. Parameters: io - Handle to the file. Returns: Returns 0 on success. */ int io_flush(IOHANDLE io); /* Function: io_stdin Returns an to the standard input. */ IOHANDLE io_stdin(); /* Function: io_stdout Returns an to the standard output. */ IOHANDLE io_stdout(); /* Function: io_stderr Returns an to the standard error. */ IOHANDLE io_stderr(); /* Group: Threads */ /* Function: thread_sleep Suspends the current thread for a given period. Parameters: milliseconds - Number of milliseconds to sleep. */ void thread_sleep(int milliseconds); /* Function: thread_create Creates a new thread. Parameters: threadfunc - Entry point for the new thread. user - Pointer to pass to the thread. */ void *thread_create(void (*threadfunc)(void *), void *user); /* Function: thread_wait Waits for a thread to be done or destroyed. Parameters: thread - Thread to wait for. */ void thread_wait(void *thread); /* Function: thread_destroy Destroys a thread. Parameters: thread - Thread to destroy. */ void thread_destroy(void *thread); /* Function: thread_yeild Yeild the current threads execution slice. */ void thread_yield(); /* Function: thread_detach Puts the thread in the detached thread, guaranteeing that resources of the thread will be freed immediately when the thread terminates. Parameters: thread - Thread to detach */ void thread_detach(void *thread); /* Group: Locks */ typedef void* LOCK; LOCK lock_create(); void lock_destroy(LOCK lock); int lock_try(LOCK lock); void lock_wait(LOCK lock); void lock_release(LOCK lock); /* Group: Semaphores */ #if !defined(CONF_PLATFORM_MACOSX) #if defined(CONF_FAMILY_UNIX) #include typedef sem_t SEMAPHORE; #elif defined(CONF_FAMILY_WINDOWS) typedef void* SEMAPHORE; #else #error missing sempahore implementation #endif void semaphore_init(SEMAPHORE *sem); void semaphore_wait(SEMAPHORE *sem); void semaphore_signal(SEMAPHORE *sem); void semaphore_destroy(SEMAPHORE *sem); #endif /* Group: Timer */ #ifdef __GNUC__ /* if compiled with -pedantic-errors it will complain about long not being a C90 thing. */ __extension__ typedef long long int64; #else typedef long long int64; #endif /* Function: time_get Fetches a sample from a high resolution timer. Returns: Current value of the timer. Remarks: To know how fast the timer is ticking, see . */ int64 time_get(); /* Function: time_freq Returns the frequency of the high resolution timer. Returns: Returns the frequency of the high resolution timer. */ int64 time_freq(); /* Function: time_timestamp Retrives the current time as a UNIX timestamp Returns: The time as a UNIX timestamp */ int time_timestamp(); /* Group: Network General */ typedef struct { int type; int ipv4sock; int ipv6sock; } NETSOCKET; enum { NETADDR_MAXSTRSIZE = 1+(8*4+7)+1+1+5+1, // [XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX]:XXXXX NETTYPE_INVALID = 0, NETTYPE_IPV4 = 1, NETTYPE_IPV6 = 2, NETTYPE_LINK_BROADCAST = 4, NETTYPE_ALL = NETTYPE_IPV4|NETTYPE_IPV6 }; typedef struct { unsigned int type; unsigned char ip[16]; unsigned short port; } NETADDR; /* Function: net_init Initiates network functionallity. Returns: Returns 0 on success, Remarks: You must call this function before using any other network functions. */ int net_init(); /* Function: net_host_lookup Does a hostname lookup by name and fills out the passed NETADDR struct with the recieved details. Returns: 0 on success. */ int net_host_lookup(const char *hostname, NETADDR *addr, int types); /* Function: net_addr_comp Compares two network addresses. Parameters: a - Address to compare b - Address to compare to. Returns: <0 - Address a is lesser then address b 0 - Address a is equal to address b >0 - Address a is greater then address b */ int net_addr_comp(const NETADDR *a, const NETADDR *b); /* Function: net_addr_str Turns a network address into a representive string. Parameters: addr - Address to turn into a string. string - Buffer to fill with the string. max_length - Maximum size of the string. add_port - add port to string or not Remarks: - The string will always be zero terminated */ void net_addr_str(const NETADDR *addr, char *string, int max_length, int add_port); /* Function: net_addr_from_str Turns string into a network address. Returns: 0 on success Parameters: addr - Address to fill in. string - String to parse. */ int net_addr_from_str(NETADDR *addr, const char *string); /* Group: Network UDP */ /* Function: net_udp_create Creates a UDP socket and binds it to a port. Parameters: bindaddr - Address to bind the socket to. Returns: On success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID. */ NETSOCKET net_udp_create(NETADDR bindaddr); /* Function: net_udp_send Sends a packet over an UDP socket. Parameters: sock - Socket to use. addr - Where to send the packet. data - Pointer to the packet data to send. size - Size of the packet. Returns: On success it returns the number of bytes sent. Returns -1 on error. */ int net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size); /* Function: net_udp_recv Recives a packet over an UDP socket. Parameters: sock - Socket to use. addr - Pointer to an NETADDR that will recive the address. data - Pointer to a buffer that will recive the data. maxsize - Maximum size to recive. Returns: On success it returns the number of bytes recived. Returns -1 on error. */ int net_udp_recv(NETSOCKET sock, NETADDR *addr, void *data, int maxsize); /* Function: net_udp_close Closes an UDP socket. Parameters: sock - Socket to close. Returns: Returns 0 on success. -1 on error. */ int net_udp_close(NETSOCKET sock); /* Group: Network TCP */ /* Function: net_tcp_create Creates a TCP socket. Parameters: bindaddr - Address to bind the socket to. Returns: On success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID. */ NETSOCKET net_tcp_create(NETADDR bindaddr); /* Function: net_tcp_listen Makes the socket start listening for new connections. Parameters: sock - Socket to start listen to. backlog - Size of the queue of incomming connections to keep. Returns: Returns 0 on success. */ int net_tcp_listen(NETSOCKET sock, int backlog); /* Function: net_tcp_accept Polls a listning socket for a new connection. Parameters: sock - Listning socket to poll. new_sock - Pointer to a socket to fill in with the new socket. addr - Pointer to an address that will be filled in the remote address (optional, can be NULL). Returns: Returns a non-negative integer on success. Negative integer on failure. */ int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *addr); /* Function: net_tcp_connect Connects one socket to another. Parameters: sock - Socket to connect. addr - Address to connect to. Returns: Returns 0 on success. */ int net_tcp_connect(NETSOCKET sock, const NETADDR *addr); /* Function: net_tcp_send Sends data to a TCP stream. Parameters: sock - Socket to send data to. data - Pointer to the data to send. size - Size of the data to send. Returns: Number of bytes sent. Negative value on failure. */ int net_tcp_send(NETSOCKET sock, const void *data, int size); /* Function: net_tcp_recv Recvives data from a TCP stream. Parameters: sock - Socket to recvive data from. data - Pointer to a buffer to write the data to max_size - Maximum of data to write to the buffer. Returns: Number of bytes recvived. Negative value on failure. When in non-blocking mode, it returns 0 when there is no more data to be fetched. */ int net_tcp_recv(NETSOCKET sock, void *data, int maxsize); /* Function: net_tcp_close Closes a TCP socket. Parameters: sock - Socket to close. Returns: Returns 0 on success. Negative value on failure. */ int net_tcp_close(NETSOCKET sock); /* Group: Strings */ /* Function: str_append Appends a string to another. Parameters: dst - Pointer to a buffer that contains a string. src - String to append. dst_size - Size of the buffer of the dst string. Remarks: - The strings are treated as zero-termineted strings. - Garantees that dst string will contain zero-termination. */ void str_append(char *dst, const char *src, int dst_size); /* Function: str_copy Copies a string to another. Parameters: dst - Pointer to a buffer that shall recive the string. src - String to be copied. dst_size - Size of the buffer dst. Remarks: - The strings are treated as zero-termineted strings. - Garantees that dst string will contain zero-termination. */ void str_copy(char *dst, const char *src, int dst_size); /* Function: str_length Returns the length of a zero terminated string. Parameters: str - Pointer to the string. Returns: Length of string in bytes excluding the zero termination. */ int str_length(const char *str); /* Function: str_format Performs printf formating into a buffer. Parameters: buffer - Pointer to the buffer to recive the formated string. buffer_size - Size of the buffer. format - printf formating string. ... - Parameters for the formating. Remarks: - See the C manual for syntax for the printf formating string. - The strings are treated as zero-termineted strings. - Garantees that dst string will contain zero-termination. */ void str_format(char *buffer, int buffer_size, const char *format, ...); /* Function: str_sanitize_strong Replaces all characters below 32 and above 127 with whitespace. Parameters: str - String to sanitize. Remarks: - The strings are treated as zero-termineted strings. */ void str_sanitize_strong(char *str); /* Function: str_sanitize_cc Replaces all characters below 32 with whitespace. Parameters: str - String to sanitize. Remarks: - The strings are treated as zero-termineted strings. */ void str_sanitize_cc(char *str); /* Function: str_sanitize Replaces all characters below 32 with whitespace with exception to \t, \n and \r. Parameters: str - String to sanitize. Remarks: - The strings are treated as zero-termineted strings. */ void str_sanitize(char *str); /* Function: str_skip_to_whitespace Skips leading non-whitespace characters(all but ' ', '\t', '\n', '\r'). Parameters: str - Pointer to the string. Returns: Pointer to the first whitespace character found within the string. Remarks: - The strings are treated as zero-termineted strings. */ char *str_skip_to_whitespace(char *str); /* Function: str_skip_whitespaces Skips leading whitespace characters(' ', '\t', '\n', '\r'). Parameters: str - Pointer to the string. Returns: Pointer to the first non-whitespace character found within the string. Remarks: - The strings are treated as zero-termineted strings. */ char *str_skip_whitespaces(char *str); /* Function: str_comp_nocase Compares to strings case insensitive. Parameters: a - String to compare. b - String to compare. Returns: <0 - String a is lesser then string b 0 - String a is equal to string b >0 - String a is greater then string b Remarks: - Only garanted to work with a-z/A-Z. - The strings are treated as zero-termineted strings. */ int str_comp_nocase(const char *a, const char *b); /* Function: str_comp_nocase_num Compares up to num characters of two strings case insensitive. Parameters: a - String to compare. b - String to compare. num - Maximum characters to compare Returns: <0 - String a is lesser than string b 0 - String a is equal to string b >0 - String a is greater than string b Remarks: - Only garanted to work with a-z/A-Z. - The strings are treated as zero-termineted strings. */ int str_comp_nocase_num(const char *a, const char *b, const int num); /* Function: str_comp Compares to strings case sensitive. Parameters: a - String to compare. b - String to compare. Returns: <0 - String a is lesser then string b 0 - String a is equal to string b >0 - String a is greater then string b Remarks: - The strings are treated as zero-termineted strings. */ int str_comp(const char *a, const char *b); /* Function: str_comp_num Compares up to num characters of two strings case sensitive. Parameters: a - String to compare. b - String to compare. num - Maximum characters to compare Returns: <0 - String a is lesser then string b 0 - String a is equal to string b >0 - String a is greater then string b Remarks: - The strings are treated as zero-termineted strings. */ int str_comp_num(const char *a, const char *b, const int num); /* Function: str_comp_filenames Compares two strings case sensitive, digit chars will be compared as numbers. Parameters: a - String to compare. b - String to compare. Returns: <0 - String a is lesser then string b 0 - String a is equal to string b >0 - String a is greater then string b Remarks: - The strings are treated as zero-termineted strings. */ int str_comp_filenames(const char *a, const char *b); /* Function: str_find_nocase Finds a string inside another string case insensitive. Parameters: haystack - String to search in needle - String to search for Returns: A pointer into haystack where the needle was found. Returns NULL of needle could not be found. Remarks: - Only garanted to work with a-z/A-Z. - The strings are treated as zero-termineted strings. */ const char *str_find_nocase(const char *haystack, const char *needle); /* Function: str_find Finds a string inside another string case sensitive. Parameters: haystack - String to search in needle - String to search for Returns: A pointer into haystack where the needle was found. Returns NULL of needle could not be found. Remarks: - The strings are treated as zero-termineted strings. */ const char *str_find(const char *haystack, const char *needle); /* Function: str_hex Takes a datablock and generates a hexstring of it. Parameters: dst - Buffer to fill with hex data dst_size - size of the buffer data - Data to turn into hex data - Size of the data Remarks: - The desination buffer will be zero-terminated */ void str_hex(char *dst, int dst_size, const void *data, int data_size); /* Function: str_timestamp Copies a time stamp in the format year-month-day_hour-minute-second to the string. Parameters: buffer - Pointer to a buffer that shall receive the time stamp string. buffer_size - Size of the buffer. Remarks: - Guarantees that buffer string will contain zero-termination. */ void str_timestamp(char *buffer, int buffer_size); /* Group: Filesystem */ /* Function: fs_listdir Lists the files in a directory Parameters: dir - Directory to list cb - Callback function to call for each entry type - Type of the directory user - Pointer to give to the callback Returns: Always returns 0. */ typedef int (*FS_LISTDIR_CALLBACK)(const char *name, int is_dir, int dir_type, void *user); int fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, int type, void *user); /* Function: fs_makedir Creates a directory Parameters: path - Directory to create Returns: Returns 0 on success. Negative value on failure. Remarks: Does not create several directories if needed. "a/b/c" will result in a failure if b or a does not exist. */ int fs_makedir(const char *path); /* Function: fs_storage_path Fetches per user configuration directory. Returns: Returns 0 on success. Negative value on failure. Remarks: - Returns ~/.appname on UNIX based systems - Returns ~/Library/Applications Support/appname on Mac OS X - Returns %APPDATA%/Appname on Windows based systems */ int fs_storage_path(const char *appname, char *path, int max); /* Function: fs_is_dir Checks if directory exists Returns: Returns 1 on success, 0 on failure. */ int fs_is_dir(const char *path); /* Function: fs_chdir Changes current working directory Returns: Returns 0 on success, 1 on failure. */ int fs_chdir(const char *path); /* Function: fs_getcwd Gets the current working directory. Returns: Returns a pointer to the buffer on success, 0 on failure. */ char *fs_getcwd(char *buffer, int buffer_size); /* Function: fs_parent_dir Get the parent directory of a directory Parameters: path - The directory string Returns: Returns 0 on success, 1 on failure. Remarks: - The string is treated as zero-termineted string. */ int fs_parent_dir(char *path); /* Function: fs_remove Deletes the file with the specified name. Parameters: filename - The file to delete Returns: Returns 0 on success, 1 on failure. Remarks: - The strings are treated as zero-terminated strings. */ int fs_remove(const char *filename); /* Function: fs_rename Renames the file or directory. If the paths differ the file will be moved. Parameters: oldname - The actual name newname - The new name Returns: Returns 0 on success, 1 on failure. Remarks: - The strings are treated as zero-terminated strings. */ int fs_rename(const char *oldname, const char *newname); /* Group: Undocumented */ /* Function: net_tcp_connect_non_blocking DOCTODO: serp */ int net_tcp_connect_non_blocking(NETSOCKET sock, NETADDR bindaddr); /* Function: net_set_non_blocking DOCTODO: serp */ int net_set_non_blocking(NETSOCKET sock); /* Function: net_set_non_blocking DOCTODO: serp */ int net_set_blocking(NETSOCKET sock); /* Function: net_errno DOCTODO: serp */ int net_errno(); /* Function: net_would_block DOCTODO: serp */ int net_would_block(); int net_socket_read_wait(NETSOCKET sock, int time); void mem_debug_dump(IOHANDLE file); void swap_endian(void *data, unsigned elem_size, unsigned num); typedef void (*DBG_LOGGER)(const char *line); void dbg_logger(DBG_LOGGER logger); void dbg_logger_stdout(); void dbg_logger_debugger(); void dbg_logger_file(const char *filename); typedef struct { int allocated; int active_allocations; int total_allocations; } MEMSTATS; const MEMSTATS *mem_stats(); typedef struct { int sent_packets; int sent_bytes; int recv_packets; int recv_bytes; } NETSTATS; void net_stats(NETSTATS *stats); int str_toint(const char *str); float str_tofloat(const char *str); int str_isspace(char c); char str_uppercase(char c); unsigned str_quickhash(const char *str); /* Function: gui_messagebox Display plain OS-dependent message box Parameters: title - title of the message box message - text to display */ void gui_messagebox(const char *title, const char *message); /* Function: str_utf8_rewind Moves a cursor backwards in an utf8 string Parameters: str - utf8 string cursor - position in the string Returns: New cursor position. Remarks: - Won't move the cursor less then 0 */ int str_utf8_rewind(const char *str, int cursor); /* Function: str_utf8_forward Moves a cursor forwards in an utf8 string Parameters: str - utf8 string cursor - position in the string Returns: New cursor position. Remarks: - Won't move the cursor beyond the zero termination marker */ int str_utf8_forward(const char *str, int cursor); /* Function: str_utf8_decode Decodes an utf8 character Parameters: ptr - pointer to an utf8 string. this pointer will be moved forward Returns: Unicode value for the character. -1 for invalid characters and 0 for end of string. Remarks: - This function will also move the pointer forward. */ int str_utf8_decode(const char **ptr); /* Function: str_utf8_encode Encode an utf8 character Parameters: ptr - Pointer to a buffer that should recive the data. Should be able to hold at least 4 bytes. Returns: Number of bytes put into the buffer. Remarks: - Does not do zero termination of the string. */ int str_utf8_encode(char *ptr, int chr); /* Function: str_utf8_check Checks if a strings contains just valid utf8 characters. Parameters: str - Pointer to a possible utf8 string. Returns: 0 - invalid characters found. 1 - only valid characters found. Remarks: - The string is treated as zero-terminated utf8 string. */ int str_utf8_check(const char *str); #ifdef __cplusplus } #endif #endif ================================================ FILE: server/obj/.gitignore ================================================ *.o ================================================ FILE: server/src/argparse.c ================================================ #include "argparse.h" #if defined(__cplusplus) extern "C" { #endif #define OPT_UNSET 1 static const char * prefix_skip(const char *str, const char *prefix) { size_t len = strlen(prefix); return strncmp(str, prefix, len) ? NULL : str + len; } int prefix_cmp(const char *str, const char *prefix) { for (;; str++, prefix++) if (!*prefix) return 0; else if (*str != *prefix) return (unsigned char)*prefix - (unsigned char)*str; } static void argparse_error(struct argparse *this_, const struct argparse_option *opt, const char *reason) { if (!strncmp(this_->argv[0], "--", 2)) { fprintf(stderr, "error: option `%s` %s\n", opt->long_name, reason); exit(-1); } else { fprintf(stderr, "error: option `%c` %s\n", opt->short_name, reason); exit(-1); } } static int argparse_getvalue(struct argparse *this_, const struct argparse_option *opt, int flags) { const char *s = NULL; if (!opt->value) goto skipped; switch (opt->type) { case ARGPARSE_OPT_BOOLEAN: if (flags & OPT_UNSET) { *(int *)opt->value = *(int *)opt->value - 1; } else { *(int *)opt->value = *(int *)opt->value + 1; } if (*(int *)opt->value < 0) { *(int *)opt->value = 0; } break; case ARGPARSE_OPT_BIT: if (flags & OPT_UNSET) { *(int *)opt->value &= ~opt->data; } else { *(int *)opt->value |= opt->data; } break; case ARGPARSE_OPT_STRING: if (this_->optvalue) { *(const char **)opt->value = this_->optvalue; this_->optvalue = NULL; } else if (this_->argc > 1) { this_->argc--; *(const char **)opt->value = *++this_->argv; } else { argparse_error(this_, opt, "requires a value"); } break; case ARGPARSE_OPT_INTEGER: if (this_->optvalue) { *(int *)opt->value = strtol(this_->optvalue, (char **)&s, 0); this_->optvalue = NULL; } else if (this_->argc > 1) { this_->argc--; *(int *)opt->value = strtol(*++this_->argv, (char **)&s, 0); } else { argparse_error(this_, opt, "requires a value"); } if (*s) argparse_error(this_, opt, "expects a numerical value"); break; default: assert(0); } skipped: if (opt->callback) { return opt->callback(this_, opt); } return 0; } static void argparse_options_check(const struct argparse_option *options) { for (; options->type != ARGPARSE_OPT_END; options++) { switch (options->type) { case ARGPARSE_OPT_END: case ARGPARSE_OPT_BOOLEAN: case ARGPARSE_OPT_BIT: case ARGPARSE_OPT_INTEGER: case ARGPARSE_OPT_STRING: continue; default: fprintf(stderr, "wrong option type: %d", options->type); break; } } } static int argparse_short_opt(struct argparse *this_, const struct argparse_option *options) { for (; options->type != ARGPARSE_OPT_END; options++) { if (options->short_name == *this_->optvalue) { this_->optvalue = this_->optvalue[1] ? this_->optvalue + 1 : NULL; return argparse_getvalue(this_, options, 0); } } return -2; } static int argparse_long_opt(struct argparse *this_, const struct argparse_option *options) { for (; options->type != ARGPARSE_OPT_END; options++) { const char *rest; int opt_flags = 0; if (!options->long_name) continue; rest = prefix_skip(this_->argv[0] + 2, options->long_name); if (!rest) { // Negation allowed? if (options->flags & OPT_NONEG) { continue; } // Only boolean/bit allow negation. if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != ARGPARSE_OPT_BIT) { continue; } if (!prefix_cmp(this_->argv[0] + 2, "no-")) { rest = prefix_skip(this_->argv[0] + 2 + 3, options->long_name); if (!rest) continue; opt_flags |= OPT_UNSET; } else { continue; } } if (*rest) { if (*rest != '=') continue; this_->optvalue = rest + 1; } return argparse_getvalue(this_, options, opt_flags); } return -2; } int argparse_init(struct argparse *this_, struct argparse_option *options, const char *usage, int flags) { memset(this_, 0, sizeof(*this_)); this_->options = options; this_->usage = usage; this_->flags = flags; return 0; } int argparse_parse(struct argparse *this_, int argc, const char **argv) { this_->argc = argc - 1; this_->argv = argv + 1; this_->out = argv; argparse_options_check(this_->options); for (; this_->argc; this_->argc--, this_->argv++) { const char *arg = this_->argv[0]; if (arg[0] != '-' || !arg[1]) { if (this_->flags & ARGPARSE_STOP_AT_NON_OPTION) { goto end; } // if it's not option or is a single char '-', copy verbatimly this_->out[this_->cpidx++] = this_->argv[0]; continue; } // short option if (arg[1] != '-') { this_->optvalue = arg + 1; switch (argparse_short_opt(this_, this_->options)) { case -1: break; case -2: goto unknown; } while (this_->optvalue) { switch (argparse_short_opt(this_, this_->options)) { case -1: break; case -2: goto unknown; } } continue; } // if '--' presents if (!arg[2]) { this_->argc--; this_->argv++; break; } // long option switch (argparse_long_opt(this_, this_->options)) { case -1: break; case -2: goto unknown; } continue; unknown: fprintf(stderr, "error: unknown option `%s`\n", this_->argv[0]); argparse_usage(this_); exit(0); } end: memmove(this_->out + this_->cpidx, this_->argv, this_->argc * sizeof(*this_->out)); this_->out[this_->cpidx + this_->argc] = NULL; return this_->cpidx + this_->argc; } void argparse_usage(struct argparse *this_) { fprintf(stdout, "Usage: %s\n", this_->usage); fputc('\n', stdout); const struct argparse_option *options; // figure out best width size_t usage_opts_width = 0; size_t len; options = this_->options; for (; options->type != ARGPARSE_OPT_END; options++) { len = 0; if ((options)->short_name) { len += 2; } if ((options)->short_name && (options)->long_name) { len += 2; // separator ", " } if ((options)->long_name) { len += strlen((options)->long_name) + 2; } if (options->type == ARGPARSE_OPT_INTEGER) { len += strlen("="); } else if (options->type == ARGPARSE_OPT_STRING) { len += strlen("="); } len = ceil((float)len / 4) * 4; if (usage_opts_width < len) { usage_opts_width = len; } } usage_opts_width += 4; // 4 spaces prefix options = this_->options; for (; options->type != ARGPARSE_OPT_END; options++) { size_t pos; int pad; pos = fprintf(stdout, " "); if (options->short_name) { pos += fprintf(stdout, "-%c", options->short_name); } if (options->long_name && options->short_name) { pos += fprintf(stdout, ", "); } if (options->long_name) { pos += fprintf(stdout, "--%s", options->long_name); } if (options->type == ARGPARSE_OPT_INTEGER) { pos += fprintf(stdout, "="); } else if (options->type == ARGPARSE_OPT_STRING) { pos += fprintf(stdout, "="); } if (pos <= usage_opts_width) { pad = usage_opts_width - pos; } else { fputc('\n', stdout); pad = usage_opts_width; } fprintf(stdout, "%*s%s\n", pad + 2, "", options->help); } } int argparse_help_cb(struct argparse *this_, const struct argparse_option *option) { (void)option; argparse_usage(this_); exit(0); return 0; } #if defined(__cplusplus) } #endif ================================================ FILE: server/src/json.c ================================================ /* vim: set et ts=3 sw=3 sts=3 ft=c: * * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. * https://github.com/udp/json-parser * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "json.h" #ifdef _MSC_VER #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #endif #ifdef __cplusplus const struct _json_value json_value_none; /* zero-d by ctor */ #else const struct _json_value json_value_none = { 0 }; #endif #include #include #include #include typedef unsigned short json_uchar; static unsigned char hex_value (json_char c) { if (isdigit(c)) return c - '0'; switch (c) { case 'a': case 'A': return 0x0A; case 'b': case 'B': return 0x0B; case 'c': case 'C': return 0x0C; case 'd': case 'D': return 0x0D; case 'e': case 'E': return 0x0E; case 'f': case 'F': return 0x0F; default: return 0xFF; } } typedef struct { unsigned long used_memory; unsigned int uint_max; unsigned long ulong_max; json_settings settings; int first_pass; } json_state; static void * default_alloc (size_t size, int zero, void * user_data) { return zero ? calloc (1, size) : malloc (size); } static void default_free (void * ptr, void * user_data) { free (ptr); } static void * json_alloc (json_state * state, unsigned long size, int zero) { if ((state->ulong_max - state->used_memory) < size) return 0; if (state->settings.max_memory && (state->used_memory += size) > state->settings.max_memory) { return 0; } return state->settings.mem_alloc (size, zero, state->settings.user_data); } static int new_value (json_state * state, json_value ** top, json_value ** root, json_value ** alloc, json_type type) { json_value * value; int values_size; if (!state->first_pass) { value = *top = *alloc; *alloc = (*alloc)->_reserved.next_alloc; if (!*root) *root = value; switch (value->type) { case json_array: if (! (value->u.array.values = (json_value **) json_alloc (state, value->u.array.length * sizeof (json_value *), 0)) ) { return 0; } value->u.array.length = 0; break; case json_object: values_size = sizeof (*value->u.object.values) * value->u.object.length; if (! ((*(void **) &value->u.object.values) = json_alloc (state, values_size + ((unsigned long) value->u.object.values), 0)) ) { return 0; } value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size; value->u.object.length = 0; break; case json_string: if (! (value->u.string.ptr = (json_char *) json_alloc (state, (value->u.string.length + 1) * sizeof (json_char), 0)) ) { return 0; } value->u.string.length = 0; break; default: break; }; return 1; } value = (json_value *) json_alloc (state, sizeof (json_value), 1); if (!value) return 0; if (!*root) *root = value; value->type = type; value->parent = *top; if (*alloc) (*alloc)->_reserved.next_alloc = value; *alloc = *top = value; return 1; } #define e_off \ ((int) (i - cur_line_begin)) #define whitespace \ case '\n': ++ cur_line; cur_line_begin = i; \ case ' ': case '\t': case '\r' #define string_add(b) \ do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0); static const long flag_next = 1 << 0, flag_reproc = 1 << 1, flag_need_comma = 1 << 2, flag_seek_value = 1 << 3, flag_escaped = 1 << 4, flag_string = 1 << 5, flag_need_colon = 1 << 6, flag_done = 1 << 7, flag_num_negative = 1 << 8, flag_num_zero = 1 << 9, flag_num_e = 1 << 10, flag_num_e_got_sign = 1 << 11, flag_num_e_negative = 1 << 12, flag_line_comment = 1 << 13, flag_block_comment = 1 << 14; json_value * json_parse_ex (json_settings * settings, const json_char * json, size_t length, char * error_buf) { json_char error [json_error_max]; unsigned int cur_line; const json_char * cur_line_begin, * i, * end; json_value * top, * root, * alloc = 0; json_state state = { 0 }; long flags; long num_digits = 0, num_e = 0; json_int_t num_fraction = 0; /* Skip UTF-8 BOM */ if (length >= 3 && ((unsigned char) json [0]) == 0xEF && ((unsigned char) json [1]) == 0xBB && ((unsigned char) json [2]) == 0xBF) { json += 3; length -= 3; } error[0] = '\0'; end = (json + length); memcpy (&state.settings, settings, sizeof (json_settings)); if (!state.settings.mem_alloc) state.settings.mem_alloc = default_alloc; if (!state.settings.mem_free) state.settings.mem_free = default_free; memset (&state.uint_max, 0xFF, sizeof (state.uint_max)); memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max)); state.uint_max -= 8; /* limit of how much can be added before next check */ state.ulong_max -= 8; for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass) { json_uchar uchar; unsigned char uc_b1, uc_b2, uc_b3, uc_b4; json_char * string = 0; unsigned int string_length = 0; top = root = 0; flags = flag_seek_value; cur_line = 1; cur_line_begin = json; for (i = json ;; ++ i) { json_char b = (i == end ? 0 : *i); if (flags & flag_string) { if (!b) { sprintf (error, "Unexpected EOF in string (at %d:%d)", cur_line, e_off); goto e_failed; } if (string_length > state.uint_max) goto e_overflow; if (flags & flag_escaped) { flags &= ~ flag_escaped; switch (b) { case 'b': string_add ('\b'); break; case 'f': string_add ('\f'); break; case 'n': string_add ('\n'); break; case 'r': string_add ('\r'); break; case 't': string_add ('\t'); break; case 'u': if (end - i < 4 || (uc_b1 = hex_value (*++ i)) == 0xFF || (uc_b2 = hex_value (*++ i)) == 0xFF || (uc_b3 = hex_value (*++ i)) == 0xFF || (uc_b4 = hex_value (*++ i)) == 0xFF) { sprintf (error, "Invalid character value `%c` (at %d:%d)", b, cur_line, e_off); goto e_failed; } uc_b1 = uc_b1 * 16 + uc_b2; uc_b2 = uc_b3 * 16 + uc_b4; uchar = ((json_char) uc_b1) * 256 + uc_b2; if (sizeof (json_char) >= sizeof (json_uchar) || (uc_b1 == 0 && uc_b2 <= 0x7F)) { string_add ((json_char) uchar); break; } if (uchar <= 0x7FF) { if (state.first_pass) string_length += 2; else { string [string_length ++] = 0xC0 | ((uc_b2 & 0xC0) >> 6) | ((uc_b1 & 0x7) << 2); string [string_length ++] = 0x80 | (uc_b2 & 0x3F); } break; } if (state.first_pass) string_length += 3; else { string [string_length ++] = 0xE0 | ((uc_b1 & 0xF0) >> 4); string [string_length ++] = 0x80 | ((uc_b1 & 0xF) << 2) | ((uc_b2 & 0xC0) >> 6); string [string_length ++] = 0x80 | (uc_b2 & 0x3F); } break; default: string_add (b); }; continue; } if (b == '\\') { flags |= flag_escaped; continue; } if (b == '"') { if (!state.first_pass) string [string_length] = 0; flags &= ~ flag_string; string = 0; switch (top->type) { case json_string: top->u.string.length = string_length; flags |= flag_next; break; case json_object: if (state.first_pass) (*(json_char **) &top->u.object.values) += string_length + 1; else { top->u.object.values [top->u.object.length].name = (json_char *) top->_reserved.object_mem; top->u.object.values [top->u.object.length].name_length = string_length; (*(json_char **) &top->_reserved.object_mem) += string_length + 1; } flags |= flag_seek_value | flag_need_colon; continue; default: break; }; } else { string_add (b); continue; } } if (state.settings.settings & json_enable_comments) { if (flags & (flag_line_comment | flag_block_comment)) { if (flags & flag_line_comment) { if (b == '\r' || b == '\n' || !b) { flags &= ~ flag_line_comment; -- i; /* so null can be reproc'd */ } continue; } if (flags & flag_block_comment) { if (!b) { sprintf (error, "%d:%d: Unexpected EOF in block comment", cur_line, e_off); goto e_failed; } if (b == '*' && i < (end - 1) && i [1] == '/') { flags &= ~ flag_block_comment; ++ i; /* skip closing sequence */ } continue; } } else if (b == '/') { if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object) { sprintf (error, "%d:%d: Comment not allowed here", cur_line, e_off); goto e_failed; } if (++ i == end) { sprintf (error, "%d:%d: EOF unexpected", cur_line, e_off); goto e_failed; } switch (b = *i) { case '/': flags |= flag_line_comment; continue; case '*': flags |= flag_block_comment; continue; default: sprintf (error, "%d:%d: Unexpected `%c` in comment opening sequence", cur_line, e_off, b); goto e_failed; }; } } if (flags & flag_done) { if (!b) break; switch (b) { whitespace: continue; default: sprintf (error, "%d:%d: Trailing garbage: `%c`", cur_line, e_off, b); goto e_failed; }; } if (flags & flag_seek_value) { switch (b) { whitespace: continue; case ']': if (top->type == json_array) flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next; else { sprintf (error, "%d:%d: Unexpected ]", cur_line, e_off); goto e_failed; } break; default: if (flags & flag_need_comma) { if (b == ',') { flags &= ~ flag_need_comma; continue; } else { sprintf (error, "%d:%d: Expected , before %c", cur_line, e_off, b); goto e_failed; } } if (flags & flag_need_colon) { if (b == ':') { flags &= ~ flag_need_colon; continue; } else { sprintf (error, "%d:%d: Expected : before %c", cur_line, e_off, b); goto e_failed; } } flags &= ~ flag_seek_value; switch (b) { case '{': if (!new_value (&state, &top, &root, &alloc, json_object)) goto e_alloc_failure; continue; case '[': if (!new_value (&state, &top, &root, &alloc, json_array)) goto e_alloc_failure; flags |= flag_seek_value; continue; case '"': if (!new_value (&state, &top, &root, &alloc, json_string)) goto e_alloc_failure; flags |= flag_string; string = top->u.string.ptr; string_length = 0; continue; case 't': if ((end - i) < 3 || *(++ i) != 'r' || *(++ i) != 'u' || *(++ i) != 'e') goto e_unknown_value; if (!new_value (&state, &top, &root, &alloc, json_boolean)) goto e_alloc_failure; top->u.boolean = 1; flags |= flag_next; break; case 'f': if ((end - i) < 4 || *(++ i) != 'a' || *(++ i) != 'l' || *(++ i) != 's' || *(++ i) != 'e') goto e_unknown_value; if (!new_value (&state, &top, &root, &alloc, json_boolean)) goto e_alloc_failure; flags |= flag_next; break; case 'n': if ((end - i) < 3 || *(++ i) != 'u' || *(++ i) != 'l' || *(++ i) != 'l') goto e_unknown_value; if (!new_value (&state, &top, &root, &alloc, json_null)) goto e_alloc_failure; flags |= flag_next; break; default: if (isdigit (b) || b == '-') { if (!new_value (&state, &top, &root, &alloc, json_integer)) goto e_alloc_failure; if (!state.first_pass) { while (isdigit (b) || b == '+' || b == '-' || b == 'e' || b == 'E' || b == '.') { if ( (++ i) == end) { b = 0; break; } b = *i; } flags |= flag_next | flag_reproc; break; } flags &= ~ (flag_num_negative | flag_num_e | flag_num_e_got_sign | flag_num_e_negative | flag_num_zero); num_digits = 0; num_fraction = 0; num_e = 0; if (b != '-') { flags |= flag_reproc; break; } flags |= flag_num_negative; continue; } else { sprintf (error, "%d:%d: Unexpected %c when seeking value", cur_line, e_off, b); goto e_failed; } }; }; } else { switch (top->type) { case json_object: switch (b) { whitespace: continue; case '"': if (flags & flag_need_comma) { sprintf (error, "%d:%d: Expected , before \"", cur_line, e_off); goto e_failed; } flags |= flag_string; string = (json_char *) top->_reserved.object_mem; string_length = 0; break; case '}': flags = (flags & ~ flag_need_comma) | flag_next; break; case ',': if (flags & flag_need_comma) { flags &= ~ flag_need_comma; break; } default: sprintf (error, "%d:%d: Unexpected `%c` in object", cur_line, e_off, b); goto e_failed; }; break; case json_integer: case json_double: if (isdigit (b)) { ++ num_digits; if (top->type == json_integer || flags & flag_num_e) { if (! (flags & flag_num_e)) { if (flags & flag_num_zero) { sprintf (error, "%d:%d: Unexpected `0` before `%c`", cur_line, e_off, b); goto e_failed; } if (num_digits == 1 && b == '0') flags |= flag_num_zero; } else { flags |= flag_num_e_got_sign; num_e = (num_e * 10) + (b - '0'); continue; } top->u.integer = (top->u.integer * 10) + (b - '0'); continue; } num_fraction = (num_fraction * 10) + (b - '0'); continue; } if (b == '+' || b == '-') { if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign)) { flags |= flag_num_e_got_sign; if (b == '-') flags |= flag_num_e_negative; continue; } } else if (b == '.' && top->type == json_integer) { if (!num_digits) { sprintf (error, "%d:%d: Expected digit before `.`", cur_line, e_off); goto e_failed; } top->type = json_double; top->u.dbl = (double) top->u.integer; num_digits = 0; continue; } if (! (flags & flag_num_e)) { if (top->type == json_double) { if (!num_digits) { sprintf (error, "%d:%d: Expected digit after `.`", cur_line, e_off); goto e_failed; } top->u.dbl += ((double) num_fraction) / (pow (10, (double) num_digits)); } if (b == 'e' || b == 'E') { flags |= flag_num_e; if (top->type == json_integer) { top->type = json_double; top->u.dbl = (double) top->u.integer; } num_digits = 0; flags &= ~ flag_num_zero; continue; } } else { if (!num_digits) { sprintf (error, "%d:%d: Expected digit after `e`", cur_line, e_off); goto e_failed; } top->u.dbl *= pow (10, (double) (flags & flag_num_e_negative ? - num_e : num_e)); } if (flags & flag_num_negative) { if (top->type == json_integer) top->u.integer = - top->u.integer; else top->u.dbl = - top->u.dbl; } flags |= flag_next | flag_reproc; break; default: break; }; } if (flags & flag_reproc) { flags &= ~ flag_reproc; -- i; } if (flags & flag_next) { flags = (flags & ~ flag_next) | flag_need_comma; if (!top->parent) { /* root value done */ flags |= flag_done; continue; } if (top->parent->type == json_array) flags |= flag_seek_value; if (!state.first_pass) { json_value * parent = top->parent; switch (parent->type) { case json_object: parent->u.object.values [parent->u.object.length].value = top; break; case json_array: parent->u.array.values [parent->u.array.length] = top; break; default: break; }; } if ( (++ top->parent->u.array.length) > state.uint_max) goto e_overflow; top = top->parent; continue; } } alloc = root; } return root; e_unknown_value: sprintf (error, "%d:%d: Unknown value", cur_line, e_off); goto e_failed; e_alloc_failure: strcpy (error, "Memory allocation failure"); goto e_failed; e_overflow: sprintf (error, "%d:%d: Too long (caught overflow)", cur_line, e_off); goto e_failed; e_failed: if (error_buf) { if (*error) strcpy (error_buf, error); else strcpy (error_buf, "Unknown error"); } if (state.first_pass) alloc = root; while (alloc) { top = alloc->_reserved.next_alloc; state.settings.mem_free (alloc, state.settings.user_data); alloc = top; } if (!state.first_pass) json_value_free_ex (&state.settings, root); return 0; } json_value * json_parse (const json_char * json, size_t length) { json_settings settings = { 0 }; return json_parse_ex (&settings, json, length, 0); } void json_value_free_ex (json_settings * settings, json_value * value) { json_value * cur_value; if (!value) return; value->parent = 0; while (value) { switch (value->type) { case json_array: if (!value->u.array.length) { settings->mem_free (value->u.array.values, settings->user_data); break; } value = value->u.array.values [-- value->u.array.length]; continue; case json_object: if (!value->u.object.length) { settings->mem_free (value->u.object.values, settings->user_data); break; } value = value->u.object.values [-- value->u.object.length].value; continue; case json_string: settings->mem_free (value->u.string.ptr, settings->user_data); break; default: break; }; cur_value = value; value = value->parent; settings->mem_free (cur_value, settings->user_data); } } void json_value_free (json_value * value) { json_settings settings = { 0 }; settings.mem_free = default_free; json_value_free_ex (&settings, value); } ================================================ FILE: server/src/main.cpp ================================================ #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include "server.h" #include "main.h" #if defined(CONF_FAMILY_UNIX) #include #endif #ifndef PRId64 #define PRId64 "I64d" #endif static volatile int gs_Running = 1; static volatile int gs_ReloadConfig = 0; static void ExitFunc(int Signal) { printf("[EXIT] Caught signal %d\n", Signal); gs_Running = 0; } static void ReloadFunc(int Signal) { printf("[RELOAD] Caught signal %d\n", Signal); gs_ReloadConfig = 1; } CConfig::CConfig() { // Initialize to default values m_Verbose = false; // -v, --verbose str_copy(m_aConfigFile, "config.json", sizeof(m_aConfigFile)); // -c, --config str_copy(m_aWebDir, "../web/", sizeof(m_aJSONFile)); // -d, --web-dir str_copy(m_aTemplateFile, "template.html", sizeof(m_aTemplateFile)); str_copy(m_aJSONFile, "json/stats.json", sizeof(m_aJSONFile)); str_copy(m_aBindAddr, "", sizeof(m_aBindAddr)); // -b, --bind m_Port = 35601; // -p, --port } CMain::CMain(CConfig Config) : m_Config(Config) { mem_zero(m_aClients, sizeof(m_aClients)); for(int i = 0; i < NET_MAX_CLIENTS; i++) m_aClients[i].m_ClientNetID = -1; } CMain::CClient *CMain::ClientNet(int ClientNetID) { if(ClientNetID < 0 || ClientNetID >= NET_MAX_CLIENTS) return 0; for(int i = 0; i < NET_MAX_CLIENTS; i++) { if(Client(i)->m_ClientNetID == ClientNetID) return Client(i); } return 0; } int CMain::ClientNetToClient(int ClientNetID) { if(ClientNetID < 0 || ClientNetID >= NET_MAX_CLIENTS) return -1; for(int i = 0; i < NET_MAX_CLIENTS; i++) { if(Client(i)->m_ClientNetID == ClientNetID) return i; } return -1; } void CMain::OnNewClient(int ClientNetID, int ClientID) { dbg_msg("main", "OnNewClient(ncid=%d, cid=%d)", ClientNetID, ClientID); Client(ClientID)->m_ClientNetID = ClientNetID; Client(ClientID)->m_ClientNetType = m_Server.Network()->ClientAddr(ClientNetID)->type; Client(ClientID)->m_TimeConnected = time_get(); Client(ClientID)->m_Connected = true; if(Client(ClientID)->m_ClientNetType == NETTYPE_IPV4) Client(ClientID)->m_Stats.m_Online4 = true; else if(Client(ClientID)->m_ClientNetType == NETTYPE_IPV6) Client(ClientID)->m_Stats.m_Online6 = true; } void CMain::OnDelClient(int ClientNetID) { int ClientID = ClientNetToClient(ClientNetID); dbg_msg("main", "OnDelClient(ncid=%d, cid=%d)", ClientNetID, ClientID); if(ClientID >= 0 && ClientID < NET_MAX_CLIENTS) { Client(ClientID)->m_Connected = false; Client(ClientID)->m_ClientNetID = -1; Client(ClientID)->m_ClientNetType = NETTYPE_INVALID; mem_zero(&Client(ClientID)->m_Stats, sizeof(CClient::CStats)); } } int CMain::HandleMessage(int ClientNetID, char *pMessage) { CClient *pClient = ClientNet(ClientNetID); if(!pClient) return true; if(str_comp_num(pMessage, "update", sizeof("update")-1) == 0) { char *pData = str_skip_whitespaces(&pMessage[sizeof("update")-1]); // parse json data json_settings JsonSettings; mem_zero(&JsonSettings, sizeof(JsonSettings)); char aError[256]; json_value *pJsonData = json_parse_ex(&JsonSettings, pData, strlen(pData), aError); if(!pJsonData) { dbg_msg("main", "JSON Error: %s", aError); if(pClient->m_Stats.m_Pong) m_Server.Network()->Send(ClientNetID, "1"); return 1; } // extract data const json_value &rStart = (*pJsonData); if(rStart["uptime"].type) pClient->m_Stats.m_Uptime = rStart["uptime"].u.integer; if(rStart["load"].type) pClient->m_Stats.m_Load = rStart["load"].u.dbl; if(rStart["network_rx"].type) pClient->m_Stats.m_NetworkRx = rStart["network_rx"].u.integer; if(rStart["network_tx"].type) pClient->m_Stats.m_NetworkTx = rStart["network_tx"].u.integer; if(rStart["network_in"].type) pClient->m_Stats.m_NetworkIN = rStart["network_in"].u.integer; if(rStart["network_out"].type) pClient->m_Stats.m_NetworkOUT = rStart["network_out"].u.integer; if(rStart["memory_total"].type) pClient->m_Stats.m_MemTotal = rStart["memory_total"].u.integer; if(rStart["memory_used"].type) pClient->m_Stats.m_MemUsed = rStart["memory_used"].u.integer; if(rStart["swap_total"].type) pClient->m_Stats.m_SwapTotal = rStart["swap_total"].u.integer; if(rStart["swap_used"].type) pClient->m_Stats.m_SwapUsed = rStart["swap_used"].u.integer; if(rStart["hdd_total"].type) pClient->m_Stats.m_HDDTotal = rStart["hdd_total"].u.integer; if(rStart["hdd_used"].type) pClient->m_Stats.m_HDDUsed = rStart["hdd_used"].u.integer; if(rStart["cpu"].type) pClient->m_Stats.m_CPU = rStart["cpu"].u.dbl; if(rStart["online4"].type && pClient->m_ClientNetType == NETTYPE_IPV6) pClient->m_Stats.m_Online4 = rStart["online4"].u.boolean; if(rStart["online6"].type && pClient->m_ClientNetType == NETTYPE_IPV4) pClient->m_Stats.m_Online6 = rStart["online6"].u.boolean; if(rStart["custom"].type == json_string) str_copy(pClient->m_Stats.m_aCustom, rStart["custom"].u.string.ptr, sizeof(pClient->m_Stats.m_aCustom)); if(m_Config.m_Verbose) { if(rStart["online4"].type) dbg_msg("main", "Online4: %s\nUptime: %" PRId64 "\nLoad: %f\nNetworkRx: %" PRId64 "\nNetworkTx: %" PRId64 "\nNetworkIN: %" PRId64 "\nNetworkOUT: %" PRId64 "\nMemTotal: %" PRId64 "\nMemUsed: %" PRId64 "\nSwapTotal: %" PRId64 "\nSwapUsed: %" PRId64 "\nHDDTotal: %" PRId64 "\nHDDUsed: %" PRId64 "\nCPU: %f\n", rStart["online4"].u.boolean ? "true" : "false", pClient->m_Stats.m_Uptime, pClient->m_Stats.m_Load, pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU); else if(rStart["online6"].type) dbg_msg("main", "Online6: %s\nUptime: %" PRId64 "\nLoad: %f\nNetworkRx: %" PRId64 "\nNetworkTx: %" PRId64 "\nNetworkIN: %" PRId64 "\nNetworkOUT: %" PRId64 "\nMemTotal: %" PRId64 "\nMemUsed: %" PRId64 "\nSwapTotal: %" PRId64 "\nSwapUsed: %" PRId64 "\nHDDTotal: %" PRId64 "\nHDDUsed: %" PRId64 "\nCPU: %f\n", rStart["online6"].u.boolean ? "true" : "false", pClient->m_Stats.m_Uptime, pClient->m_Stats.m_Load, pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU); else dbg_msg("main", "Uptime: %" PRId64 "\nLoad: %f\nNetworkRx: %" PRId64 "\nNetworkTx: %" PRId64 "\nNetworkIN: %" PRId64 "\nNetworkOUT: %" PRId64 "\nMemTotal: %" PRId64 "\nMemUsed: %" PRId64 "\nSwapTotal: %" PRId64 "\nSwapUsed: %" PRId64 "\nHDDTotal: %" PRId64 "\nHDDUsed: %" PRId64 "\nCPU: %f\n", pClient->m_Stats.m_Uptime, pClient->m_Stats.m_Load, pClient->m_Stats.m_NetworkRx, pClient->m_Stats.m_NetworkTx, pClient->m_Stats.m_NetworkIN, pClient->m_Stats.m_NetworkOUT, pClient->m_Stats.m_MemTotal, pClient->m_Stats.m_MemUsed, pClient->m_Stats.m_SwapTotal, pClient->m_Stats.m_SwapUsed, pClient->m_Stats.m_HDDTotal, pClient->m_Stats.m_HDDUsed, pClient->m_Stats.m_CPU); } // clean up json_value_free(pJsonData); if(pClient->m_Stats.m_Pong) m_Server.Network()->Send(ClientNetID, "0"); return 0; } else if(str_comp_num(pMessage, "pong", sizeof("pong")-1) == 0) { char *pData = str_skip_whitespaces(&pMessage[sizeof("pong")-1]); if(!str_comp(pData, "0") || !str_comp(pData, "off")) pClient->m_Stats.m_Pong = false; else if(!str_comp(pData, "1") || !str_comp(pData, "on")) pClient->m_Stats.m_Pong = true; return 0; } if(pClient->m_Stats.m_Pong) m_Server.Network()->Send(ClientNetID, "1"); return 1; } void CMain::JSONUpdateThread(void *pUser) { CJSONUpdateThreadData *m_pJSONUpdateThreadData = (CJSONUpdateThreadData *)pUser; CClient *pClients = m_pJSONUpdateThreadData->pClients; CConfig *pConfig = m_pJSONUpdateThreadData->pConfig; while(gs_Running) { char aFileBuf[2048*NET_MAX_CLIENTS]; char *pBuf = aFileBuf; str_format(pBuf, sizeof(aFileBuf), "{\n\"servers\": [\n"); pBuf += strlen(pBuf); for(int i = 0; i < NET_MAX_CLIENTS; i++) { if(!pClients[i].m_Active || pClients[i].m_Disabled) continue; if(pClients[i].m_Connected) { // Uptime char aUptime[16]; int Days = pClients[i].m_Stats.m_Uptime/60.0/60.0/24.0; if(Days > 0) { if(Days > 1) str_format(aUptime, sizeof(aUptime), "%d 天", Days); else str_format(aUptime, sizeof(aUptime), "%d 天", Days); } else str_format(aUptime, sizeof(aUptime), "%02d:%02d:%02d", (int)(pClients[i].m_Stats.m_Uptime/60.0/60.0), (int)((pClients[i].m_Stats.m_Uptime/60)%60), (int)((pClients[i].m_Stats.m_Uptime)%60)); str_format(pBuf, sizeof(aFileBuf) - (pBuf - aFileBuf), "{ \"name\": \"%s\", \"type\": \"%s\", \"host\": \"%s\", \"location\": \"%s\", \"online4\": %s, \"online6\": %s, \"uptime\": \"%s\", \"load\": %.2f, \"network_rx\": %" PRId64 ", \"network_tx\": %" PRId64 ", \"network_in\": %" PRId64 ", \"network_out\": %" PRId64 ", \"cpu\": %d, \"memory_total\": %" PRId64 ", \"memory_used\": %" PRId64 ", \"swap_total\": %" PRId64 ", \"swap_used\": %" PRId64 ", \"hdd_total\": %" PRId64 ", \"hdd_used\": %" PRId64 ", \"custom\": \"%s\" },\n", pClients[i].m_aName, pClients[i].m_aType, pClients[i].m_aHost, pClients[i].m_aLocation, pClients[i].m_Stats.m_Online4 ? "true" : "false", pClients[i].m_Stats.m_Online6 ? "true" : "false", aUptime, pClients[i].m_Stats.m_Load, pClients[i].m_Stats.m_NetworkRx, pClients[i].m_Stats.m_NetworkTx, pClients[i].m_Stats.m_NetworkIN, pClients[i].m_Stats.m_NetworkOUT, (int)pClients[i].m_Stats.m_CPU, pClients[i].m_Stats.m_MemTotal, pClients[i].m_Stats.m_MemUsed, pClients[i].m_Stats.m_SwapTotal, pClients[i].m_Stats.m_SwapUsed, pClients[i].m_Stats.m_HDDTotal, pClients[i].m_Stats.m_HDDUsed, pClients[i].m_Stats.m_aCustom); pBuf += strlen(pBuf); } else { str_format(pBuf, sizeof(aFileBuf) - (pBuf - aFileBuf), "{ \"name\": \"%s\", \"type\": \"%s\", \"host\": \"%s\", \"location\": \"%s\", \"online4\": false, \"online6\": false },\n", pClients[i].m_aName, pClients[i].m_aType, pClients[i].m_aHost, pClients[i].m_aLocation); pBuf += strlen(pBuf); } } if(!m_pJSONUpdateThreadData->m_ReloadRequired) str_format(pBuf - 2, sizeof(aFileBuf) - (pBuf - aFileBuf), "\n],\n\"updated\": \"%lld\"\n}", (long long)time(/*ago*/0)); else { str_format(pBuf - 2, sizeof(aFileBuf) - (pBuf - aFileBuf), "\n],\n\"updated\": \"%lld\",\n\"reload\": true\n}", (long long)time(/*ago*/0)); m_pJSONUpdateThreadData->m_ReloadRequired--; } pBuf += strlen(pBuf); char aJSONFileTmp[1024]; str_format(aJSONFileTmp, sizeof(aJSONFileTmp), "%s~", pConfig->m_aJSONFile); IOHANDLE File = io_open(aJSONFileTmp, IOFLAG_WRITE); if(!File) { dbg_msg("main", "Couldn't open %s", aJSONFileTmp); exit(1); } io_write(File, aFileBuf, (pBuf - aFileBuf)); io_flush(File); io_close(File); fs_rename(aJSONFileTmp, pConfig->m_aJSONFile); thread_sleep(1000); } fs_remove(pConfig->m_aJSONFile); } int CMain::ReadConfig() { // read and parse config IOHANDLE File = io_open(m_Config.m_aConfigFile, IOFLAG_READ); if(!File) { dbg_msg("main", "Couldn't open %s", m_Config.m_aConfigFile); return 1; } int FileSize = (int)io_length(File); char *pFileData = (char *)mem_alloc(FileSize + 1, 1); io_read(File, pFileData, FileSize); pFileData[FileSize] = 0; io_close(File); // parse json data json_settings JsonSettings; mem_zero(&JsonSettings, sizeof(JsonSettings)); char aError[256]; json_value *pJsonData = json_parse_ex(&JsonSettings, pFileData, strlen(pFileData), aError); if(!pJsonData) { dbg_msg("main", "JSON Error in file %s: %s", m_Config.m_aConfigFile, aError); mem_free(pFileData); return 1; } // reset clients for(int i = 0; i < NET_MAX_CLIENTS; i++) { if(!Client(i)->m_Active || !Client(i)->m_Connected) continue; m_Server.Network()->Drop(Client(i)->m_ClientNetID, "Server reloading..."); } mem_zero(m_aClients, sizeof(m_aClients)); for(int i = 0; i < NET_MAX_CLIENTS; i++) m_aClients[i].m_ClientNetID = -1; // extract data int ID = 0; const json_value &rStart = (*pJsonData)["servers"]; if(rStart.type == json_array) { for(unsigned i = 0; i < rStart.u.array.length; i++) { if(ID < 0 || ID >= NET_MAX_CLIENTS) continue; Client(ID)->m_Active = true; Client(ID)->m_Disabled = rStart[i]["disabled"].u.boolean; str_copy(Client(ID)->m_aName, rStart[i]["name"].u.string.ptr, sizeof(Client(ID)->m_aName)); str_copy(Client(ID)->m_aUsername, rStart[i]["username"].u.string.ptr, sizeof(Client(ID)->m_aUsername)); str_copy(Client(ID)->m_aType, rStart[i]["type"].u.string.ptr, sizeof(Client(ID)->m_aType)); str_copy(Client(ID)->m_aHost, rStart[i]["host"].u.string.ptr, sizeof(Client(ID)->m_aHost)); str_copy(Client(ID)->m_aLocation, rStart[i]["location"].u.string.ptr, sizeof(Client(ID)->m_aLocation)); str_copy(Client(ID)->m_aPassword, rStart[i]["password"].u.string.ptr, sizeof(Client(ID)->m_aPassword)); if(m_Config.m_Verbose) { if(Client(ID)->m_Disabled) dbg_msg("main", "[#%d: Name: \"%s\", Username: \"%s\", Type: \"%s\", Host: \"%s\", Location: \"%s\", Password: \"%s\"]", ID, Client(ID)->m_aName, Client(ID)->m_aUsername, Client(ID)->m_aType, Client(ID)->m_aHost, Client(ID)->m_aLocation, Client(ID)->m_aPassword); else dbg_msg("main", "#%d: Name: \"%s\", Username: \"%s\", Type: \"%s\", Host: \"%s\", Location: \"%s\", Password: \"%s\"", ID, Client(ID)->m_aName, Client(ID)->m_aUsername, Client(ID)->m_aType, Client(ID)->m_aHost, Client(ID)->m_aLocation, Client(ID)->m_aPassword); } ID++; } } // clean up json_value_free(pJsonData); mem_free(pFileData); // tell clients to reload the page m_JSONUpdateThreadData.m_ReloadRequired = 2; return 0; } int CMain::Run() { if(m_Server.Init(this, m_Config.m_aBindAddr, m_Config.m_Port)) return 1; if(ReadConfig()) return 1; // Start JSON Update Thread m_JSONUpdateThreadData.m_ReloadRequired = 2; m_JSONUpdateThreadData.pClients = m_aClients; m_JSONUpdateThreadData.pConfig = &m_Config; void *LoadThread = thread_create(JSONUpdateThread, &m_JSONUpdateThreadData); //thread_detach(LoadThread); while(gs_Running) { if(gs_ReloadConfig) { if(ReadConfig()) return 1; m_Server.NetBan()->UnbanAll(); gs_ReloadConfig = 0; } m_Server.Update(); // wait for incomming data net_socket_read_wait(*m_Server.Network()->Socket(), 10); } dbg_msg("server", "Closing."); m_Server.Network()->Close(); thread_wait(LoadThread); return 0; } int main(int argc, const char *argv[]) { int RetVal; dbg_logger_stdout(); #if defined(CONF_FAMILY_UNIX) signal(SIGINT, ExitFunc); signal(SIGTERM, ExitFunc); signal(SIGQUIT, ExitFunc); signal(SIGHUP, ReloadFunc); #endif char aUsage[128]; CConfig Config; str_format(aUsage, sizeof(aUsage), "%s [options]", argv[0]); const char *pConfigFile = 0; const char *pWebDir = 0; const char *pBindAddr = 0; struct argparse_option aOptions[] = { OPT_HELP(), OPT_BOOLEAN('v', "verbose", &Config.m_Verbose, "Verbose output", 0), OPT_STRING('c', "config", &pConfigFile, "Config file to use", 0), OPT_STRING('d', "web-dir", &pWebDir, "Location of the web directory", 0), OPT_STRING('b', "bind", &pBindAddr, "Bind to address", 0), OPT_INTEGER('p', "port", &Config.m_Port, "Listen on port", 0), OPT_END(), }; struct argparse Argparse; argparse_init(&Argparse, aOptions, aUsage, 0); argc = argparse_parse(&Argparse, argc, argv); if(pConfigFile) str_copy(Config.m_aConfigFile, pConfigFile, sizeof(Config.m_aConfigFile)); if(pWebDir) str_copy(Config.m_aWebDir, pWebDir, sizeof(Config.m_aWebDir)); if(pBindAddr) str_copy(Config.m_aBindAddr, pBindAddr, sizeof(Config.m_aBindAddr)); if(Config.m_aWebDir[strlen(Config.m_aWebDir)-1] != '/') str_append(Config.m_aWebDir, "/", sizeof(Config.m_aWebDir)); if(!fs_is_dir(Config.m_aWebDir)) { dbg_msg("main", "ERROR: Can't find web directory: %s", Config.m_aWebDir); return 1; } char aTmp[1024]; str_format(aTmp, sizeof(aTmp), "%s%s", Config.m_aWebDir, Config.m_aJSONFile); str_copy(Config.m_aJSONFile, aTmp, sizeof(Config.m_aJSONFile)); CMain Main(Config); RetVal = Main.Run(); return RetVal; } ================================================ FILE: server/src/main.h ================================================ #ifndef MAIN_H #define MAIN_H #include #include "server.h" class CConfig { public: bool m_Verbose; char m_aConfigFile[1024]; char m_aWebDir[1024]; char m_aTemplateFile[1024]; char m_aJSONFile[1024]; char m_aBindAddr[256]; int m_Port; CConfig(); }; class CMain { CConfig m_Config; CServer m_Server; struct CClient { bool m_Active; bool m_Disabled; bool m_Connected; int m_ClientNetID; int m_ClientNetType; char m_aUsername[128]; char m_aName[128]; char m_aType[128]; char m_aHost[128]; char m_aLocation[128]; char m_aPassword[128]; int64 m_TimeConnected; int64 m_LastUpdate; struct CStats { bool m_Online4; bool m_Online6; int64_t m_Uptime; double m_Load; int64_t m_NetworkRx; int64_t m_NetworkTx; int64_t m_NetworkIN; int64_t m_NetworkOUT; int64_t m_MemTotal; int64_t m_MemUsed; int64_t m_SwapTotal; int64_t m_SwapUsed; int64_t m_HDDTotal; int64_t m_HDDUsed; double m_CPU; char m_aCustom[512]; // Options bool m_Pong; } m_Stats; } m_aClients[NET_MAX_CLIENTS]; struct CJSONUpdateThreadData { CClient *pClients; CConfig *pConfig; volatile short m_ReloadRequired; } m_JSONUpdateThreadData; static void JSONUpdateThread(void *pUser); public: CMain(CConfig Config); void OnNewClient(int ClienNettID, int ClientID); void OnDelClient(int ClientNetID); int HandleMessage(int ClientNetID, char *pMessage); int ReadConfig(); int Run(); CClient *Client(int ClientID) { return &m_aClients[ClientID]; } CClient *ClientNet(int ClientNetID); const CConfig *Config() const { return &m_Config; } int ClientNetToClient(int ClientNetID); }; #endif ================================================ FILE: server/src/netban.cpp ================================================ #include #include "netban.h" bool CNetBan::StrAllnum(const char *pStr) { while(*pStr) { if(!(*pStr >= '0' && *pStr <= '9')) return false; pStr++; } return true; } CNetBan::CNetHash::CNetHash(const NETADDR *pAddr) { if(pAddr->type==NETTYPE_IPV4) m_Hash = (pAddr->ip[0]+pAddr->ip[1]+pAddr->ip[2]+pAddr->ip[3])&0xFF; else m_Hash = (pAddr->ip[0]+pAddr->ip[1]+pAddr->ip[2]+pAddr->ip[3]+pAddr->ip[4]+pAddr->ip[5]+pAddr->ip[6]+pAddr->ip[7]+ pAddr->ip[8]+pAddr->ip[9]+pAddr->ip[10]+pAddr->ip[11]+pAddr->ip[12]+pAddr->ip[13]+pAddr->ip[14]+pAddr->ip[15])&0xFF; m_HashIndex = 0; } CNetBan::CNetHash::CNetHash(const CNetRange *pRange) { m_Hash = 0; m_HashIndex = 0; for(int i = 0; pRange->m_LB.ip[i] == pRange->m_UB.ip[i]; ++i) { m_Hash += pRange->m_LB.ip[i]; ++m_HashIndex; } m_Hash &= 0xFF; } int CNetBan::CNetHash::MakeHashArray(const NETADDR *pAddr, CNetHash aHash[17]) { int Length = pAddr->type==NETTYPE_IPV4 ? 4 : 16; aHash[0].m_Hash = 0; aHash[0].m_HashIndex = 0; for(int i = 1, Sum = 0; i <= Length; ++i) { Sum += pAddr->ip[i-1]; aHash[i].m_Hash = Sum&0xFF; aHash[i].m_HashIndex = i%Length; } return Length; } template typename CNetBan::CBan *CNetBan::CBanPool::Add(const T *pData, const CBanInfo *pInfo, const CNetHash *pNetHash) { if(!m_pFirstFree) return 0; // create new ban CBan *pBan = m_pFirstFree; pBan->m_Data = *pData; pBan->m_Info = *pInfo; pBan->m_NetHash = *pNetHash; if(pBan->m_pNext) pBan->m_pNext->m_pPrev = pBan->m_pPrev; if(pBan->m_pPrev) pBan->m_pPrev->m_pNext = pBan->m_pNext; else m_pFirstFree = pBan->m_pNext; // add it to the hash list if(m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]) m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]->m_pHashPrev = pBan; pBan->m_pHashPrev = 0; pBan->m_pHashNext = m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash] = pBan; // insert it into the used list if(m_pFirstUsed) { for(CBan *p = m_pFirstUsed; ; p = p->m_pNext) { if(p->m_Info.m_Expires == CBanInfo::EXPIRES_NEVER || (pInfo->m_Expires != CBanInfo::EXPIRES_NEVER && pInfo->m_Expires <= p->m_Info.m_Expires)) { // insert before pBan->m_pNext = p; pBan->m_pPrev = p->m_pPrev; if(p->m_pPrev) p->m_pPrev->m_pNext = pBan; else m_pFirstUsed = pBan; p->m_pPrev = pBan; break; } if(!p->m_pNext) { // last entry p->m_pNext = pBan; pBan->m_pPrev = p; pBan->m_pNext = 0; break; } } } else { m_pFirstUsed = pBan; pBan->m_pNext = pBan->m_pPrev = 0; } // update ban count ++m_CountUsed; return pBan; } template int CNetBan::CBanPool::Remove(CBan *pBan) { if(pBan == 0) return -1; // remove from hash list if(pBan->m_pHashNext) pBan->m_pHashNext->m_pHashPrev = pBan->m_pHashPrev; if(pBan->m_pHashPrev) pBan->m_pHashPrev->m_pHashNext = pBan->m_pHashNext; else m_paaHashList[pBan->m_NetHash.m_HashIndex][pBan->m_NetHash.m_Hash] = pBan->m_pHashNext; pBan->m_pHashNext = pBan->m_pHashPrev = 0; // remove from used list if(pBan->m_pNext) pBan->m_pNext->m_pPrev = pBan->m_pPrev; if(pBan->m_pPrev) pBan->m_pPrev->m_pNext = pBan->m_pNext; else m_pFirstUsed = pBan->m_pNext; // add to recycle list if(m_pFirstFree) m_pFirstFree->m_pPrev = pBan; pBan->m_pPrev = 0; pBan->m_pNext = m_pFirstFree; m_pFirstFree = pBan; // update ban count --m_CountUsed; return 0; } template void CNetBan::CBanPool::Update(CBan *pBan, const CBanInfo *pInfo) { pBan->m_Info = *pInfo; // remove from used list if(pBan->m_pNext) pBan->m_pNext->m_pPrev = pBan->m_pPrev; if(pBan->m_pPrev) pBan->m_pPrev->m_pNext = pBan->m_pNext; else m_pFirstUsed = pBan->m_pNext; // insert it into the used list if(m_pFirstUsed) { for(CBan *p = m_pFirstUsed; ; p = p->m_pNext) { if(p->m_Info.m_Expires == CBanInfo::EXPIRES_NEVER || (pInfo->m_Expires != CBanInfo::EXPIRES_NEVER && pInfo->m_Expires <= p->m_Info.m_Expires)) { // insert before pBan->m_pNext = p; pBan->m_pPrev = p->m_pPrev; if(p->m_pPrev) p->m_pPrev->m_pNext = pBan; else m_pFirstUsed = pBan; p->m_pPrev = pBan; break; } if(!p->m_pNext) { // last entry p->m_pNext = pBan; pBan->m_pPrev = p; pBan->m_pNext = 0; break; } } } else { m_pFirstUsed = pBan; pBan->m_pNext = pBan->m_pPrev = 0; } } template void CNetBan::CBanPool::Reset() { mem_zero(m_paaHashList, sizeof(m_paaHashList)); mem_zero(m_aBans, sizeof(m_aBans)); m_pFirstUsed = 0; m_CountUsed = 0; for(int i = 1; i < MAX_BANS-1; ++i) { m_aBans[i].m_pNext = &m_aBans[i+1]; m_aBans[i].m_pPrev = &m_aBans[i-1]; } m_aBans[0].m_pNext = &m_aBans[1]; m_aBans[MAX_BANS-1].m_pPrev = &m_aBans[MAX_BANS-2]; m_pFirstFree = &m_aBans[0]; } template typename CNetBan::CBan *CNetBan::CBanPool::Get(int Index) const { if(Index < 0 || Index >= Num()) return 0; for(CNetBan::CBan *pBan = m_pFirstUsed; pBan; pBan = pBan->m_pNext, --Index) { if(Index == 0) return pBan; } return 0; } template void CNetBan::MakeBanInfo(const CBan *pBan, char *pBuf, unsigned BuffSize, int Type) const { if(pBan == 0 || pBuf == 0) { if(BuffSize > 0) pBuf[0] = 0; return; } // build type based part char aBuf[256]; if(Type == MSGTYPE_PLAYER) str_copy(aBuf, "You have been banned", sizeof(aBuf)); else { char aTemp[256]; switch(Type) { case MSGTYPE_LIST: str_format(aBuf, sizeof(aBuf), "%s banned", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp))); break; case MSGTYPE_BANADD: str_format(aBuf, sizeof(aBuf), "banned %s", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp))); break; case MSGTYPE_BANREM: str_format(aBuf, sizeof(aBuf), "unbanned %s", NetToString(&pBan->m_Data, aTemp, sizeof(aTemp))); break; default: aBuf[0] = 0; } } // add info part if(pBan->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER) { int Mins = ((pBan->m_Info.m_Expires-time_timestamp()) + 59) / 60; if(Mins <= 1) str_format(pBuf, BuffSize, "%s for 1 minute (%s)", aBuf, pBan->m_Info.m_aReason); else str_format(pBuf, BuffSize, "%s for %d minutes (%s)", aBuf, Mins, pBan->m_Info.m_aReason); } else str_format(pBuf, BuffSize, "%s for life (%s)", aBuf, pBan->m_Info.m_aReason); } template int CNetBan::Ban(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason) { // do not ban localhost if(NetMatch(pData, &m_LocalhostIPV4) || NetMatch(pData, &m_LocalhostIPV6)) { dbg_msg("net_ban", "ban failed (localhost)"); return -1; } int Stamp = Seconds > 0 ? time_timestamp()+Seconds : CBanInfo::EXPIRES_NEVER; // set up info CBanInfo Info = {0}; Info.m_Expires = Stamp; str_copy(Info.m_aReason, pReason, sizeof(Info.m_aReason)); // check if it already exists CNetHash NetHash(pData); CBan *pBan = pBanPool->Find(pData, &NetHash); if(pBan) { // adjust the ban pBanPool->Update(pBan, &Info); char aBuf[128]; MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_LIST); dbg_msg("net_ban", aBuf); return 1; } // add ban and print result pBan = pBanPool->Add(pData, &Info, &NetHash); if(pBan) { char aBuf[128]; MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_BANADD); dbg_msg("net_ban", aBuf); return 0; } else dbg_msg("net_ban", "ban failed (full banlist)"); return -1; } template int CNetBan::Unban(T *pBanPool, const typename T::CDataType *pData) { CNetHash NetHash(pData); CBan *pBan = pBanPool->Find(pData, &NetHash); if(pBan) { char aBuf[256]; MakeBanInfo(pBan, aBuf, sizeof(aBuf), MSGTYPE_BANREM); pBanPool->Remove(pBan); dbg_msg("net_ban", aBuf); return 0; } else dbg_msg("net_ban", "unban failed (invalid entry)"); return -1; } void CNetBan::Init() { m_BanAddrPool.Reset(); m_BanRangePool.Reset(); net_host_lookup("localhost", &m_LocalhostIPV4, NETTYPE_IPV4); net_host_lookup("localhost", &m_LocalhostIPV6, NETTYPE_IPV6); } void CNetBan::Update() { int Now = time_timestamp(); // remove expired bans char aBuf[256], aNetStr[256]; while(m_BanAddrPool.First() && m_BanAddrPool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanAddrPool.First()->m_Info.m_Expires < Now) { str_format(aBuf, sizeof(aBuf), "ban %s expired", NetToString(&m_BanAddrPool.First()->m_Data, aNetStr, sizeof(aNetStr))); dbg_msg("net_ban", aBuf); m_BanAddrPool.Remove(m_BanAddrPool.First()); } while(m_BanRangePool.First() && m_BanRangePool.First()->m_Info.m_Expires != CBanInfo::EXPIRES_NEVER && m_BanRangePool.First()->m_Info.m_Expires < Now) { str_format(aBuf, sizeof(aBuf), "ban %s expired", NetToString(&m_BanRangePool.First()->m_Data, aNetStr, sizeof(aNetStr))); dbg_msg("net_ban", aBuf); m_BanRangePool.Remove(m_BanRangePool.First()); } } int CNetBan::BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason) { return Ban(&m_BanAddrPool, pAddr, Seconds, pReason); } int CNetBan::BanRange(const CNetRange *pRange, int Seconds, const char *pReason) { if(pRange->IsValid()) return Ban(&m_BanRangePool, pRange, Seconds, pReason); dbg_msg("net_ban", "ban failed (invalid range)"); return -1; } int CNetBan::UnbanByAddr(const NETADDR *pAddr) { return Unban(&m_BanAddrPool, pAddr); } int CNetBan::UnbanByRange(const CNetRange *pRange) { if(pRange->IsValid()) return Unban(&m_BanRangePool, pRange); dbg_msg("net_ban", "ban failed (invalid range)"); return -1; } int CNetBan::UnbanByIndex(int Index) { int Result; char aBuf[256]; CBanAddr *pBan = m_BanAddrPool.Get(Index); if(pBan) { NetToString(&pBan->m_Data, aBuf, sizeof(aBuf)); Result = m_BanAddrPool.Remove(pBan); } else { CBanRange *pBan = m_BanRangePool.Get(Index-m_BanAddrPool.Num()); if(pBan) { NetToString(&pBan->m_Data, aBuf, sizeof(aBuf)); Result = m_BanRangePool.Remove(pBan); } else { dbg_msg("net_ban", "unban failed (invalid index)"); return -1; } } char aMsg[256]; str_format(aMsg, sizeof(aMsg), "unbanned index %i (%s)", Index, aBuf); dbg_msg("net_ban", aMsg); return Result; } void CNetBan::UnbanAll() { m_BanAddrPool.Reset(); m_BanRangePool.Reset(); } bool CNetBan::IsBanned(const NETADDR *pAddr, char *pBuf, unsigned BufferSize) const { CNetHash aHash[17]; int Length = CNetHash::MakeHashArray(pAddr, aHash); // check ban adresses CBanAddr *pBan = m_BanAddrPool.Find(pAddr, &aHash[Length]); if(pBan) { MakeBanInfo(pBan, pBuf, BufferSize, MSGTYPE_PLAYER); return true; } // check ban ranges for(int i = Length-1; i >= 0; --i) { for(CBanRange *pBan = m_BanRangePool.First(&aHash[i]); pBan; pBan = pBan->m_pHashNext) { if(NetMatch(&pBan->m_Data, pAddr, i, Length)) { MakeBanInfo(pBan, pBuf, BufferSize, MSGTYPE_PLAYER); return true; } } } return false; } ================================================ FILE: server/src/netban.h ================================================ #ifndef NETBAN_H #define NETBAN_H #include inline int NetComp(const NETADDR *pAddr1, const NETADDR *pAddr2) { return mem_comp(pAddr1, pAddr2, pAddr1->type==NETTYPE_IPV4 ? 8 : 20); } class CNetRange { public: NETADDR m_LB; NETADDR m_UB; bool IsValid() const { return m_LB.type == m_UB.type && NetComp(&m_LB, &m_UB) < 0; } }; inline int NetComp(const CNetRange *pRange1, const CNetRange *pRange2) { return NetComp(&pRange1->m_LB, &pRange2->m_LB) || NetComp(&pRange1->m_UB, &pRange2->m_UB); } class CNetBan { protected: bool NetMatch(const NETADDR *pAddr1, const NETADDR *pAddr2) const { return NetComp(pAddr1, pAddr2) == 0; } bool NetMatch(const CNetRange *pRange, const NETADDR *pAddr, int Start, int Length) const { return pRange->m_LB.type == pAddr->type && (Start == 0 || mem_comp(&pRange->m_LB.ip[0], &pAddr->ip[0], Start) == 0) && mem_comp(&pRange->m_LB.ip[Start], &pAddr->ip[Start], Length-Start) <= 0 && mem_comp(&pRange->m_UB.ip[Start], &pAddr->ip[Start], Length-Start) >= 0; } bool NetMatch(const CNetRange *pRange, const NETADDR *pAddr) const { return NetMatch(pRange, pAddr, 0, pRange->m_LB.type==NETTYPE_IPV4 ? 4 : 16); } const char *NetToString(const NETADDR *pData, char *pBuffer, unsigned BufferSize) const { char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(pData, aAddrStr, sizeof(aAddrStr), false); str_format(pBuffer, BufferSize, "'%s'", aAddrStr); return pBuffer; } const char *NetToString(const CNetRange *pData, char *pBuffer, unsigned BufferSize) const { char aAddrStr1[NETADDR_MAXSTRSIZE], aAddrStr2[NETADDR_MAXSTRSIZE]; net_addr_str(&pData->m_LB, aAddrStr1, sizeof(aAddrStr1), false); net_addr_str(&pData->m_UB, aAddrStr2, sizeof(aAddrStr2), false); str_format(pBuffer, BufferSize, "'%s' - '%s'", aAddrStr1, aAddrStr2); return pBuffer; } // todo: move? static bool StrAllnum(const char *pStr); class CNetHash { public: int m_Hash; int m_HashIndex; // matching parts for ranges, 0 for addr CNetHash() {} CNetHash(const NETADDR *pAddr); CNetHash(const CNetRange *pRange); static int MakeHashArray(const NETADDR *pAddr, CNetHash aHash[17]); }; struct CBanInfo { enum { EXPIRES_NEVER=-1, REASON_LENGTH=64, }; int m_Expires; char m_aReason[REASON_LENGTH]; }; template struct CBan { T m_Data; CBanInfo m_Info; CNetHash m_NetHash; // hash list CBan *m_pHashNext; CBan *m_pHashPrev; // used or free list CBan *m_pNext; CBan *m_pPrev; }; template class CBanPool { public: typedef T CDataType; CBan *Add(const CDataType *pData, const CBanInfo *pInfo, const CNetHash *pNetHash); int Remove(CBan *pBan); void Update(CBan *pBan, const CBanInfo *pInfo); void Reset(); int Num() const { return m_CountUsed; } bool IsFull() const { return m_CountUsed == MAX_BANS; } CBan *First() const { return m_pFirstUsed; } CBan *First(const CNetHash *pNetHash) const { return m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; } CBan *Find(const CDataType *pData, const CNetHash *pNetHash) const { for(CBan *pBan = m_paaHashList[pNetHash->m_HashIndex][pNetHash->m_Hash]; pBan; pBan = pBan->m_pHashNext) { if(NetComp(&pBan->m_Data, pData) == 0) return pBan; } return 0; } CBan *Get(int Index) const; private: enum { MAX_BANS=1024, }; CBan *m_paaHashList[HashCount][256]; CBan m_aBans[MAX_BANS]; CBan *m_pFirstFree; CBan *m_pFirstUsed; int m_CountUsed; }; typedef CBanPool CBanAddrPool; typedef CBanPool CBanRangePool; typedef CBan CBanAddr; typedef CBan CBanRange; template void MakeBanInfo(const CBan *pBan, char *pBuf, unsigned BuffSize, int Type) const; template int Ban(T *pBanPool, const typename T::CDataType *pData, int Seconds, const char *pReason); template int Unban(T *pBanPool, const typename T::CDataType *pData); CBanAddrPool m_BanAddrPool; CBanRangePool m_BanRangePool; NETADDR m_LocalhostIPV4, m_LocalhostIPV6; public: enum { MSGTYPE_PLAYER=0, MSGTYPE_LIST, MSGTYPE_BANADD, MSGTYPE_BANREM, }; virtual ~CNetBan() {} void Init(); void Update(); virtual int BanAddr(const NETADDR *pAddr, int Seconds, const char *pReason); virtual int BanRange(const CNetRange *pRange, int Seconds, const char *pReason); int UnbanByAddr(const NETADDR *pAddr); int UnbanByRange(const CNetRange *pRange); int UnbanByIndex(int Index); void UnbanAll(); bool IsBanned(const NETADDR *pAddr, char *pBuf, unsigned BufferSize) const; }; #endif ================================================ FILE: server/src/network.cpp ================================================ #include #include "netban.h" #include "network.h" bool CNetwork::Open(NETADDR BindAddr, CNetBan *pNetBan) { // zero out the whole structure mem_zero(this, sizeof(*this)); m_Socket.type = NETTYPE_INVALID; m_Socket.ipv4sock = -1; m_Socket.ipv6sock = -1; m_pNetBan = pNetBan; // open socket m_Socket = net_tcp_create(BindAddr); if(!m_Socket.type) return false; if(net_tcp_listen(m_Socket, NET_MAX_CLIENTS)) return false; net_set_non_blocking(m_Socket); for(int i = 0; i < NET_MAX_CLIENTS; i++) m_aSlots[i].m_Connection.Reset(); return true; } void CNetwork::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser) { m_pfnNewClient = pfnNewClient; m_pfnDelClient = pfnDelClient; m_UserPtr = pUser; } int CNetwork::Close() { for(int i = 0; i < NET_MAX_CLIENTS; i++) m_aSlots[i].m_Connection.Disconnect("Closing connection."); net_tcp_close(m_Socket); return 0; } int CNetwork::Drop(int ClientID, const char *pReason) { if(m_pfnDelClient) m_pfnDelClient(ClientID, pReason, m_UserPtr); m_aSlots[ClientID].m_Connection.Disconnect(pReason); return 0; } int CNetwork::AcceptClient(NETSOCKET Socket, const NETADDR *pAddr) { char aError[256] = { 0 }; int FreeSlot = -1; // look for free slot or multiple client for(int i = 0; i < NET_MAX_CLIENTS; i++) { if(FreeSlot == -1 && m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) FreeSlot = i; if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE) { if(net_addr_comp(pAddr, m_aSlots[i].m_Connection.PeerAddress()) == 0) { str_copy(aError, "Only one client per IP allowed.", sizeof(aError)); break; } } } // accept client if(!aError[0] && FreeSlot != -1) { m_aSlots[FreeSlot].m_Connection.Init(Socket, pAddr); if(m_pfnNewClient) m_pfnNewClient(FreeSlot, m_UserPtr); return 0; } // reject client if(!aError[0]) str_copy(aError, "No free slot available.", sizeof(aError)); net_tcp_send(Socket, aError, str_length(aError)); net_tcp_close(Socket); return -1; } int CNetwork::Update() { NETSOCKET Socket; NETADDR Addr; if(net_tcp_accept(m_Socket, &Socket, &Addr) > 0) { // check if we should just drop the packet char aBuf[128]; if(NetBan() && NetBan()->IsBanned(&Addr, aBuf, sizeof(aBuf))) { // banned, reply with a message and drop net_tcp_send(Socket, aBuf, str_length(aBuf)); net_tcp_close(Socket); } else AcceptClient(Socket, &Addr); } for(int i = 0; i < NET_MAX_CLIENTS; i++) { if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE) m_aSlots[i].m_Connection.Update(); if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) Drop(i, m_aSlots[i].m_Connection.ErrorString()); } return 0; } int CNetwork::Recv(char *pLine, int MaxLength, int *pClientID) { for(int i = 0; i < NET_MAX_CLIENTS; i++) { if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ONLINE && m_aSlots[i].m_Connection.Recv(pLine, MaxLength)) { if(pClientID) *pClientID = i; return 1; } } return 0; } int CNetwork::Send(int ClientID, const char *pLine) { if(m_aSlots[ClientID].m_Connection.State() == NET_CONNSTATE_ONLINE) return m_aSlots[ClientID].m_Connection.Send(pLine); else return -1; } ================================================ FILE: server/src/network.h ================================================ #ifndef NETWORK_H #define NETWORK_H enum { NET_CONNSTATE_OFFLINE=0, NET_CONNSTATE_CONNECT=1, NET_CONNSTATE_PENDING=2, NET_CONNSTATE_ONLINE=3, NET_CONNSTATE_ERROR=4, NET_MAX_PACKETSIZE = 1400, NET_MAX_CLIENTS = 64 }; typedef int (*NETFUNC_DELCLIENT)(int ClientID, const char* pReason, void *pUser); typedef int (*NETFUNC_NEWCLIENT)(int ClientID, void *pUser); class CNetworkClient { private: int m_State; NETADDR m_PeerAddr; NETSOCKET m_Socket; char m_aBuffer[NET_MAX_PACKETSIZE]; int m_BufferOffset; char m_aErrorString[256]; bool m_LineEndingDetected; char m_aLineEnding[3]; public: void Init(NETSOCKET Socket, const NETADDR *pAddr); void Disconnect(const char *pReason); int State() const { return m_State; } const NETADDR *PeerAddress() const { return &m_PeerAddr; } const char *ErrorString() const { return m_aErrorString; } void Reset(); int Update(); int Send(const char *pLine); int Recv(char *pLine, int MaxLength); }; class CNetwork { private: struct CSlot { CNetworkClient m_Connection; }; NETSOCKET m_Socket; class CNetBan *m_pNetBan; CSlot m_aSlots[NET_MAX_CLIENTS]; NETFUNC_NEWCLIENT m_pfnNewClient; NETFUNC_DELCLIENT m_pfnDelClient; void *m_UserPtr; public: void SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); // bool Open(NETADDR BindAddr, CNetBan *pNetBan); int Close(); // int Recv(char *pLine, int MaxLength, int *pClientID = 0); int Send(int ClientID, const char *pLine); int Update(); // int AcceptClient(NETSOCKET Socket, const NETADDR *pAddr); int Drop(int ClientID, const char *pReason); // status requests const NETADDR *ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } const NETSOCKET *Socket() const { return &m_Socket; } class CNetBan *NetBan() const { return m_pNetBan; } }; #endif ================================================ FILE: server/src/network_client.cpp ================================================ #include #include "network.h" void CNetworkClient::Reset() { m_State = NET_CONNSTATE_OFFLINE; mem_zero(&m_PeerAddr, sizeof(m_PeerAddr)); m_aErrorString[0] = 0; m_Socket.type = NETTYPE_INVALID; m_Socket.ipv4sock = -1; m_Socket.ipv6sock = -1; m_aBuffer[0] = 0; m_BufferOffset = 0; m_LineEndingDetected = false; #if defined(CONF_FAMILY_WINDOWS) m_aLineEnding[0] = '\r'; m_aLineEnding[1] = '\n'; m_aLineEnding[2] = 0; #else m_aLineEnding[0] = '\n'; m_aLineEnding[1] = 0; m_aLineEnding[2] = 0; #endif } void CNetworkClient::Init(NETSOCKET Socket, const NETADDR *pAddr) { Reset(); m_Socket = Socket; net_set_non_blocking(m_Socket); m_PeerAddr = *pAddr; m_State = NET_CONNSTATE_ONLINE; } void CNetworkClient::Disconnect(const char *pReason) { if(State() == NET_CONNSTATE_OFFLINE) return; if(pReason && pReason[0]) Send(pReason); net_tcp_close(m_Socket); Reset(); } int CNetworkClient::Update() { if(State() == NET_CONNSTATE_ONLINE) { if((int)(sizeof(m_aBuffer)) <= m_BufferOffset) { m_State = NET_CONNSTATE_ERROR; str_copy(m_aErrorString, "too weak connection (out of buffer)", sizeof(m_aErrorString)); return -1; } int Bytes = net_tcp_recv(m_Socket, m_aBuffer+m_BufferOffset, (int)(sizeof(m_aBuffer))-m_BufferOffset); if(Bytes > 0) { m_BufferOffset += Bytes; } else if(Bytes < 0) { if(net_would_block()) // no data received return 0; m_State = NET_CONNSTATE_ERROR; // error str_copy(m_aErrorString, "connection failure", sizeof(m_aErrorString)); return -1; } else { m_State = NET_CONNSTATE_ERROR; str_copy(m_aErrorString, "remote end closed the connection", sizeof(m_aErrorString)); return -1; } } return 0; } int CNetworkClient::Recv(char *pLine, int MaxLength) { if(State() == NET_CONNSTATE_ONLINE) { if(m_BufferOffset) { // find message start int StartOffset = 0; while(m_aBuffer[StartOffset] == '\r' || m_aBuffer[StartOffset] == '\n') { // detect clients line ending format if(!m_LineEndingDetected) { m_aLineEnding[0] = m_aBuffer[StartOffset]; if(StartOffset+1 < m_BufferOffset && (m_aBuffer[StartOffset+1] == '\r' || m_aBuffer[StartOffset+1] == '\n') && m_aBuffer[StartOffset] != m_aBuffer[StartOffset+1]) m_aLineEnding[1] = m_aBuffer[StartOffset+1]; m_LineEndingDetected = true; } if(++StartOffset >= m_BufferOffset) { m_BufferOffset = 0; return 0; } } // find message end int EndOffset = StartOffset; while(m_aBuffer[EndOffset] != '\r' && m_aBuffer[EndOffset] != '\n') { if(++EndOffset >= m_BufferOffset) { if(StartOffset > 0) { mem_move(m_aBuffer, m_aBuffer+StartOffset, m_BufferOffset-StartOffset); m_BufferOffset -= StartOffset; } return 0; } } // extract message and update buffer if(MaxLength-1 < EndOffset-StartOffset) { if(StartOffset > 0) { mem_move(m_aBuffer, m_aBuffer+StartOffset, m_BufferOffset-StartOffset); m_BufferOffset -= StartOffset; } return 0; } mem_copy(pLine, m_aBuffer+StartOffset, EndOffset-StartOffset); pLine[EndOffset-StartOffset] = 0; str_sanitize_cc(pLine); mem_move(m_aBuffer, m_aBuffer+EndOffset, m_BufferOffset-EndOffset); m_BufferOffset -= EndOffset; return 1; } } return 0; } int CNetworkClient::Send(const char *pLine) { if(State() != NET_CONNSTATE_ONLINE) return -1; char aBuf[1024]; str_copy(aBuf, pLine, (int)(sizeof(aBuf))-2); int Length = str_length(aBuf); aBuf[Length] = m_aLineEnding[0]; aBuf[Length+1] = m_aLineEnding[1]; aBuf[Length+2] = m_aLineEnding[2]; Length += 3; const char *pData = aBuf; while(1) { int Send = net_tcp_send(m_Socket, pData, Length); if(Send < 0) { m_State = NET_CONNSTATE_ERROR; str_copy(m_aErrorString, "failed to send packet", sizeof(m_aErrorString)); return -1; } if(Send >= Length) break; pData += Send; Length -= Send; } return 0; } ================================================ FILE: server/src/server.cpp ================================================ #include #include "netban.h" #include "network.h" #include "main.h" #include "server.h" int CServer::NewClientCallback(int ClientID, void *pUser) { CServer *pThis = (CServer *)pUser; char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(pThis->m_Network.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); if(pThis->Main()->Config()->m_Verbose) dbg_msg("server", "Connection accepted. ncid=%d addr=%s'", ClientID, aAddrStr); pThis->m_aClients[ClientID].m_State = CClient::STATE_CONNECTED; pThis->m_aClients[ClientID].m_TimeConnected = time_get(); pThis->m_Network.Send(ClientID, "Authentication required:"); return 0; } int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser) { CServer *pThis = (CServer *)pUser; char aAddrStr[NETADDR_MAXSTRSIZE]; net_addr_str(pThis->m_Network.ClientAddr(ClientID), aAddrStr, sizeof(aAddrStr), true); if(pThis->Main()->Config()->m_Verbose) dbg_msg("server", "Client dropped. ncid=%d addr=%s reason='%s'", ClientID, aAddrStr, pReason); if(pThis->m_aClients[ClientID].m_State == CClient::STATE_AUTHED) pThis->Main()->OnDelClient(ClientID); pThis->m_aClients[ClientID].m_State = CClient::STATE_EMPTY; return 0; } int CServer::Init(CMain *pMain, const char *Bind, int Port) { m_pMain = pMain; m_NetBan.Init(); for(int i = 0; i < NET_MAX_CLIENTS; i++) m_aClients[i].m_State = CClient::STATE_EMPTY; m_Ready = false; if(Port == 0) { dbg_msg("server", "Will not bind to port 0."); return 1; } NETADDR BindAddr; if(Bind[0] && net_host_lookup(Bind, &BindAddr, NETTYPE_ALL) == 0) { // got bindaddr BindAddr.type = NETTYPE_ALL; BindAddr.port = Port; } else { mem_zero(&BindAddr, sizeof(BindAddr)); BindAddr.type = NETTYPE_ALL; BindAddr.port = Port; } if(m_Network.Open(BindAddr, &m_NetBan)) { m_Network.SetCallbacks(NewClientCallback, DelClientCallback, this); m_Ready = true; dbg_msg("server", "Bound to %s:%d", Bind, Port); return 0; } else dbg_msg("server", "Couldn't open socket. Port (%d) might already be in use.", Port); return 1; } void CServer::Update() { if(!m_Ready) return; m_NetBan.Update(); m_Network.Update(); char aBuf[NET_MAX_PACKETSIZE]; int ClientID; while(m_Network.Recv(aBuf, (int)(sizeof(aBuf))-1, &ClientID)) { dbg_assert(m_aClients[ClientID].m_State != CClient::STATE_EMPTY, "Got message from empty slot."); if(m_aClients[ClientID].m_State == CClient::STATE_CONNECTED) { int ID = -1; char aUsername[128] = {0}; char aPassword[128] = {0}; const char *pTmp; if(!(pTmp = str_find(aBuf, ":")) || (unsigned)(pTmp - aBuf) > sizeof(aUsername) || (unsigned)(str_length(pTmp) - 1) > sizeof(aPassword)) { m_Network.NetBan()->BanAddr(m_Network.ClientAddr(ClientID), 60, "You're an idiot, go away."); m_Network.Drop(ClientID, "Fuck off."); return; } str_copy(aUsername, aBuf, pTmp - aBuf + 1); str_copy(aPassword, pTmp + 1, sizeof(aPassword)); if(!*aUsername || !*aPassword) { m_Network.NetBan()->BanAddr(m_Network.ClientAddr(ClientID), 60, "You're an idiot, go away."); m_Network.Drop(ClientID, "Username and password must not be blank."); return; } for(int i = 0; i < NET_MAX_CLIENTS; i++) { if(!Main()->Client(i)->m_Active) continue; if(str_comp(Main()->Client(i)->m_aUsername, aUsername) == 0 && str_comp(Main()->Client(i)->m_aPassword, aPassword) == 0) ID = i; } if(ID == -1) { m_Network.NetBan()->BanAddr(m_Network.ClientAddr(ClientID), 60, "Wrong username and/or password."); m_Network.Drop(ClientID, "Wrong username and/or password."); } else if(Main()->Client(ID)->m_ClientNetID != -1) { m_Network.Drop(ClientID, "Only one connection per user allowed."); } else { m_aClients[ClientID].m_State = CClient::STATE_AUTHED; m_aClients[ClientID].m_LastReceived = time_get(); m_Network.Send(ClientID, "Authentication successful. Access granted."); if(m_Network.ClientAddr(ClientID)->type == NETTYPE_IPV4) m_Network.Send(ClientID, "You are connecting via: IPv4"); else if(m_Network.ClientAddr(ClientID)->type == NETTYPE_IPV6) m_Network.Send(ClientID, "You are connecting via: IPv6"); if(Main()->Config()->m_Verbose) dbg_msg("server", "ncid=%d authed", ClientID); Main()->OnNewClient(ClientID, ID); } } else if(m_aClients[ClientID].m_State == CClient::STATE_AUTHED) { m_aClients[ClientID].m_LastReceived = time_get(); if(Main()->Config()->m_Verbose) dbg_msg("server", "ncid=%d cmd='%s'", ClientID, aBuf); if(str_comp(aBuf, "logout") == 0) m_Network.Drop(ClientID, "Logout. Bye Bye ~"); else Main()->HandleMessage(ClientID, aBuf); } } for(int i = 0; i < NET_MAX_CLIENTS; ++i) { if(m_aClients[i].m_State == CClient::STATE_CONNECTED && time_get() > m_aClients[i].m_TimeConnected + 5 * time_freq()) { m_Network.NetBan()->BanAddr(m_Network.ClientAddr(i), 30, "Authentication timeout."); m_Network.Drop(i, "Authentication timeout."); } else if(m_aClients[i].m_State == CClient::STATE_AUTHED && time_get() > m_aClients[i].m_LastReceived + 15 * time_freq()) m_Network.Drop(i, "Timeout."); } } void CServer::Send(int ClientID, const char *pLine) { if(!m_Ready) return; if(ClientID == -1) { for(int i = 0; i < NET_MAX_CLIENTS; i++) { if(m_aClients[i].m_State == CClient::STATE_AUTHED) m_Network.Send(i, pLine); } } else if(ClientID >= 0 && ClientID < NET_MAX_CLIENTS && m_aClients[ClientID].m_State == CClient::STATE_AUTHED) m_Network.Send(ClientID, pLine); } void CServer::Shutdown() { if(!m_Ready) return; m_Network.Close(); } ================================================ FILE: server/src/server.h ================================================ #ifndef SERVER_H #define SERVER_H #include "netban.h" #include "network.h" class CServer { class CClient { public: enum { STATE_EMPTY=0, STATE_CONNECTED, STATE_AUTHED, }; int m_State; int64 m_TimeConnected; int64 m_LastReceived; }; CClient m_aClients[NET_MAX_CLIENTS]; CNetwork m_Network; CNetBan m_NetBan; class CMain *m_pMain; bool m_Ready; static int NewClientCallback(int ClientID, void *pUser); static int DelClientCallback(int ClientID, const char *pReason, void *pUser); public: int Init(CMain *pMain, const char *Bind, int Port); void Update(); void Send(int ClientID, const char *pLine); void Shutdown(); CNetwork *Network() { return &m_Network; } CNetBan *NetBan() { return &m_NetBan; } CMain *Main() { return m_pMain; } }; #endif ================================================ FILE: server/src/system.c ================================================ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include #include #include #include #include #include #include "system.h" #if defined(CONF_FAMILY_UNIX) #include #include /* unix net includes */ #include #include #include #include #include #include #include #include #include #include #include #if defined(CONF_PLATFORM_MACOSX) #include #endif #elif defined(CONF_FAMILY_WINDOWS) #define WIN32_LEAN_AND_MEAN #define _WIN32_WINNT 0x0501 /* required for mingw to get getaddrinfo to work */ #include #include #include #include #include #include #else #error NOT IMPLEMENTED #endif #if defined(CONF_PLATFORM_SOLARIS) #include #endif #if defined(__cplusplus) extern "C" { #endif IOHANDLE io_stdin() { return (IOHANDLE)stdin; } IOHANDLE io_stdout() { return (IOHANDLE)stdout; } IOHANDLE io_stderr() { return (IOHANDLE)stderr; } static DBG_LOGGER loggers[16]; static int num_loggers = 0; static NETSTATS network_stats = {0}; static MEMSTATS memory_stats = {0}; static NETSOCKET invalid_socket = {NETTYPE_INVALID, -1, -1}; void dbg_logger(DBG_LOGGER logger) { loggers[num_loggers++] = logger; } void dbg_assert_imp(const char *filename, int line, int test, const char *msg) { if(!test) { dbg_msg("assert", "%s(%d): %s", filename, line, msg); dbg_break(); } } void dbg_break() { *((volatile unsigned*)0) = 0x0; } void dbg_msg(const char *sys, const char *fmt, ...) { va_list args; char str[1024*8]; char *msg; int i, len; str_format(str, sizeof(str), "[%s]: ", sys); len = strlen(str); msg = (char *)str + len; va_start(args, fmt); #if defined(CONF_FAMILY_WINDOWS) _vsnprintf(msg, sizeof(str)-len, fmt, args); #else vsnprintf(msg, sizeof(str)-len, fmt, args); #endif va_end(args); for(i = 0; i < num_loggers; i++) loggers[i](str); } static void logger_stdout(const char *line) { printf("%s\n", line); fflush(stdout); } static void logger_debugger(const char *line) { #if defined(CONF_FAMILY_WINDOWS) OutputDebugString(line); OutputDebugString("\n"); #endif } static IOHANDLE logfile = 0; static void logger_file(const char *line) { io_write(logfile, line, strlen(line)); io_write_newline(logfile); io_flush(logfile); } void dbg_logger_stdout() { dbg_logger(logger_stdout); } void dbg_logger_debugger() { dbg_logger(logger_debugger); } void dbg_logger_file(const char *filename) { logfile = io_open(filename, IOFLAG_WRITE); if(logfile) dbg_logger(logger_file); else dbg_msg("dbg/logger", "failed to open '%s' for logging", filename); } /* */ typedef struct MEMHEADER { const char *filename; int line; int size; struct MEMHEADER *prev; struct MEMHEADER *next; } MEMHEADER; typedef struct MEMTAIL { int guard; } MEMTAIL; static struct MEMHEADER *first = 0; static const int MEM_GUARD_VAL = 0xbaadc0de; void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment) { /* TODO: fix alignment */ /* TODO: add debugging */ MEMTAIL *tail; MEMHEADER *header = (struct MEMHEADER *)malloc(size+sizeof(MEMHEADER)+sizeof(MEMTAIL)); dbg_assert(header != 0, "mem_alloc failure"); if(!header) return NULL; tail = (struct MEMTAIL *)(((char*)(header+1))+size); header->size = size; header->filename = filename; header->line = line; memory_stats.allocated += header->size; memory_stats.total_allocations++; memory_stats.active_allocations++; tail->guard = MEM_GUARD_VAL; header->prev = (MEMHEADER *)0; header->next = first; if(first) first->prev = header; first = header; /*dbg_msg("mem", "++ %p", header+1); */ return header+1; } void mem_free(void *p) { if(p) { MEMHEADER *header = (MEMHEADER *)p - 1; MEMTAIL *tail = (MEMTAIL *)(((char*)(header+1))+header->size); if(tail->guard != MEM_GUARD_VAL) dbg_msg("mem", "!! %p", p); /* dbg_msg("mem", "-- %p", p); */ memory_stats.allocated -= header->size; memory_stats.active_allocations--; if(header->prev) header->prev->next = header->next; else first = header->next; if(header->next) header->next->prev = header->prev; free(header); } } void mem_debug_dump(IOHANDLE file) { char buf[1024]; MEMHEADER *header = first; if(!file) file = io_open("memory.txt", IOFLAG_WRITE); if(file) { while(header) { str_format(buf, sizeof(buf), "%s(%d): %d", header->filename, header->line, header->size); io_write(file, buf, strlen(buf)); io_write_newline(file); header = header->next; } io_close(file); } } void mem_copy(void *dest, const void *source, unsigned size) { memcpy(dest, source, size); } void mem_move(void *dest, const void *source, unsigned size) { memmove(dest, source, size); } void mem_zero(void *block, unsigned size) { memset(block, 0, size); } int mem_check_imp() { MEMHEADER *header = first; while(header) { MEMTAIL *tail = (MEMTAIL *)(((char*)(header+1))+header->size); if(tail->guard != MEM_GUARD_VAL) { dbg_msg("mem", "Memory check failed at %s(%d): %d", header->filename, header->line, header->size); return 0; } header = header->next; } return 1; } IOHANDLE io_open(const char *filename, int flags) { if(flags == IOFLAG_READ) { #if defined(CONF_FAMILY_WINDOWS) // check for filename case sensitive WIN32_FIND_DATA finddata; HANDLE handle; int length; length = str_length(filename); if(!filename || !length || filename[length-1] == '\\') return 0x0; handle = FindFirstFile(filename, &finddata); if(handle == INVALID_HANDLE_VALUE) return 0x0; else if(str_comp(filename+length-str_length(finddata.cFileName), finddata.cFileName) != 0) { FindClose(handle); return 0x0; } FindClose(handle); #endif return (IOHANDLE)fopen(filename, "rb"); } if(flags == IOFLAG_WRITE) return (IOHANDLE)fopen(filename, "wb"); return 0x0; } unsigned io_read(IOHANDLE io, void *buffer, unsigned size) { return fread(buffer, 1, size, (FILE*)io); } unsigned io_skip(IOHANDLE io, int size) { fseek((FILE*)io, size, SEEK_CUR); return size; } int io_seek(IOHANDLE io, int offset, int origin) { int real_origin; switch(origin) { case IOSEEK_START: real_origin = SEEK_SET; break; case IOSEEK_CUR: real_origin = SEEK_CUR; break; case IOSEEK_END: real_origin = SEEK_END; break; default: return -1; } return fseek((FILE*)io, offset, real_origin); } long int io_tell(IOHANDLE io) { return ftell((FILE*)io); } long int io_length(IOHANDLE io) { long int length; io_seek(io, 0, IOSEEK_END); length = io_tell(io); io_seek(io, 0, IOSEEK_START); return length; } unsigned io_write(IOHANDLE io, const void *buffer, unsigned size) { return fwrite(buffer, 1, size, (FILE*)io); } unsigned io_write_newline(IOHANDLE io) { #if defined(CONF_FAMILY_WINDOWS) return fwrite("\r\n", 1, 2, (FILE*)io); #else return fwrite("\n", 1, 1, (FILE*)io); #endif } int io_close(IOHANDLE io) { fclose((FILE*)io); return 1; } int io_flush(IOHANDLE io) { fflush((FILE*)io); return 0; } void *thread_create(void (*threadfunc)(void *), void *u) { #if defined(CONF_FAMILY_UNIX) pthread_t id; pthread_create(&id, NULL, (void *(*)(void*))threadfunc, u); return (void*)id; #elif defined(CONF_FAMILY_WINDOWS) return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadfunc, u, 0, NULL); #else #error not implemented #endif } void thread_wait(void *thread) { #if defined(CONF_FAMILY_UNIX) pthread_join((pthread_t)thread, NULL); #elif defined(CONF_FAMILY_WINDOWS) WaitForSingleObject((HANDLE)thread, INFINITE); #else #error not implemented #endif } void thread_destroy(void *thread) { #if defined(CONF_FAMILY_UNIX) void *r = 0; pthread_join((pthread_t)thread, &r); #else /*#error not implemented*/ #endif } void thread_yield() { #if defined(CONF_FAMILY_UNIX) sched_yield(); #elif defined(CONF_FAMILY_WINDOWS) Sleep(0); #else #error not implemented #endif } void thread_sleep(int milliseconds) { #if defined(CONF_FAMILY_UNIX) usleep(milliseconds*1000); #elif defined(CONF_FAMILY_WINDOWS) Sleep(milliseconds); #else #error not implemented #endif } void thread_detach(void *thread) { #if defined(CONF_FAMILY_UNIX) pthread_detach((pthread_t)(thread)); #elif defined(CONF_FAMILY_WINDOWS) CloseHandle(thread); #else #error not implemented #endif } #if defined(CONF_FAMILY_UNIX) typedef pthread_mutex_t LOCKINTERNAL; #elif defined(CONF_FAMILY_WINDOWS) typedef CRITICAL_SECTION LOCKINTERNAL; #else #error not implemented on this platform #endif LOCK lock_create() { LOCKINTERNAL *lock = (LOCKINTERNAL*)mem_alloc(sizeof(LOCKINTERNAL), 4); #if defined(CONF_FAMILY_UNIX) pthread_mutex_init(lock, 0x0); #elif defined(CONF_FAMILY_WINDOWS) InitializeCriticalSection((LPCRITICAL_SECTION)lock); #else #error not implemented on this platform #endif return (LOCK)lock; } void lock_destroy(LOCK lock) { #if defined(CONF_FAMILY_UNIX) pthread_mutex_destroy((LOCKINTERNAL *)lock); #elif defined(CONF_FAMILY_WINDOWS) DeleteCriticalSection((LPCRITICAL_SECTION)lock); #else #error not implemented on this platform #endif mem_free(lock); } int lock_try(LOCK lock) { #if defined(CONF_FAMILY_UNIX) return pthread_mutex_trylock((LOCKINTERNAL *)lock); #elif defined(CONF_FAMILY_WINDOWS) return !TryEnterCriticalSection((LPCRITICAL_SECTION)lock); #else #error not implemented on this platform #endif } void lock_wait(LOCK lock) { #if defined(CONF_FAMILY_UNIX) pthread_mutex_lock((LOCKINTERNAL *)lock); #elif defined(CONF_FAMILY_WINDOWS) EnterCriticalSection((LPCRITICAL_SECTION)lock); #else #error not implemented on this platform #endif } void lock_release(LOCK lock) { #if defined(CONF_FAMILY_UNIX) pthread_mutex_unlock((LOCKINTERNAL *)lock); #elif defined(CONF_FAMILY_WINDOWS) LeaveCriticalSection((LPCRITICAL_SECTION)lock); #else #error not implemented on this platform #endif } #if !defined(CONF_PLATFORM_MACOSX) #if defined(CONF_FAMILY_UNIX) void semaphore_init(SEMAPHORE *sem) { sem_init(sem, 0, 0); } void semaphore_wait(SEMAPHORE *sem) { sem_wait(sem); } void semaphore_signal(SEMAPHORE *sem) { sem_post(sem); } void semaphore_destroy(SEMAPHORE *sem) { sem_destroy(sem); } #elif defined(CONF_FAMILY_WINDOWS) void semaphore_init(SEMAPHORE *sem) { *sem = CreateSemaphore(0, 0, 10000, 0); } void semaphore_wait(SEMAPHORE *sem) { WaitForSingleObject((HANDLE)*sem, INFINITE); } void semaphore_signal(SEMAPHORE *sem) { ReleaseSemaphore((HANDLE)*sem, 1, NULL); } void semaphore_destroy(SEMAPHORE *sem) { CloseHandle((HANDLE)*sem); } #else #error not implemented on this platform #endif #endif /* ----- time ----- */ int64 time_get() { #if defined(CONF_FAMILY_UNIX) struct timeval val; gettimeofday(&val, NULL); return (int64)val.tv_sec*(int64)1000000+(int64)val.tv_usec; #elif defined(CONF_FAMILY_WINDOWS) static int64 last = 0; int64 t; QueryPerformanceCounter((PLARGE_INTEGER)&t); if(ttype != NETTYPE_IPV4) { dbg_msg("system", "couldn't convert NETADDR of type %d to ipv4", src->type); return; } dest->sin_family = AF_INET; dest->sin_port = htons(src->port); mem_copy(&dest->sin_addr.s_addr, src->ip, 4); } static void netaddr_to_sockaddr_in6(const NETADDR *src, struct sockaddr_in6 *dest) { mem_zero(dest, sizeof(struct sockaddr_in6)); if(src->type != NETTYPE_IPV6) { dbg_msg("system", "couldn't not convert NETADDR of type %d to ipv6", src->type); return; } dest->sin6_family = AF_INET6; dest->sin6_port = htons(src->port); mem_copy(&dest->sin6_addr.s6_addr, src->ip, 16); } static void sockaddr_to_netaddr(const struct sockaddr *src, NETADDR *dst) { if(src->sa_family == AF_INET) { mem_zero(dst, sizeof(NETADDR)); dst->type = NETTYPE_IPV4; dst->port = htons(((struct sockaddr_in*)src)->sin_port); mem_copy(dst->ip, &((struct sockaddr_in*)src)->sin_addr.s_addr, 4); } else if(src->sa_family == AF_INET6) { mem_zero(dst, sizeof(NETADDR)); dst->type = NETTYPE_IPV6; dst->port = htons(((struct sockaddr_in6*)src)->sin6_port); mem_copy(dst->ip, &((struct sockaddr_in6*)src)->sin6_addr.s6_addr, 16); } else { mem_zero(dst, sizeof(struct sockaddr)); dbg_msg("system", "couldn't convert sockaddr of family %d", src->sa_family); } } int net_addr_comp(const NETADDR *a, const NETADDR *b) { return mem_comp(a, b, sizeof(NETADDR)); } void net_addr_str(const NETADDR *addr, char *string, int max_length, int add_port) { if(addr->type == NETTYPE_IPV4) { if(add_port != 0) str_format(string, max_length, "%d.%d.%d.%d:%d", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3], addr->port); else str_format(string, max_length, "%d.%d.%d.%d", addr->ip[0], addr->ip[1], addr->ip[2], addr->ip[3]); } else if(addr->type == NETTYPE_IPV6) { if(add_port != 0) str_format(string, max_length, "[%x:%x:%x:%x:%x:%x:%x:%x]:%d", (addr->ip[0]<<8)|addr->ip[1], (addr->ip[2]<<8)|addr->ip[3], (addr->ip[4]<<8)|addr->ip[5], (addr->ip[6]<<8)|addr->ip[7], (addr->ip[8]<<8)|addr->ip[9], (addr->ip[10]<<8)|addr->ip[11], (addr->ip[12]<<8)|addr->ip[13], (addr->ip[14]<<8)|addr->ip[15], addr->port); else str_format(string, max_length, "[%x:%x:%x:%x:%x:%x:%x:%x]", (addr->ip[0]<<8)|addr->ip[1], (addr->ip[2]<<8)|addr->ip[3], (addr->ip[4]<<8)|addr->ip[5], (addr->ip[6]<<8)|addr->ip[7], (addr->ip[8]<<8)|addr->ip[9], (addr->ip[10]<<8)|addr->ip[11], (addr->ip[12]<<8)|addr->ip[13], (addr->ip[14]<<8)|addr->ip[15]); } else str_format(string, max_length, "unknown type %d", addr->type); } static int priv_net_extract(const char *hostname, char *host, int max_host, int *port) { int i; *port = 0; host[0] = 0; if(hostname[0] == '[') { // ipv6 mode for(i = 1; i < max_host && hostname[i] && hostname[i] != ']'; i++) host[i-1] = hostname[i]; host[i-1] = 0; if(hostname[i] != ']') // malformatted return -1; i++; if(hostname[i] == ':') *port = atol(hostname+i+1); } else { // generic mode (ipv4, hostname etc) for(i = 0; i < max_host-1 && hostname[i] && hostname[i] != ':'; i++) host[i] = hostname[i]; host[i] = 0; if(hostname[i] == ':') *port = atol(hostname+i+1); } return 0; } int net_host_lookup(const char *hostname, NETADDR *addr, int types) { struct addrinfo hints; struct addrinfo *result; int e; char host[256]; int port = 0; if(priv_net_extract(hostname, host, sizeof(host), &port)) return -1; /* dbg_msg("host lookup", "host='%s' port=%d %d", host, port, types); */ mem_zero(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; if(types == NETTYPE_IPV4) hints.ai_family = AF_INET; else if(types == NETTYPE_IPV6) hints.ai_family = AF_INET6; e = getaddrinfo(host, NULL, &hints, &result); if(e != 0 || !result) return -1; sockaddr_to_netaddr(result->ai_addr, addr); freeaddrinfo(result); addr->port = port; return 0; } static int parse_int(int *out, const char **str) { int i = 0; *out = 0; if(**str < '0' || **str > '9') return -1; i = **str - '0'; (*str)++; while(1) { if(**str < '0' || **str > '9') { *out = i; return 0; } i = (i*10) + (**str - '0'); (*str)++; } return 0; } static int parse_char(char c, const char **str) { if(**str != c) return -1; (*str)++; return 0; } static int parse_uint8(unsigned char *out, const char **str) { int i; if(parse_int(&i, str) != 0) return -1; if(i < 0 || i > 0xff) return -1; *out = i; return 0; } static int parse_uint16(unsigned short *out, const char **str) { int i; if(parse_int(&i, str) != 0) return -1; if(i < 0 || i > 0xffff) return -1; *out = i; return 0; } int net_addr_from_str(NETADDR *addr, const char *string) { const char *str = string; mem_zero(addr, sizeof(NETADDR)); if(str[0] == '[') { /* ipv6 */ struct sockaddr_in6 sa6; char buf[128]; int i; str++; for(i = 0; i < 127 && str[i] && str[i] != ']'; i++) buf[i] = str[i]; buf[i] = 0; str += i; #if defined(CONF_FAMILY_WINDOWS) { int size; sa6.sin6_family = AF_INET6; size = (int)sizeof(sa6); if(WSAStringToAddress(buf, AF_INET6, NULL, (struct sockaddr *)&sa6, &size) != 0) return -1; } #else if(inet_pton(AF_INET6, buf, &sa6) != 1) return -1; #endif sockaddr_to_netaddr((struct sockaddr *)&sa6, addr); if(*str == ']') { str++; if(*str == ':') { str++; if(parse_uint16(&addr->port, &str)) return -1; } } else return -1; return 0; } else { /* ipv4 */ if(parse_uint8(&addr->ip[0], &str)) return -1; if(parse_char('.', &str)) return -1; if(parse_uint8(&addr->ip[1], &str)) return -1; if(parse_char('.', &str)) return -1; if(parse_uint8(&addr->ip[2], &str)) return -1; if(parse_char('.', &str)) return -1; if(parse_uint8(&addr->ip[3], &str)) return -1; if(*str == ':') { str++; if(parse_uint16(&addr->port, &str)) return -1; } addr->type = NETTYPE_IPV4; } return 0; } static void priv_net_close_socket(int sock) { #if defined(CONF_FAMILY_WINDOWS) closesocket(sock); #else close(sock); #endif } static int priv_net_close_all_sockets(NETSOCKET sock) { /* close down ipv4 */ if(sock.ipv4sock >= 0) { priv_net_close_socket(sock.ipv4sock); sock.ipv4sock = -1; sock.type &= ~NETTYPE_IPV4; } /* close down ipv6 */ if(sock.ipv6sock >= 0) { priv_net_close_socket(sock.ipv6sock); sock.ipv6sock = -1; sock.type &= ~NETTYPE_IPV6; } return 0; } static int priv_net_create_socket(int domain, int type, struct sockaddr *addr, int sockaddrlen) { int sock, e; /* create socket */ sock = socket(domain, type, 0); if(sock < 0) { #if defined(CONF_FAMILY_WINDOWS) char buf[128]; int error = WSAGetLastError(); if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, 0, buf, sizeof(buf), 0) == 0) buf[0] = 0; dbg_msg("net", "failed to create socket with domain %d and type %d (%d '%s')", domain, type, error, buf); #else dbg_msg("net", "failed to create socket with domain %d and type %d (%d '%s')", domain, type, errno, strerror(errno)); #endif return -1; } /* set to IPv6 only if thats what we are creating */ #if defined(IPV6_V6ONLY) /* windows sdk 6.1 and higher */ if(domain == AF_INET6) { int ipv6only = 1; setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&ipv6only, sizeof(ipv6only)); } #endif if(type == SOCK_STREAM) { int tmp = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)); } /* bind the socket */ e = bind(sock, addr, sockaddrlen); if(e != 0) { #if defined(CONF_FAMILY_WINDOWS) char buf[128]; int error = WSAGetLastError(); if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, 0, buf, sizeof(buf), 0) == 0) buf[0] = 0; dbg_msg("net", "failed to bind socket with domain %d and type %d (%d '%s')", domain, type, error, buf); #else dbg_msg("net", "failed to bind socket with domain %d and type %d (%d '%s')", domain, type, errno, strerror(errno)); #endif priv_net_close_socket(sock); return -1; } /* return the newly created socket */ return sock; } NETSOCKET net_udp_create(NETADDR bindaddr) { NETSOCKET sock = invalid_socket; NETADDR tmpbindaddr = bindaddr; int broadcast = 1; int recvsize = 65536; if(bindaddr.type&NETTYPE_IPV4) { struct sockaddr_in addr; int socket = -1; /* bind, we should check for error */ tmpbindaddr.type = NETTYPE_IPV4; netaddr_to_sockaddr_in(&tmpbindaddr, &addr); socket = priv_net_create_socket(AF_INET, SOCK_DGRAM, (struct sockaddr *)&addr, sizeof(addr)); if(socket >= 0) { sock.type |= NETTYPE_IPV4; sock.ipv4sock = socket; /* set broadcast */ setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); /* set receive buffer size */ setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char*)&recvsize, sizeof(recvsize)); } } if(bindaddr.type&NETTYPE_IPV6) { struct sockaddr_in6 addr; int socket = -1; /* bind, we should check for error */ tmpbindaddr.type = NETTYPE_IPV6; netaddr_to_sockaddr_in6(&tmpbindaddr, &addr); socket = priv_net_create_socket(AF_INET6, SOCK_DGRAM, (struct sockaddr *)&addr, sizeof(addr)); if(socket >= 0) { sock.type |= NETTYPE_IPV6; sock.ipv6sock = socket; /* set broadcast */ setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); /* set receive buffer size */ setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char*)&recvsize, sizeof(recvsize)); } } /* set non-blocking */ net_set_non_blocking(sock); /* return */ return sock; } int net_udp_send(NETSOCKET sock, const NETADDR *addr, const void *data, int size) { int d = -1; if(addr->type&NETTYPE_IPV4) { if(sock.ipv4sock >= 0) { struct sockaddr_in sa; if(addr->type&NETTYPE_LINK_BROADCAST) { mem_zero(&sa, sizeof(sa)); sa.sin_port = htons(addr->port); sa.sin_family = AF_INET; sa.sin_addr.s_addr = INADDR_BROADCAST; } else netaddr_to_sockaddr_in(addr, &sa); d = sendto((int)sock.ipv4sock, (const char*)data, size, 0, (struct sockaddr *)&sa, sizeof(sa)); } else dbg_msg("net", "can't sent ipv4 traffic to this socket"); } if(addr->type&NETTYPE_IPV6) { if(sock.ipv6sock >= 0) { struct sockaddr_in6 sa; if(addr->type&NETTYPE_LINK_BROADCAST) { mem_zero(&sa, sizeof(sa)); sa.sin6_port = htons(addr->port); sa.sin6_family = AF_INET6; sa.sin6_addr.s6_addr[0] = 0xff; /* multicast */ sa.sin6_addr.s6_addr[1] = 0x02; /* link local scope */ sa.sin6_addr.s6_addr[15] = 1; /* all nodes */ } else netaddr_to_sockaddr_in6(addr, &sa); d = sendto((int)sock.ipv6sock, (const char*)data, size, 0, (struct sockaddr *)&sa, sizeof(sa)); } else dbg_msg("net", "can't sent ipv6 traffic to this socket"); } /* else dbg_msg("net", "can't sent to network of type %d", addr->type); */ /*if(d < 0) { char addrstr[256]; net_addr_str(addr, addrstr, sizeof(addrstr)); dbg_msg("net", "sendto error (%d '%s')", errno, strerror(errno)); dbg_msg("net", "\tsock = %d %x", sock, sock); dbg_msg("net", "\tsize = %d %x", size, size); dbg_msg("net", "\taddr = %s", addrstr); }*/ network_stats.sent_bytes += size; network_stats.sent_packets++; return d; } int net_udp_recv(NETSOCKET sock, NETADDR *addr, void *data, int maxsize) { char sockaddrbuf[128]; socklen_t fromlen;// = sizeof(sockaddrbuf); int bytes = 0; if(bytes == 0 && sock.ipv4sock >= 0) { fromlen = sizeof(struct sockaddr_in); bytes = recvfrom(sock.ipv4sock, (char*)data, maxsize, 0, (struct sockaddr *)&sockaddrbuf, &fromlen); } if(bytes <= 0 && sock.ipv6sock >= 0) { fromlen = sizeof(struct sockaddr_in6); bytes = recvfrom(sock.ipv6sock, (char*)data, maxsize, 0, (struct sockaddr *)&sockaddrbuf, &fromlen); } if(bytes > 0) { sockaddr_to_netaddr((struct sockaddr *)&sockaddrbuf, addr); network_stats.recv_bytes += bytes; network_stats.recv_packets++; return bytes; } else if(bytes == 0) return 0; return -1; /* error */ } int net_udp_close(NETSOCKET sock) { return priv_net_close_all_sockets(sock); } NETSOCKET net_tcp_create(NETADDR bindaddr) { NETSOCKET sock = invalid_socket; NETADDR tmpbindaddr = bindaddr; if(bindaddr.type&NETTYPE_IPV4) { struct sockaddr_in addr; int socket = -1; /* bind, we should check for error */ tmpbindaddr.type = NETTYPE_IPV4; netaddr_to_sockaddr_in(&tmpbindaddr, &addr); socket = priv_net_create_socket(AF_INET, SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr)); if(socket >= 0) { sock.type |= NETTYPE_IPV4; sock.ipv4sock = socket; } } if(bindaddr.type&NETTYPE_IPV6) { struct sockaddr_in6 addr; int socket = -1; /* bind, we should check for error */ tmpbindaddr.type = NETTYPE_IPV6; netaddr_to_sockaddr_in6(&tmpbindaddr, &addr); socket = priv_net_create_socket(AF_INET6, SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr)); if(socket >= 0) { sock.type |= NETTYPE_IPV6; sock.ipv6sock = socket; } } /* return */ return sock; } int net_set_non_blocking(NETSOCKET sock) { unsigned long mode = 1; if(sock.ipv4sock >= 0) { #if defined(CONF_FAMILY_WINDOWS) ioctlsocket(sock.ipv4sock, FIONBIO, (unsigned long *)&mode); #else ioctl(sock.ipv4sock, FIONBIO, (unsigned long *)&mode); #endif } if(sock.ipv6sock >= 0) { #if defined(CONF_FAMILY_WINDOWS) ioctlsocket(sock.ipv6sock, FIONBIO, (unsigned long *)&mode); #else ioctl(sock.ipv6sock, FIONBIO, (unsigned long *)&mode); #endif } return 0; } int net_set_blocking(NETSOCKET sock) { unsigned long mode = 0; if(sock.ipv4sock >= 0) { #if defined(CONF_FAMILY_WINDOWS) ioctlsocket(sock.ipv4sock, FIONBIO, (unsigned long *)&mode); #else ioctl(sock.ipv4sock, FIONBIO, (unsigned long *)&mode); #endif } if(sock.ipv6sock >= 0) { #if defined(CONF_FAMILY_WINDOWS) ioctlsocket(sock.ipv6sock, FIONBIO, (unsigned long *)&mode); #else ioctl(sock.ipv6sock, FIONBIO, (unsigned long *)&mode); #endif } return 0; } int net_tcp_listen(NETSOCKET sock, int backlog) { int err = -1; if(sock.ipv4sock >= 0) err = listen(sock.ipv4sock, backlog); if(sock.ipv6sock >= 0) err = listen(sock.ipv6sock, backlog); return err; } int net_tcp_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR *a) { int s; socklen_t sockaddr_len; *new_sock = invalid_socket; if(sock.ipv4sock >= 0) { struct sockaddr_in addr; sockaddr_len = sizeof(addr); s = accept(sock.ipv4sock, (struct sockaddr *)&addr, &sockaddr_len); if (s != -1) { sockaddr_to_netaddr((const struct sockaddr *)&addr, a); new_sock->type = NETTYPE_IPV4; new_sock->ipv4sock = s; return s; } } if(sock.ipv6sock >= 0) { struct sockaddr_in6 addr; sockaddr_len = sizeof(addr); s = accept(sock.ipv6sock, (struct sockaddr *)&addr, &sockaddr_len); if (s != -1) { sockaddr_to_netaddr((const struct sockaddr *)&addr, a); new_sock->type = NETTYPE_IPV6; new_sock->ipv6sock = s; return s; } } return -1; } int net_tcp_connect(NETSOCKET sock, const NETADDR *a) { if(a->type&NETTYPE_IPV4) { struct sockaddr_in addr; netaddr_to_sockaddr_in(a, &addr); return connect(sock.ipv4sock, (struct sockaddr *)&addr, sizeof(addr)); } if(a->type&NETTYPE_IPV6) { struct sockaddr_in6 addr; netaddr_to_sockaddr_in6(a, &addr); return connect(sock.ipv6sock, (struct sockaddr *)&addr, sizeof(addr)); } return -1; } int net_tcp_connect_non_blocking(NETSOCKET sock, NETADDR bindaddr) { int res = 0; net_set_non_blocking(sock); res = net_tcp_connect(sock, &bindaddr); net_set_blocking(sock); return res; } int net_tcp_send(NETSOCKET sock, const void *data, int size) { int bytes = -1; if(sock.ipv4sock >= 0) bytes = send((int)sock.ipv4sock, (const char*)data, size, 0); if(sock.ipv6sock >= 0) bytes = send((int)sock.ipv6sock, (const char*)data, size, 0); return bytes; } int net_tcp_recv(NETSOCKET sock, void *data, int maxsize) { int bytes = -1; if(sock.ipv4sock >= 0) bytes = recv((int)sock.ipv4sock, (char*)data, maxsize, 0); if(sock.ipv6sock >= 0) bytes = recv((int)sock.ipv6sock, (char*)data, maxsize, 0); return bytes; } int net_tcp_close(NETSOCKET sock) { return priv_net_close_all_sockets(sock); } int net_errno() { #if defined(CONF_FAMILY_WINDOWS) return WSAGetLastError(); #else return errno; #endif } int net_would_block() { #if defined(CONF_FAMILY_WINDOWS) return net_errno() == WSAEWOULDBLOCK; #else return net_errno() == EWOULDBLOCK; #endif } int net_init() { #if defined(CONF_FAMILY_WINDOWS) WSADATA wsaData; int err = WSAStartup(MAKEWORD(1, 1), &wsaData); dbg_assert(err == 0, "network initialization failed."); return err==0?0:1; #endif return 0; } int fs_listdir(const char *dir, FS_LISTDIR_CALLBACK cb, int type, void *user) { #if defined(CONF_FAMILY_WINDOWS) WIN32_FIND_DATA finddata; HANDLE handle; char buffer[1024*2]; int length; str_format(buffer, sizeof(buffer), "%s/*", dir); handle = FindFirstFileA(buffer, &finddata); if (handle == INVALID_HANDLE_VALUE) return 0; str_format(buffer, sizeof(buffer), "%s/", dir); length = str_length(buffer); /* add all the entries */ do { str_copy(buffer+length, finddata.cFileName, (int)sizeof(buffer)-length); if(cb(finddata.cFileName, fs_is_dir(buffer), type, user)) break; } while (FindNextFileA(handle, &finddata)); FindClose(handle); return 0; #else struct dirent *entry; char buffer[1024*2]; int length; DIR *d = opendir(dir); if(!d) return 0; str_format(buffer, sizeof(buffer), "%s/", dir); length = str_length(buffer); while((entry = readdir(d)) != NULL) { str_copy(buffer+length, entry->d_name, (int)sizeof(buffer)-length); if(cb(entry->d_name, fs_is_dir(buffer), type, user)) break; } /* close the directory and return */ closedir(d); return 0; #endif } int fs_storage_path(const char *appname, char *path, int max) { #if defined(CONF_FAMILY_WINDOWS) char *home = getenv("APPDATA"); if(!home) return -1; _snprintf(path, max, "%s/%s", home, appname); return 0; #else char *home = getenv("HOME"); #if !defined(CONF_PLATFORM_MACOSX) int i; #endif if(!home) return -1; #if defined(CONF_PLATFORM_MACOSX) snprintf(path, max, "%s/Library/Application Support/%s", home, appname); #else snprintf(path, max, "%s/.%s", home, appname); for(i = strlen(home)+2; path[i]; i++) path[i] = tolower(path[i]); #endif return 0; #endif } int fs_makedir(const char *path) { #if defined(CONF_FAMILY_WINDOWS) if(_mkdir(path) == 0) return 0; if(errno == EEXIST) return 0; return -1; #else if(mkdir(path, 0755) == 0) return 0; if(errno == EEXIST) return 0; return -1; #endif } int fs_is_dir(const char *path) { #if defined(CONF_FAMILY_WINDOWS) /* TODO: do this smarter */ WIN32_FIND_DATA finddata; HANDLE handle; char buffer[1024*2]; str_format(buffer, sizeof(buffer), "%s/*", path); if ((handle = FindFirstFileA(buffer, &finddata)) == INVALID_HANDLE_VALUE) return 0; FindClose(handle); return 1; #else struct stat sb; if (stat(path, &sb) == -1) return 0; if (S_ISDIR(sb.st_mode)) return 1; else return 0; #endif } int fs_chdir(const char *path) { if(fs_is_dir(path)) { if(chdir(path)) return 1; else return 0; } else return 1; } char *fs_getcwd(char *buffer, int buffer_size) { if(buffer == 0) return 0; #if defined(CONF_FAMILY_WINDOWS) return _getcwd(buffer, buffer_size); #else return getcwd(buffer, buffer_size); #endif } int fs_parent_dir(char *path) { char *parent = 0; for(; *path; ++path) { if(*path == '/' || *path == '\\') parent = path; } if(parent) { *parent = 0; return 0; } return 1; } int fs_remove(const char *filename) { if(remove(filename) != 0) return 1; return 0; } int fs_rename(const char *oldname, const char *newname) { if(rename(oldname, newname) != 0) return 1; return 0; } void swap_endian(void *data, unsigned elem_size, unsigned num) { char *src = (char*) data; char *dst = src + (elem_size - 1); while(num) { unsigned n = elem_size>>1; char tmp; while(n) { tmp = *src; *src = *dst; *dst = tmp; src++; dst--; n--; } src = src + (elem_size>>1); dst = src + (elem_size - 1); num--; } } int net_socket_read_wait(NETSOCKET sock, int time) { struct timeval tv; fd_set readfds; int sockid; tv.tv_sec = 0; tv.tv_usec = 1000*time; sockid = 0; FD_ZERO(&readfds); if(sock.ipv4sock >= 0) { FD_SET(sock.ipv4sock, &readfds); sockid = sock.ipv4sock; } if(sock.ipv6sock >= 0) { FD_SET(sock.ipv6sock, &readfds); if(sock.ipv6sock > sockid) sockid = sock.ipv6sock; } /* don't care about writefds and exceptfds */ select(sockid+1, &readfds, NULL, NULL, &tv); if(sock.ipv4sock >= 0 && FD_ISSET(sock.ipv4sock, &readfds)) return 1; if(sock.ipv6sock >= 0 && FD_ISSET(sock.ipv6sock, &readfds)) return 1; return 0; } int time_timestamp() { return time(0); } void str_append(char *dst, const char *src, int dst_size) { int s = strlen(dst); int i = 0; while(s < dst_size) { dst[s] = src[i]; if(!src[i]) /* check for null termination */ break; s++; i++; } dst[dst_size-1] = 0; /* assure null termination */ } void str_copy(char *dst, const char *src, int dst_size) { strncpy(dst, src, dst_size); dst[dst_size-1] = 0; /* assure null termination */ } int str_length(const char *str) { return (int)strlen(str); } void str_format(char *buffer, int buffer_size, const char *format, ...) { #if defined(CONF_FAMILY_WINDOWS) va_list ap; va_start(ap, format); _vsnprintf(buffer, buffer_size, format, ap); va_end(ap); #else va_list ap; va_start(ap, format); vsnprintf(buffer, buffer_size, format, ap); va_end(ap); #endif buffer[buffer_size-1] = 0; /* assure null termination */ } /* makes sure that the string only contains the characters between 32 and 127 */ void str_sanitize_strong(char *str_in) { unsigned char *str = (unsigned char *)str_in; while(*str) { *str &= 0x7f; if(*str < 32) *str = 32; str++; } } /* makes sure that the string only contains the characters between 32 and 255 */ void str_sanitize_cc(char *str_in) { unsigned char *str = (unsigned char *)str_in; while(*str) { if(*str < 32) *str = ' '; str++; } } /* makes sure that the string only contains the characters between 32 and 255 + \r\n\t */ void str_sanitize(char *str_in) { unsigned char *str = (unsigned char *)str_in; while(*str) { if(*str < 32 && !(*str == '\r') && !(*str == '\n') && !(*str == '\t')) *str = ' '; str++; } } char *str_skip_to_whitespace(char *str) { while(*str && (*str != ' ' && *str != '\t' && *str != '\n')) str++; return str; } char *str_skip_whitespaces(char *str) { while(*str && (*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r')) str++; return str; } /* case */ int str_comp_nocase(const char *a, const char *b) { #if defined(CONF_FAMILY_WINDOWS) return _stricmp(a,b); #else return strcasecmp(a,b); #endif } int str_comp_nocase_num(const char *a, const char *b, const int num) { #if defined(CONF_FAMILY_WINDOWS) return _strnicmp(a, b, num); #else return strncasecmp(a, b, num); #endif } int str_comp(const char *a, const char *b) { return strcmp(a, b); } int str_comp_num(const char *a, const char *b, const int num) { return strncmp(a, b, num); } int str_comp_filenames(const char *a, const char *b) { int result; for(; *a && *b; ++a, ++b) { if(*a >= '0' && *a <= '9' && *b >= '0' && *b <= '9') { result = 0; do { if(!result) result = *a - *b; ++a; ++b; } while(*a >= '0' && *a <= '9' && *b >= '0' && *b <= '9'); if(*a >= '0' && *a <= '9') return 1; else if(*b >= '0' && *b <= '9') return -1; else if(result) return result; } if(tolower(*a) != tolower(*b)) break; } return tolower(*a) - tolower(*b); } const char *str_find_nocase(const char *haystack, const char *needle) { while(*haystack) /* native implementation */ { const char *a = haystack; const char *b = needle; while(*a && *b && tolower(*a) == tolower(*b)) { a++; b++; } if(!(*b)) return haystack; haystack++; } return 0; } const char *str_find(const char *haystack, const char *needle) { while(*haystack) /* native implementation */ { const char *a = haystack; const char *b = needle; while(*a && *b && *a == *b) { a++; b++; } if(!(*b)) return haystack; haystack++; } return 0; } void str_hex(char *dst, int dst_size, const void *data, int data_size) { static const char hex[] = "0123456789ABCDEF"; int b; for(b = 0; b < data_size && b < dst_size/4-4; b++) { dst[b*3] = hex[((const unsigned char *)data)[b]>>4]; dst[b*3+1] = hex[((const unsigned char *)data)[b]&0xf]; dst[b*3+2] = ' '; dst[b*3+3] = 0; } } void str_timestamp(char *buffer, int buffer_size) { time_t time_data; struct tm *time_info; time(&time_data); time_info = localtime(&time_data); strftime(buffer, buffer_size, "%Y-%m-%d_%H-%M-%S", time_info); buffer[buffer_size-1] = 0; /* assure null termination */ } int mem_comp(const void *a, const void *b, int size) { return memcmp(a,b,size); } const MEMSTATS *mem_stats() { return &memory_stats; } void net_stats(NETSTATS *stats_inout) { *stats_inout = network_stats; } void gui_messagebox(const char *title, const char *message) { #if defined(CONF_PLATFORM_MACOSX) DialogRef theItem; DialogItemIndex itemIndex; /* FIXME: really needed? can we rely on glfw? */ /* HACK - get events without a bundle */ ProcessSerialNumber psn; GetCurrentProcess(&psn); TransformProcessType(&psn,kProcessTransformToForegroundApplication); SetFrontProcess(&psn); /* END HACK */ CreateStandardAlert(kAlertStopAlert, CFStringCreateWithCString(NULL, title, kCFStringEncodingASCII), CFStringCreateWithCString(NULL, message, kCFStringEncodingASCII), NULL, &theItem); RunStandardAlert(theItem, NULL, &itemIndex); #elif defined(CONF_FAMILY_UNIX) static char cmd[1024]; int err; /* use xmessage which is available on nearly every X11 system */ snprintf(cmd, sizeof(cmd), "xmessage -center -title '%s' '%s'", title, message); err = system(cmd); dbg_msg("gui/msgbox", "result = %i", err); #elif defined(CONF_FAMILY_WINDOWS) MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK); #else /* this is not critical */ #warning not implemented #endif } int str_isspace(char c) { return c == ' ' || c == '\n' || c == '\t'; } char str_uppercase(char c) { if(c >= 'a' && c <= 'z') return 'A' + (c-'a'); return c; } int str_toint(const char *str) { return atoi(str); } float str_tofloat(const char *str) { return atof(str); } static int str_utf8_isstart(char c) { if((c&0xC0) == 0x80) /* 10xxxxxx */ return 0; return 1; } int str_utf8_rewind(const char *str, int cursor) { while(cursor) { cursor--; if(str_utf8_isstart(*(str + cursor))) break; } return cursor; } int str_utf8_forward(const char *str, int cursor) { const char *buf = str + cursor; if(!buf[0]) return cursor; if((*buf&0x80) == 0x0) /* 0xxxxxxx */ return cursor+1; else if((*buf&0xE0) == 0xC0) /* 110xxxxx */ { if(!buf[1]) return cursor+1; return cursor+2; } else if((*buf & 0xF0) == 0xE0) /* 1110xxxx */ { if(!buf[1]) return cursor+1; if(!buf[2]) return cursor+2; return cursor+3; } else if((*buf & 0xF8) == 0xF0) /* 11110xxx */ { if(!buf[1]) return cursor+1; if(!buf[2]) return cursor+2; if(!buf[3]) return cursor+3; return cursor+4; } /* invalid */ return cursor+1; } int str_utf8_encode(char *ptr, int chr) { /* encode */ if(chr <= 0x7F) { ptr[0] = (char)chr; return 1; } else if(chr <= 0x7FF) { ptr[0] = 0xC0|((chr>>6)&0x1F); ptr[1] = 0x80|(chr&0x3F); return 2; } else if(chr <= 0xFFFF) { ptr[0] = 0xE0|((chr>>12)&0x0F); ptr[1] = 0x80|((chr>>6)&0x3F); ptr[2] = 0x80|(chr&0x3F); return 3; } else if(chr <= 0x10FFFF) { ptr[0] = 0xF0|((chr>>18)&0x07); ptr[1] = 0x80|((chr>>12)&0x3F); ptr[2] = 0x80|((chr>>6)&0x3F); ptr[3] = 0x80|(chr&0x3F); return 4; } return 0; } int str_utf8_decode(const char **ptr) { const char *buf = *ptr; int ch = 0; do { if((*buf&0x80) == 0x0) /* 0xxxxxxx */ { ch = *buf; buf++; } else if((*buf&0xE0) == 0xC0) /* 110xxxxx */ { ch = (*buf++ & 0x3F) << 6; if(!(*buf)) break; ch += (*buf++ & 0x3F); if(ch == 0) ch = -1; } else if((*buf & 0xF0) == 0xE0) /* 1110xxxx */ { ch = (*buf++ & 0x1F) << 12; if(!(*buf)) break; ch += (*buf++ & 0x3F) << 6; if(!(*buf)) break; ch += (*buf++ & 0x3F); if(ch == 0) ch = -1; } else if((*buf & 0xF8) == 0xF0) /* 11110xxx */ { ch = (*buf++ & 0x0F) << 18; if(!(*buf)) break; ch += (*buf++ & 0x3F) << 12; if(!(*buf)) break; ch += (*buf++ & 0x3F) << 6; if(!(*buf)) break; ch += (*buf++ & 0x3F); if(ch == 0) ch = -1; } else { /* invalid */ buf++; break; } *ptr = buf; return ch; } while(0); /* out of bounds */ *ptr = buf; return -1; } int str_utf8_check(const char *str) { while(*str) { if((*str&0x80) == 0x0) str++; else if((*str&0xE0) == 0xC0 && (*(str+1)&0xC0) == 0x80) str += 2; else if((*str&0xF0) == 0xE0 && (*(str+1)&0xC0) == 0x80 && (*(str+2)&0xC0) == 0x80) str += 3; else if((*str&0xF8) == 0xF0 && (*(str+1)&0xC0) == 0x80 && (*(str+2)&0xC0) == 0x80 && (*(str+3)&0xC0) == 0x80) str += 4; else return 0; } return 1; } unsigned str_quickhash(const char *str) { unsigned hash = 5381; for(; *str; str++) hash = ((hash << 5) + hash) + (*str); /* hash * 33 + c */ return hash; } #if defined(__cplusplus) } #endif ================================================ FILE: web/css/light.css ================================================ body { font-family:Molengo,"Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi Micro Hei", sans-serif; background: #ebebeb url('../img/light.png');padding-top: 70px;padding-bottom: 30px;} .announcement { color: #777; border-bottom: solid 3px #d0d0d0; background-color: #fff; padding: 10px 10px; text-align: center; transition: 0.3s; } .announcement p { display: inline-block;font-size: 15px;margin: 0;line-height: 1;text-indent: 5px; } .announcement i { display: inline-block;font-size: 15px;margin: 0;line-height: 1;color: #444; } .path-announcement2 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .path-announcement { margin-top: 50px; background-color: #fafbfc; border-bottom: 1px solid #e1e4e8; } .path-announcement p { color: #444444; line-height: 3.5; margin-left: 20px; } .path-announcement a { color: #666; box-shadow: 0px 1px 0px 0px #999; text-decoration: none; } .path-announcement a:focus,.path-announcement a:hover { color: #337ab7; box-shadow: 0px 1px 0px 0px #555; } .path-announcement i { margin-right: 10px; } .path-announcement i,.path-announcement p { display: inline; } .navbar { min-height: 50px; } .navbar-top { background-color: #444 !important; } .navbar-brand { color: #fff; padding: 10px; font-size: 20px; } .dropdown .dropdown-toggle { padding-bottom: 10px; padding-top: 10px; } .navbar-inverse .navbar-brand { color: #fff; padding: 15px 20px 10px; font-size: 16px; font-weight: 600; } .content { background: #ffffff; padding: 20px; border: 1px #eee solid; -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, .1); -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, .1); box-shadow: 0 1px 10px rgba(0, 0, 0, .1); margin-bottom: 20px; margin-top: 50px; } .table { background: #ffffff; margin-bottom: 0; border-collapse: collapse; border-radius: 3px; } .table th, .table td { text-align: center; } .table-striped tbody > tr.even > td, .table-striped tbody > tr.even > th { background-color: #F9F9F9; } .table-striped tbody > tr.odd > td, .table-striped tbody > tr.odd > th { background-color: #FFF; } .progress { margin-bottom: 0; } .progress-bar { color: #000; } .table-hover > tbody > tr:hover > td { background: #E6E6E6; } tr.even.expandRow > :hover { background: #F9F9F9 !important; } tr.odd.expandRow > :hover { background: #FFF !important; } .expandRow > td { padding: 0 !important; border-top: 0px !important; } #cpu, #ram, #hdd, #network, #traffic { min-width: 55px; max-width: 100px; } @media only screen and (max-width: 992px) { #location, tr td:nth-child(4) { display:none; visibility:hidden; } } @media only screen and (max-width: 720px) { #type, tr td:nth-child(3) { display:none; visibility:hidden; } #location, tr td:nth-child(4) { display:none; visibility:hidden; } #uptime, tr td:nth-child(5) { display:none; visibility:hidden; } } @media only screen and (max-width: 600px) { #type, tr td:nth-child(3) { display:none; visibility:hidden; } #location, tr td:nth-child(4) { display:none; visibility:hidden; } #uptime, tr td:nth-child(5) { display:none; visibility:hidden; } #load, tr td:nth-child(6) { display:none; visibility:hidden; } } @media only screen and (max-width: 533px) { #type, tr td:nth-child(3) { display:none; visibility:hidden; } #location, tr td:nth-child(4) { display:none; visibility:hidden; } #uptime, tr td:nth-child(5) { display:none; visibility:hidden; } #traffic, tr td:nth-child(8) { display:none; visibility:hidden; } #load, tr td:nth-child(6) { display:none; visibility:hidden; } } @media only screen and (max-width: 450px) { body { font-size: 10px; } .content { padding: 0; } #name, tr td:nth-child(2) { min-width: 20px; max-width: 60px; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } #type, tr td:nth-child(3) { display:none; visibility:hidden; } #location, tr td:nth-child(4) { display:none; visibility:hidden; } #uptime, tr td:nth-child(5) { display:none; visibility:hidden; } #traffic, tr td:nth-child(8) { display:none; visibility:hidden; } #hdd, tr td:nth-child(11) { display:none; visibility:hidden; } #cpu, #ram { min-width: 20px; max-width: 40px; } } ================================================ FILE: web/index.html ================================================ 逗比云监控
加载中...
如果出现此消息,请确保您已启用Javascript!
否则云监控主服务(服务端) 未启动或已关闭.

IPv4 节点名 虚拟化 位置 在线时间 负载 网络(B/s) ↓|↑ 流量(B) ↓|↑ CPU 内存 硬盘

Updating...
================================================ FILE: web/js/serverstatus.js ================================================ // serverstatus.js var error = 0; var d = 0; var server_status = new Array(); function timeSince(date) { if(date == 0) return "从未."; var seconds = Math.floor((new Date() - date) / 1000); var interval = Math.floor(seconds / 31536000); if (interval > 1) return interval + " 年前."; interval = Math.floor(seconds / 2592000); if (interval > 1) return interval + " 月前."; interval = Math.floor(seconds / 86400); if (interval > 1) return interval + " 日前."; interval = Math.floor(seconds / 3600); if (interval > 1) return interval + " 小时前."; interval = Math.floor(seconds / 60); if (interval > 1) return interval + " 分钟前."; /*if(Math.floor(seconds) >= 5) return Math.floor(seconds) + " seconds";*/ else return "几秒前."; } function bytesToSize(bytes, precision, si) { var ret; si = typeof si !== 'undefined' ? si : 0; if(si != 0) { var kilobyte = 1000; var megabyte = kilobyte * 1000; var gigabyte = megabyte * 1000; var terabyte = gigabyte * 1000; } else { var kilobyte = 1024; var megabyte = kilobyte * 1024; var gigabyte = megabyte * 1024; var terabyte = gigabyte * 1024; } if ((bytes >= 0) && (bytes < kilobyte)) { return bytes + ' B'; } else if ((bytes >= kilobyte) && (bytes < megabyte)) { ret = (bytes / kilobyte).toFixed(precision) + ' K'; } else if ((bytes >= megabyte) && (bytes < gigabyte)) { ret = (bytes / megabyte).toFixed(precision) + ' M'; } else if ((bytes >= gigabyte) && (bytes < terabyte)) { ret = (bytes / gigabyte).toFixed(precision) + ' G'; } else if (bytes >= terabyte) { ret = (bytes / terabyte).toFixed(precision) + ' T'; } else { return bytes + ' B'; } if(si != 0) { return ret + 'B'; } else { return ret + 'iB'; } } function uptime() { $.getJSON("json/stats.json", function(result) { $("#loading-notice").remove(); if(result.reload) setTimeout(function() { location.reload(true) }, 1000); for (var i = 0; i < result.servers.length; i++) { var TableRow = $("#servers tr#r" + i); var ExpandRow = $("#servers #rt" + i); var hack; // fuck CSS for making me do this if(i%2) hack="odd"; else hack="even"; if (!TableRow.length) { $("#servers").append( "" + "
加载中
" + "加载中" + "加载中" + "" + "加载中" + "加载中" + "加载中" + "加载中" + "加载中" + "
加载中
" + "
加载中
" + "
加载中
" + "" + "
" + "
加载中
" + "
加载中
" + "
加载中
" + "
加载中
" + "
" ); TableRow = $("#servers tr#r" + i); ExpandRow = $("#servers #rt" + i); server_status[i] = true; } TableRow = TableRow[0]; if(error) { TableRow.setAttribute("data-target", "#rt" + i); server_status[i] = true; } // Online4 if (result.servers[i].online4) { TableRow.children["online4"].children[0].children[0].className = "progress-bar progress-bar-success"; TableRow.children["online4"].children[0].children[0].innerHTML = "开启"; } else { TableRow.children["online4"].children[0].children[0].className = "progress-bar progress-bar-danger"; TableRow.children["online4"].children[0].children[0].innerHTML = "关闭"; } // Online6 //if (result.servers[i].online6) { // TableRow.children["online6"].children[0].children[0].className = "progress-bar progress-bar-success"; // TableRow.children["online6"].children[0].children[0].innerHTML = "开启"; //} else { // TableRow.children["online6"].children[0].children[0].className = "progress-bar progress-bar-danger"; // TableRow.children["online6"].children[0].children[0].innerHTML = "关闭"; //} // Name TableRow.children["name"].innerHTML = result.servers[i].name; // Type TableRow.children["type"].innerHTML = result.servers[i].type; // Host //TableRow.children["host"].innerHTML = result.servers[i].host; // Location TableRow.children["location"].innerHTML = result.servers[i].location; if (!result.servers[i].online4 && !result.servers[i].online6) { if (server_status[i]) { TableRow.children["uptime"].innerHTML = "–"; TableRow.children["load"].innerHTML = "–"; TableRow.children["network"].innerHTML = "–"; TableRow.children["traffic"].innerHTML = "–"; TableRow.children["cpu"].children[0].children[0].className = "progress-bar progress-bar-danger"; TableRow.children["cpu"].children[0].children[0].style.width = "100%"; TableRow.children["cpu"].children[0].children[0].innerHTML = "关闭"; TableRow.children["memory"].children[0].children[0].className = "progress-bar progress-bar-danger"; TableRow.children["memory"].children[0].children[0].style.width = "100%"; TableRow.children["memory"].children[0].children[0].innerHTML = "关闭"; TableRow.children["hdd"].children[0].children[0].className = "progress-bar progress-bar-danger"; TableRow.children["hdd"].children[0].children[0].style.width = "100%"; TableRow.children["hdd"].children[0].children[0].innerHTML = "关闭"; if(ExpandRow.hasClass("in")) { ExpandRow.collapse("hide"); } TableRow.setAttribute("data-target", ""); server_status[i] = false; } } else { if (!server_status[i]) { TableRow.setAttribute("data-target", "#rt" + i); server_status[i] = true; } // Uptime TableRow.children["uptime"].innerHTML = result.servers[i].uptime; // Load if(result.servers[i].load == -1) { TableRow.children["load"].innerHTML = "–"; } else { TableRow.children["load"].innerHTML = result.servers[i].load; } // Network var netstr = ""; if(result.servers[i].network_rx < 1000) netstr += result.servers[i].network_rx.toFixed(0) + "B"; else if(result.servers[i].network_rx < 1000*1000) netstr += (result.servers[i].network_rx/1000).toFixed(0) + "K"; else netstr += (result.servers[i].network_rx/1000/1000).toFixed(1) + "M"; netstr += " | " if(result.servers[i].network_tx < 1000) netstr += result.servers[i].network_tx.toFixed(0) + "B"; else if(result.servers[i].network_tx < 1000*1000) netstr += (result.servers[i].network_tx/1000).toFixed(0) + "K"; else netstr += (result.servers[i].network_tx/1000/1000).toFixed(1) + "M"; TableRow.children["network"].innerHTML = netstr; //Traffic var trafficstr = ""; if(result.servers[i].network_in < 1024) trafficstr += result.servers[i].network_in.toFixed(0) + "B"; else if(result.servers[i].network_in < 1024*1024) trafficstr += (result.servers[i].network_in/1024).toFixed(0) + "K"; else if(result.servers[i].network_in < 1024*1024*1024) trafficstr += (result.servers[i].network_in/1024/1024).toFixed(1) + "M"; else if(result.servers[i].network_in < 1024*1024*1024*1024) trafficstr += (result.servers[i].network_in/1024/1024/1024).toFixed(2) + "G"; else trafficstr += (result.servers[i].network_in/1024/1024/1024/1024).toFixed(2) + "T"; trafficstr += " | " if(result.servers[i].network_out < 1024) trafficstr += result.servers[i].network_out.toFixed(0) + "B"; else if(result.servers[i].network_out < 1024*1024) trafficstr += (result.servers[i].network_out/1024).toFixed(0) + "K"; else if(result.servers[i].network_out < 1024*1024*1024) trafficstr += (result.servers[i].network_out/1024/1024).toFixed(1) + "M"; else if(result.servers[i].network_out < 1024*1024*1024*1024) trafficstr += (result.servers[i].network_out/1024/1024/1024).toFixed(2) + "G"; else trafficstr += (result.servers[i].network_out/1024/1024/1024/1024).toFixed(2) + "T"; TableRow.children["traffic"].innerHTML = trafficstr; // CPU if (result.servers[i].cpu >= 90) TableRow.children["cpu"].children[0].children[0].className = "progress-bar progress-bar-danger"; else if (result.servers[i].cpu >= 80) TableRow.children["cpu"].children[0].children[0].className = "progress-bar progress-bar-warning"; else TableRow.children["cpu"].children[0].children[0].className = "progress-bar progress-bar-success"; TableRow.children["cpu"].children[0].children[0].style.width = result.servers[i].cpu + "%"; TableRow.children["cpu"].children[0].children[0].innerHTML = result.servers[i].cpu + "%"; // Memory var Mem = ((result.servers[i].memory_used/result.servers[i].memory_total)*100.0).toFixed(0); if (Mem >= 90) TableRow.children["memory"].children[0].children[0].className = "progress-bar progress-bar-danger"; else if (Mem >= 80) TableRow.children["memory"].children[0].children[0].className = "progress-bar progress-bar-warning"; else TableRow.children["memory"].children[0].children[0].className = "progress-bar progress-bar-success"; TableRow.children["memory"].children[0].children[0].style.width = Mem + "%"; TableRow.children["memory"].children[0].children[0].innerHTML = Mem + "%"; ExpandRow[0].children["expand_mem"].innerHTML = "内存信息: " + bytesToSize(result.servers[i].memory_used*1024, 2) + " / " + bytesToSize(result.servers[i].memory_total*1024, 2); // Swap ExpandRow[0].children["expand_swap"].innerHTML = "交换分区: " + bytesToSize(result.servers[i].swap_used*1024, 2) + " / " + bytesToSize(result.servers[i].swap_total*1024, 2); // HDD var HDD = ((result.servers[i].hdd_used/result.servers[i].hdd_total)*100.0).toFixed(0); if (HDD >= 90) TableRow.children["hdd"].children[0].children[0].className = "progress-bar progress-bar-danger"; else if (HDD >= 80) TableRow.children["hdd"].children[0].children[0].className = "progress-bar progress-bar-warning"; else TableRow.children["hdd"].children[0].children[0].className = "progress-bar progress-bar-success"; TableRow.children["hdd"].children[0].children[0].style.width = HDD + "%"; TableRow.children["hdd"].children[0].children[0].innerHTML = HDD + "%"; ExpandRow[0].children["expand_hdd"].innerHTML = "硬盘信息: " + bytesToSize(result.servers[i].hdd_used*1024*1024, 2) + " / " + bytesToSize(result.servers[i].hdd_total*1024*1024, 2); // Custom if (result.servers[i].custom) { ExpandRow[0].children["expand_custom"].innerHTML = result.servers[i].custom } else { ExpandRow[0].children["expand_custom"].innerHTML = "" } } }; d = new Date(result.updated*1000); error = 0; }).fail(function(update_error) { if (!error) { $("#servers > tr.accordion-toggle").each(function(i) { var TableRow = $("#servers tr#r" + i)[0]; var ExpandRow = $("#servers #rt" + i); TableRow.children["online4"].children[0].children[0].className = "progress-bar progress-bar-error"; TableRow.children["online4"].children[0].children[0].innerHTML = "错误"; //TableRow.children["online6"].children[0].children[0].className = "progress-bar progress-bar-error"; //TableRow.children["online6"].children[0].children[0].innerHTML = "错误"; TableRow.children["uptime"].innerHTML = "
错误
"; TableRow.children["load"].innerHTML = "
错误
"; TableRow.children["network"].innerHTML = "
错误
"; TableRow.children["traffic"].innerHTML = "
错误
"; TableRow.children["cpu"].children[0].children[0].className = "progress-bar progress-bar-error"; TableRow.children["cpu"].children[0].children[0].style.width = "100%"; TableRow.children["cpu"].children[0].children[0].innerHTML = "错误"; TableRow.children["memory"].children[0].children[0].className = "progress-bar progress-bar-error"; TableRow.children["memory"].children[0].children[0].style.width = "100%"; TableRow.children["memory"].children[0].children[0].innerHTML = "错误"; TableRow.children["hdd"].children[0].children[0].className = "progress-bar progress-bar-error"; TableRow.children["hdd"].children[0].children[0].style.width = "100%"; TableRow.children["hdd"].children[0].children[0].innerHTML = "错误"; if(ExpandRow.hasClass("in")) { ExpandRow.collapse("hide"); } TableRow.setAttribute("data-target", ""); server_status[i] = false; }); } error = 1; $("#updated").html("更新错误."); }); } function updateTime() { if (!error) $("#updated").html("最后更新: " + timeSince(d)); } uptime(); updateTime(); setInterval(uptime, 2000); setInterval(updateTime, 500); // styleswitcher.js function setActiveStyleSheet(title) { var i, a, main; for(i=0; (a = document.getElementsByTagName("link")[i]); i++) { if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) { a.disabled = true; if(a.getAttribute("title") == title) a.disabled = false; } } } function getActiveStyleSheet() { var i, a; for(i=0; (a = document.getElementsByTagName("link")[i]); i++) { if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title") && !a.disabled) return a.getAttribute("title"); } return null; } function getPreferredStyleSheet() { var i, a; for(i=0; (a = document.getElementsByTagName("link")[i]); i++) { if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("rel").indexOf("alt") == -1 && a.getAttribute("title")) return a.getAttribute("title"); } return null; } function createCookie(name,value,days) { if (days) { var date = new Date(); date.setTime(date.getTime()+(days*24*60*60*1000)); var expires = "; expires="+date.toGMTString(); } else expires = ""; document.cookie = name+"="+value+expires+"; path=/"; } function readCookie(name) { var nameEQ = name + "="; var ca = document.cookie.split(';'); for(var i=0;i < ca.length;i++) { var c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); } return null; } window.onload = function(e) { var cookie = readCookie("style"); var title = cookie ? cookie : getPreferredStyleSheet(); setActiveStyleSheet(title); } window.onunload = function(e) { var title = getActiveStyleSheet(); createCookie("style", title, 365); } var cookie = readCookie("style"); var title = cookie ? cookie : getPreferredStyleSheet(); setActiveStyleSheet(title); ================================================ FILE: web/json/.gitignore ================================================ stats.json stats.json~ ================================================ FILE: web/robots.txt ================================================ # robots.txt generated at http://tool.chinaz.com/robots/ User-agent: Baiduspider Disallow: / User-agent: Sosospider Disallow: / User-agent: sogou spider Disallow: / User-agent: YodaoBot Disallow: / User-agent: Googlebot Disallow: / User-agent: Bingbot Disallow: / User-agent: Slurp Disallow: / User-agent: Teoma Disallow: / User-agent: ia_archiver Disallow: / User-agent: twiceler Disallow: / User-agent: MSNBot Disallow: / User-agent: Scrubby Disallow: / User-agent: Robozilla Disallow: / User-agent: Gigabot Disallow: / User-agent: googlebot-image Disallow: / User-agent: googlebot-mobile Disallow: / User-agent: yahoo-mmcrawler Disallow: / User-agent: yahoo-blogs/v3.9 Disallow: / User-agent: psbot Disallow: / Disallow: /ip/ Disallow: /qr/ Disallow: /