mirror of
https://github.com/lxsang/ant-http
synced 2025-04-11 22:16:44 +02:00
Compare commits
207 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
cc5bb1284c | ||
|
d3e8a05732 | ||
|
2863cf1710 | ||
|
4f91f99ca0 | ||
|
58a7738afe | ||
|
3bedc3ffb5 | ||
|
9c25e62c0a | ||
|
32552ee0ed | ||
|
375989acbe | ||
|
eed2e9c1af | ||
|
a1acea2441 | ||
|
bd0663ddf7 | ||
|
6a483e188d | ||
|
a78815c3b6 | ||
|
4e1c220b39 | ||
|
5d7a0f8b49 | ||
|
cab479a0fb | ||
|
a8201947ef | ||
|
91461f0b21 | ||
|
0d9d6b5497 | ||
|
a96523c218 | ||
|
6c39e969a4 | ||
|
cd40ec7214 | ||
|
bf9cd3f1ca | ||
|
e35c8b7a6f | ||
|
c7f6cf42cb | ||
|
088baa1ef5 | ||
|
923c6b2625 | ||
|
7315ece4dc | ||
|
e6f0588a84 | ||
|
99c5df1c48 | ||
|
8cab868003 | ||
|
7bf58c1d01 | ||
|
5d79f912c1 | ||
|
4c62fae037 | ||
|
9b8b4b7b63 | ||
|
8f982694c6 | ||
|
e1872f8b6b | ||
|
6780f94fcf | ||
|
f9f5de2809 | ||
|
4d6c7f9326 | ||
|
0c5385fd3f | ||
|
5851f3cba3 | ||
|
d4fa350a3a | ||
|
b864827f2c | ||
|
11876c6e29 | ||
|
a6bb0b16db | ||
|
100f9d74b6 | ||
|
19ea0256a4 | ||
|
b766884997 | ||
|
81e73669e6 | ||
|
82bede7e72 | ||
|
6758a4bb52 | ||
|
8d55e6efd4 | ||
|
d3f68b8153 | ||
|
24de800ee8 | ||
|
eb5af36a05 | ||
|
6fcc16fe23 | ||
|
65a6813af5 | ||
|
ba6694ab8b | ||
|
9f1f814ac4 | ||
|
e009e6dc20 | ||
|
b435495850 | ||
|
d2ebccecf0 | ||
|
7efaa32245 | ||
|
307ff008f9 | ||
|
5efad64e9c | ||
|
5f9d8dcf8f | ||
|
9b37f45e5f | ||
|
2d2ac4f1da | ||
|
818142612a | ||
|
3d4b602884 | ||
|
c7e9d2a579 | ||
|
0daef95c25 | ||
|
e9cd1addf7 | ||
|
e7ec929709 | ||
|
a31625951b | ||
|
2b457988e1 | ||
|
cab458b84e | ||
|
2d06355b91 | ||
|
9c1e925e85 | ||
|
786f395139 | ||
|
4a7a5bf632 | ||
|
6d75aed2ce | ||
|
242da4386d | ||
|
ce6fd04a16 | ||
|
5e2f34e75b | ||
|
46c1102537 | ||
|
d32df84f2e | ||
|
b28f1a3fa0 | ||
|
dd7ff9b434 | ||
|
ce5549d394 | ||
|
31accd9060 | ||
|
386146fd9c | ||
|
e2e4dc0603 | ||
|
608c2680ed | ||
|
8814364690 | ||
|
23bd7d67fc | ||
|
491f288ea5 | ||
|
50a2d40213 | ||
|
49ac8cb18b | ||
|
a9cf5f0123 | ||
|
eec7fefb3e | ||
|
be71a6d146 | ||
|
6c4713ac62 | ||
|
1f143327e4 | ||
|
8b483f0579 | ||
|
520b99f94b | ||
|
5d1998ef1f | ||
|
44a33e5413 | ||
|
08877f84ad | ||
|
cbbc48d216 | ||
|
9d19a81a8e | ||
|
77f4165fd4 | ||
|
7de4bd6c1c | ||
|
6408e646e8 | ||
|
f1a188ca3d | ||
|
7e0232f299 | ||
|
02d48fb659 | ||
|
4b6de31e66 | ||
|
708e492c49 | ||
|
9da33124bc | ||
|
1e4856b08a | ||
|
8c11f06ff0 | ||
|
69ad08b1a1 | ||
|
b35cd61da4 | ||
|
e38cd9de1b | ||
|
883ef9c3a3 | ||
|
5e60eeac02 | ||
|
806a7ccc6a | ||
|
56806fb25b | ||
|
280ad920e9 | ||
|
776bd017e2 | ||
|
2041ee2ba0 | ||
|
5765df3697 | ||
|
397145662c | ||
|
adb0811a08 | ||
|
d2a5f3220a | ||
|
a0445adcc5 | ||
|
0f44d62849 | ||
|
775b025087 | ||
|
d5682fe186 | ||
|
357b45aee9 | ||
|
7fc12a72e6 | ||
|
7b274de0db | ||
|
6f7d028c86 | ||
|
4c6eeef0ac | ||
|
1200d9d990 | ||
|
75df72e10a | ||
|
1af2945af0 | ||
|
189120bd88 | ||
|
4608ae874f | ||
|
cb98562585 | ||
|
876f60c9a2 | ||
|
909d1f10e3 | ||
|
d5c4461d34 | ||
|
b3609ea014 | ||
|
91d84baf01 | ||
|
c429a7dd66 | ||
|
ea07981160 | ||
|
4fb4674045 | ||
|
4c9808da78 | ||
|
d391b78109 | ||
|
50b248c9e2 | ||
|
d01e5e13aa | ||
|
f0ecc62162 | ||
|
85cfce134b | ||
|
3435733ac2 | ||
|
d04d29fe6b | ||
|
2562c06f31 | ||
|
348dd45385 | ||
|
457eefee25 | ||
|
b093bd0194 | ||
|
63c65e0d46 | ||
|
4e75706e22 | ||
|
e98970b378 | ||
|
98feff09d6 | ||
|
9b68d1d327 | ||
|
8d2449bd69 | ||
|
1e2e600bfc | ||
|
079f96de66 | ||
|
3f2af063aa | ||
|
fabe2e6e2a | ||
|
e05515a537 | ||
|
2dc6d09413 | ||
|
5d795ce4c3 | ||
|
f48321e1a1 | ||
|
afc0b31184 | ||
|
903fdfa483 | ||
|
bac36497e2 | ||
|
1a8423b120 | ||
|
6bd5c473b9 | ||
|
cfcadffa1f | ||
|
b07cffd28e | ||
|
94ab53febf | ||
|
adc898d5d8 | ||
|
97228be9e6 | ||
|
b82e992ef2 | ||
|
01722ae39c | ||
|
296fae73c3 | ||
|
91a45fab24 | ||
|
0d9e536e55 | ||
|
1305c6268e | ||
|
985597cb3c | ||
|
476db8ecee | ||
|
3513b562f5 | ||
|
424db8dd6e |
17
.gitea/workflows/ci.yml
Normal file
17
.gitea/workflows/ci.yml
Normal file
@ -0,0 +1,17 @@
|
||||
name: Autotools pipeline
|
||||
run-name: Building multi-platform autotools project
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build-amd64:
|
||||
uses: dany/actions/.gitea/workflows/autotools-cross.yml@master
|
||||
with:
|
||||
platform: amd64
|
||||
build-arm64:
|
||||
uses: dany/actions/.gitea/workflows/autotools-cross.yml@master
|
||||
with:
|
||||
platform: arm64
|
||||
build-arm:
|
||||
uses: dany/actions/.gitea/workflows/autotools-cross.yml@master
|
||||
with:
|
||||
platform: arm
|
20
.gitignore
vendored
20
.gitignore
vendored
@ -51,6 +51,24 @@ build
|
||||
*.cmd
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
Makefile.old
|
||||
dkms.con
|
||||
.DS_Store
|
||||
.*
|
||||
*.cache
|
||||
Makefile
|
||||
antd
|
||||
compile
|
||||
config.guess
|
||||
depcomp
|
||||
install-sh
|
||||
missing
|
||||
libtool
|
||||
config.log
|
||||
config.status
|
||||
config.sub
|
||||
configure
|
||||
aclocal.m4
|
||||
ltmain.sh
|
||||
Makefile.in
|
||||
configure~
|
@ -1,7 +0,0 @@
|
||||
language: c
|
||||
before_install:
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get install libssl-dev
|
||||
script:
|
||||
- make initd
|
||||
- make
|
90
Makefile
90
Makefile
@ -1,90 +0,0 @@
|
||||
include var.mk
|
||||
LIB_PATH=$(BUILDIRD)/plugins
|
||||
LIB_NAME=libantd
|
||||
LIB_FLAG= $(LIB_NAME).$(EXT)
|
||||
SERVERLIB= -ldl $(LIB_FLAG) $(DB_LIB) $(SSL_LIB) -lpthread
|
||||
|
||||
SERVER_O=plugin_manager.o \
|
||||
http_server.o
|
||||
#-lsocket
|
||||
|
||||
LIBOBJS = libs/ini.o \
|
||||
libs/handle.o \
|
||||
$(DB_OBJ) \
|
||||
libs/dictionary.o \
|
||||
libs/base64.o \
|
||||
libs/utils.o \
|
||||
libs/ws.o \
|
||||
libs/sha1.o \
|
||||
libs/list.o \
|
||||
libs/scheduler.o
|
||||
|
||||
PLUGINSDEP = libs/plugin.o
|
||||
|
||||
|
||||
main: initd httpd antd_plugins
|
||||
|
||||
initd:
|
||||
-mkdir -p $(LIB_PATH)
|
||||
|
||||
httpd: lib $(SERVER_O)
|
||||
$(CC) $(CFLAGS) $(SERVER_O) -o $(BUILDIRD)/httpd httpd.c $(SERVERLIB)
|
||||
cp antd $(BUILDIRD)
|
||||
|
||||
relay: lib $(SERVER_O)
|
||||
$(CC) $(CFLAGS) $(SERVER_O) -o $(BUILDIRD)/relay relay.c $(SERVERLIB)
|
||||
cp forward $(BUILDIRD)
|
||||
lib: $(LIBOBJS)
|
||||
$(CC) $(CFLAGS) $(DB_LIB) $(SSL_LIB) -shared -o $(LIB_NAME).$(EXT) $(LIBOBJS)
|
||||
cp $(LIB_NAME).$(EXT) $(LIB_PATH$)/
|
||||
%.o: %.c
|
||||
$(CC) -fPIC $(CFLAGS) -c $< -o $@
|
||||
|
||||
antd_plugins:
|
||||
- echo "make plugin"
|
||||
-for file in plugins/* ; do\
|
||||
echo $$file;\
|
||||
if [ -d "$$file" ]; then \
|
||||
make -C "$$file" clean; \
|
||||
make -C "$$file" main; \
|
||||
fi \
|
||||
done
|
||||
|
||||
plugin:
|
||||
read -r -p "Enter package name: " PKG;\
|
||||
cd plugins/$$PKG && make clean && make\
|
||||
|
||||
clean: sclean pclean
|
||||
|
||||
deb:
|
||||
-rm -r package
|
||||
-rm *.deb
|
||||
mkdir -p package/opt/www/htdocs
|
||||
mkdir package/opt/www/plugins
|
||||
mkdir package/opt/www/database package/opt/www/tmp
|
||||
mkdir package/DEBIAN
|
||||
cp $(BUILDIRD)/httpd package/opt/www
|
||||
chmod a+x package/opt/www/httpd
|
||||
cp -rf $(BUILDIRD)/plugins/* package/opt/www/plugins
|
||||
cp antd package/opt/www
|
||||
chmod a+x package/opt/www/antd
|
||||
cp config.ini.tpl package/opt/www/config.ini
|
||||
echo "Package: antd" > package/DEBIAN/control
|
||||
echo "Version: 1.0.0" >> package/DEBIAN/control
|
||||
echo "Maintainer: Xuan Sang LE" >> package/DEBIAN/control
|
||||
echo "Architecture: all" >> package/DEBIAN/control
|
||||
echo "Description: Lighweight HTTP/HTTPs server" >> package/DEBIAN/control
|
||||
dpkg-deb --build package
|
||||
-rm -r package
|
||||
|
||||
sclean:
|
||||
-rm -f *.o $(BUILDIRD)/httpd
|
||||
-rm *.$(EXT)
|
||||
pclean:
|
||||
-rm -rf $(BUILDIRD)/plugins/* libs/*.o
|
||||
-for file in plugins/* ;do \
|
||||
if [ -d "$$file" ]; then \
|
||||
make -C "$$file" clean; \
|
||||
fi \
|
||||
done
|
||||
.PRECIOUS: %.o
|
64
Makefile.am
Normal file
64
Makefile.am
Normal file
@ -0,0 +1,64 @@
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
|
||||
# AM_CFLAGS = -g -O0
|
||||
|
||||
# check for system
|
||||
#if LINUX
|
||||
# AM_CPPFLAGS = -Wl,--no-as-needed
|
||||
#else
|
||||
# AM_CPPFLAGS = -Wl,-undefined,dynamic_lookup
|
||||
#endif
|
||||
|
||||
|
||||
AM_CPPFLAGS = -W -Wall -g -std=c99 -DCONFIG_FILE=\"$(sysconfdir)/antd-config.ini\"
|
||||
# dynamic library
|
||||
lib_LTLIBRARIES = libantd.la
|
||||
libantd_la_SOURCES = lib/ini.c \
|
||||
lib/handle.c \
|
||||
lib/dictionary.c \
|
||||
lib/base64.c \
|
||||
lib/utils.c \
|
||||
lib/ws.c \
|
||||
lib/sha1.c \
|
||||
lib/list.c \
|
||||
lib/bst.c \
|
||||
lib/scheduler.c \
|
||||
lib/plugin.c
|
||||
|
||||
pkginclude_HEADERS = lib/ini.h \
|
||||
lib/handle.h \
|
||||
lib/dictionary.h \
|
||||
lib/base64.h \
|
||||
lib/utils.h \
|
||||
lib/ws.h \
|
||||
lib/sha1.h \
|
||||
lib/list.h \
|
||||
lib/bst.h \
|
||||
lib/scheduler.h \
|
||||
lib/plugin.h
|
||||
|
||||
|
||||
EXTRA_DIST = plugin_manager.h http_server.h README.md LICENSE antd-config.ini ant-d antd.service
|
||||
|
||||
# bin
|
||||
bin_PROGRAMS = antd
|
||||
# lib source files
|
||||
antd_SOURCES = plugin_manager.c \
|
||||
server.c \
|
||||
config.c \
|
||||
decode.c \
|
||||
httpd.c
|
||||
antd_LDADD = libantd.la
|
||||
|
||||
|
||||
sysconf_DATA = antd-config.ini
|
||||
|
||||
install-data-local:
|
||||
- install -c ant-d $(DESTDIR)/$(prefix)/bin
|
||||
- [ -d $(DESTDIR)/etc/systemd/system/ ] && cp antd.service $(DESTDIR)/etc/systemd/system/
|
||||
|
||||
#install-data-local: $(srcdir)/conf/config.file $(srcdir)/conf/sub1/config.file
|
||||
# mkdir $(sysconfdir)/conf
|
||||
# cp $(srcdir)/conf/config.file $(sysconfdir)/conf
|
||||
# mkdir $(sysconfdir)/conf/sub1
|
||||
# cp $(srcdir)/conf/sub1/config.file $(sysconfdir)/conf/sub1
|
36
README.md
36
README.md
@ -1,18 +1,20 @@
|
||||

|
||||
# ant-http
|
||||
[](https://travis-ci.org/lxsang/ant-http)
|
||||
|
||||
A lightweight and portable HTTP/HTTPs web server written in C:
|
||||
- New 1.0.0 version (BETA feature): Nonblocking event driven base server with configurable number of thread pool workers, good for scalability
|
||||
A lightweight HTTP/HTTPs (1.1) web server written in C:
|
||||
- Nonblocking event driven base server with configurable number of thread pool workers.
|
||||
- Initial goal is for embedded Linux, but can be used as general purpose web server.
|
||||
- Support SSL via open SSL, database via Sqlite 3, web socket integrated
|
||||
- Support:
|
||||
- SSL via open SSL,
|
||||
- builtin support Sqlite 3,
|
||||
- web socket,
|
||||
- reverse proxy
|
||||
- It is also extensible via its extension mechanism that allows to extends the server capability.
|
||||
- Grade A SSL security score by SSL Labs
|
||||

|
||||
- Page compression with gzip, deflate, cache control
|
||||
|
||||
## Plugins:
|
||||
* CGI interface for external scripting language (e.g. PHP): [https://github.com/lxsang/antd-cgi-plugin](https://github.com/lxsang/antd-cgi-plugin)
|
||||
* Lua extension [https://github.com/lxsang/antd-lua-plugin](https://github.com/lxsang/antd-lua-plugin): using Lua as serverside script
|
||||
* PHP extension [https://github.com/lxsang/antd-ph7-plugin](https://github.com/lxsang/antd-ph7-plugin): using PHP as serverside script
|
||||
* Web terminal [https://github.com/lxsang/antd-wterm-plugin](https://github.com/lxsang/antd-wterm-plugin): plugin for using Unix terminal from the web via websocket
|
||||
* Web VNC [https://github.com/lxsang/antd-wvnc-plugin](https://github.com/lxsang/antd-wvnc-plugin): Remote computer access using VNC protocol on the web (via websocket)
|
||||
|
||||
@ -25,14 +27,26 @@ A lightweight and portable HTTP/HTTPs web server written in C:
|
||||
### server dependencies
|
||||
* libssl-dev (expecting openssl v1.1.1d, only support TLSv1.2 and TLSv1.3)
|
||||
* libsqlite3-dev
|
||||
* zlib-dev
|
||||
|
||||
### build
|
||||
When all dependencies are installed, the build can be done with a few single command lines:
|
||||
With all dependencies installed:
|
||||
|
||||
```bash
|
||||
mkdir antd
|
||||
cd antd
|
||||
# build without plugin
|
||||
wget -O- https://get.bitdojo.dev/antd | bash -s ""
|
||||
# or from the distribution tarball
|
||||
tar xvzf antd-x.x.x.tar.gz
|
||||
cd antd-x.x.x
|
||||
./configure --prefix=/usr
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
The script will ask for a place to put the binaries (should be an absolute path, otherwise the build will fail) and the default HTTP port for the server config.
|
||||
### Generate distribution
|
||||
```sh
|
||||
libtoolize
|
||||
aclocal
|
||||
autoconf
|
||||
automake --add-missing
|
||||
make distcheck
|
||||
```
|
||||
|
8
antd
8
antd
@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
UNAME=`uname -s`
|
||||
|
||||
if [ "$UNAME" = "Darwin" ]; then
|
||||
DYLD_LIBRARY_PATH=$(dirname "$0")/plugins/ $(dirname "$0")/httpd $(dirname "$0")/config.ini
|
||||
else
|
||||
LD_LIBRARY_PATH=$(dirname "$0")/plugins/ $(dirname "$0")/httpd $(dirname "$0")/config.ini
|
||||
fi
|
118
antd-config.ini
Normal file
118
antd-config.ini
Normal file
@ -0,0 +1,118 @@
|
||||
[SERVER]
|
||||
; plugin directory
|
||||
plugins=/opt/www/lib/
|
||||
; plugin extension
|
||||
plugins_ext=.so
|
||||
; tmp dir
|
||||
tmpdir=/opt/www/tmp/
|
||||
; max concurent connection
|
||||
statistic_fifo=/opt/www/tmp/antd_stat
|
||||
maxcon=200
|
||||
; server backlocg
|
||||
backlog=5000
|
||||
; number of workers
|
||||
workers = 4
|
||||
; max upload file size in bytes
|
||||
max_upload_size = 10000000
|
||||
; if SSL is enable on one port, one should specify
|
||||
; the SSL cert and key files
|
||||
;Example: self signed key
|
||||
; openssl genrsa -des3 -passout pass:1234 -out keypair.key 2048
|
||||
; openssl rsa -passin pass:1234 -in keypair.key -out server.key
|
||||
; openssl req -new -key server.key -out server.csr
|
||||
; openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
|
||||
ssl.cert=/opt/www/server.crt
|
||||
ssl.key=/opt/www/server.key
|
||||
ssl.cipher=ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256
|
||||
|
||||
; enable compression
|
||||
gzip_enable = 1
|
||||
gzip_types = text\/.*,.*\/css,.*\/json,.*\/javascript
|
||||
debug_enable = 1
|
||||
; 0 to disable
|
||||
|
||||
; a configuration each port
|
||||
|
||||
[PORT:443]
|
||||
htdocs=/opt/www/htdocs
|
||||
; enable or disable SSL
|
||||
ssl.enable=0
|
||||
; other config shoud be rules applied on this port
|
||||
; For example the following rule will
|
||||
; convert a request of type:
|
||||
; name.example.com?rq=1
|
||||
;TO:
|
||||
; example.com/name/?rq=1
|
||||
; this is helpful to redirect many sub domains
|
||||
; to a sub folder of the same server
|
||||
; ^([a-zA-Z][a-zA-Z0-9]*)\.[a-zA-Z0-9]+\..*$ = /<1><url>?<query>
|
||||
; or
|
||||
; ^([a-zA-Z][a-zA-Z0-9]*)\.[a-zA-Z0-9]+\..*$ = /<1><url>?<query><break>
|
||||
; <break> prevents apply the rules below the current one when matched
|
||||
; example of reverse proxy with the proxy plugin
|
||||
; ^\/os\/+(.*)$ = http://localhost:80/os/router.lua?r=<1>&<query>
|
||||
; Sytax: [regular expression on the original request]=[new request rule]
|
||||
|
||||
[PORT:80]
|
||||
htdocs=/opt/www/htdocs
|
||||
; enable specific plugin
|
||||
plugins=lua,tunnel
|
||||
; enable or disable SSL
|
||||
ssl.enable=0
|
||||
; ^\/os\/+(.*)$ = /proxy/http://localhost:443/test.html?<query>
|
||||
; other config shoud be rules applied on this port
|
||||
; For example the following rule will
|
||||
; convert a request of type:
|
||||
; name.example.com?rq=1
|
||||
;TO:
|
||||
; example.com/name/?rq=1
|
||||
; this is helpful to redirect many sub domains
|
||||
; to a sub folder of the same server
|
||||
; ^([a-zA-Z][a-zA-Z0-9]*)\.[a-zA-Z0-9]+\..*$ = /<1><url>?<query>
|
||||
; Sytax: [regular expression on the original request]=[new request rule]
|
||||
|
||||
|
||||
|
||||
[MIMES]
|
||||
image/bmp=bmp
|
||||
image/jpeg=jpg,jpeg
|
||||
text/css=css
|
||||
text/markdown=md
|
||||
text/csv=csv
|
||||
application/pdf=pdf
|
||||
image/gif=gif
|
||||
text/html=html,htm,chtml
|
||||
application/json=json
|
||||
application/javascript=js
|
||||
image/png=png
|
||||
image/x-portable-pixmap=ppm
|
||||
application/x-rar-compressed=rar
|
||||
image/tiff=tiff
|
||||
application/x-tar=tar
|
||||
text/plain=txt
|
||||
application/x-font-ttf=ttf
|
||||
application/xhtml+xml=xhtml
|
||||
application/xml=xml
|
||||
application/zip=zip
|
||||
image/svg+xml=svg
|
||||
application/vnd.ms-fontobject=eot
|
||||
application/x-font-woff=woff,woff2
|
||||
application/x-font-otf=otf
|
||||
audio/mpeg=mp3,mpeg
|
||||
|
||||
|
||||
; Example of plugin configurations
|
||||
[PLUGIN:php]
|
||||
; theses configurations is understandable by the server
|
||||
; the name of the plugin in plugin dir
|
||||
name = fastcgi
|
||||
; run this plugin at startup
|
||||
autoload = true
|
||||
; file handle
|
||||
file_type = php,pp
|
||||
;
|
||||
; The following example configurations are application specific
|
||||
; pluggin specific configurations here, for example
|
||||
socket = /var/php.sock
|
||||
bin = /usr/bin/phpfcgi
|
||||
; etc
|
14
antd.service
Normal file
14
antd.service
Normal file
@ -0,0 +1,14 @@
|
||||
[Unit]
|
||||
Description=Antd Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/opt/www
|
||||
ExecStart=/usr/bin/ant-d
|
||||
Restart=always
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
73
build.json
Normal file
73
build.json
Normal file
@ -0,0 +1,73 @@
|
||||
{
|
||||
"name": "ant-http",
|
||||
"targets":{
|
||||
"configure": {
|
||||
"require": ["linux"],
|
||||
"jobs": [
|
||||
{
|
||||
"name": "linux-exec",
|
||||
"data": {
|
||||
"cmd":"libtoolize",
|
||||
"pwd": "home://workspace/ant-http"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-exec",
|
||||
"data": {
|
||||
"cmd":"aclocal",
|
||||
"pwd": "home://workspace/ant-http"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-exec",
|
||||
"data": {
|
||||
"cmd":"autoconf",
|
||||
"pwd": "home://workspace/ant-http"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-exec",
|
||||
"data": {
|
||||
"cmd":"automake --add-missing",
|
||||
"pwd": "home://workspace/ant-http"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "linux-exec",
|
||||
"data": {
|
||||
"cmd":"./configure --prefix=/usr --enable-debug=no",
|
||||
"pwd": "home://workspace/ant-http"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"clean": {
|
||||
"require": ["linux"],
|
||||
"jobs": [
|
||||
{
|
||||
"name": "linux-exec",
|
||||
"data": {
|
||||
"cmd":"make clean",
|
||||
"pwd": "home://workspace/ant-http"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"build": {
|
||||
"require": ["linux"],
|
||||
"jobs": [
|
||||
{
|
||||
"name": "linux-exec",
|
||||
"data": {
|
||||
"cmd":"make",
|
||||
"pwd": "home://workspace/ant-http"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"clean and build": {
|
||||
"depend": ["clean", "build"],
|
||||
"jobs": []
|
||||
}
|
||||
}
|
||||
}
|
407
config.c
Normal file
407
config.c
Normal file
@ -0,0 +1,407 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "lib/ini.h"
|
||||
#include "lib/utils.h"
|
||||
#include "config.h"
|
||||
#include "plugin_manager.h"
|
||||
|
||||
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
|
||||
|
||||
extern config_t g_server_config;
|
||||
|
||||
// define all basic mime here
|
||||
static mime_t _mimes[] = {
|
||||
{"image/bmp", "bmp"},
|
||||
{"image/jpeg", "jpg,jpeg"},
|
||||
{"text/css", "css"},
|
||||
{"text/markdown", "md"},
|
||||
{"text/csv", "csv"},
|
||||
{"application/pdf", "pdf"},
|
||||
{"image/gif", "gif"},
|
||||
{"text/html", "html"},
|
||||
{"application/json", "json"},
|
||||
{"application/javascript", "js"},
|
||||
{"image/png", "png"},
|
||||
{"text/plain", "txt"},
|
||||
{"application/xhtml+xml", "xhtml"},
|
||||
{"application/xml", "xml"},
|
||||
{"image/svg+xml", "svg"},
|
||||
{NULL, NULL}};
|
||||
|
||||
static void init_plugins()
|
||||
{
|
||||
chain_t it, it2;
|
||||
dictionary_t config;
|
||||
const char *value;
|
||||
for_each_assoc(it, g_server_config.plugins)
|
||||
{
|
||||
config = (dictionary_t)it->value;
|
||||
if (config)
|
||||
{
|
||||
for_each_assoc(it2, config)
|
||||
{
|
||||
LOG("Plugin %s: [%s] -> [%s]", it->key, it2->key, (char *)it2->value);
|
||||
if (strncmp(it2->key, "file_type", 9) == 0 && it2->value)
|
||||
{
|
||||
char *file_type = strdup((char *)it2->value);
|
||||
char *token;
|
||||
char *stringp = file_type;
|
||||
while ((token = strsep(&stringp, ",")))
|
||||
{
|
||||
trim(token, ' ');
|
||||
if (strlen(token) > 0)
|
||||
{
|
||||
dput(g_server_config.handlers, token, strdup((char *)it->key));
|
||||
LOG("Plugin %s: support %s file", it->key, token);
|
||||
}
|
||||
}
|
||||
free(file_type);
|
||||
}
|
||||
}
|
||||
value = (char *)dvalue(config, "autoload");
|
||||
if (value && (strncmp(value, "1", 1) == 0 || strncmp(value, "true", 3) == 0))
|
||||
{
|
||||
// load the plugin
|
||||
LOG("Plugin %s: auto loading...", it->key);
|
||||
UNUSED(antd_plugin_load(it->key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int config_handler(void *conf, const char *section, const char *name,
|
||||
const char *value)
|
||||
{
|
||||
config_t *pconfig = (config_t *)conf;
|
||||
regmatch_t regex_matches[2];
|
||||
char buf[255];
|
||||
char *tmp;
|
||||
struct stat st;
|
||||
// trim(section, ' ');
|
||||
// trim(value,' ');
|
||||
// trim(name,' ');
|
||||
// char * ppath = NULL;
|
||||
if (MATCH("SERVER", "plugins"))
|
||||
{
|
||||
if (stat(value, &st) == -1)
|
||||
mkdirp(value, 0755);
|
||||
tmp = realpath(value, NULL);
|
||||
if (!tmp)
|
||||
{
|
||||
ERROR("Unable to query real path for %s: %s", value, strerror(errno));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pconfig->plugins_dir)
|
||||
free(pconfig->plugins_dir);
|
||||
pconfig->plugins_dir = tmp;
|
||||
LOG("Plugin root is %s", pconfig->plugins_dir);
|
||||
}
|
||||
}
|
||||
else if (MATCH("SERVER", "plugins_ext"))
|
||||
{
|
||||
if (pconfig->plugins_ext)
|
||||
free(pconfig->plugins_ext);
|
||||
pconfig->plugins_ext = strdup(value);
|
||||
}
|
||||
else if (MATCH("SERVER", "tmpdir"))
|
||||
{
|
||||
if (stat(value, &st) == -1)
|
||||
{
|
||||
mkdirp(value, 0755);
|
||||
}
|
||||
else
|
||||
{
|
||||
removeAll(value, 0);
|
||||
}
|
||||
tmp = realpath(value, NULL);
|
||||
if (!tmp)
|
||||
{
|
||||
ERROR("Unable to query real path for %s: %s", value, strerror(errno));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pconfig->tmpdir)
|
||||
free(pconfig->tmpdir);
|
||||
pconfig->tmpdir = tmp;
|
||||
LOG("TMP root is %s", pconfig->tmpdir);
|
||||
}
|
||||
}
|
||||
else if (MATCH("SERVER", "statistic_fifo"))
|
||||
{
|
||||
if (pconfig->stat_fifo_path)
|
||||
free(pconfig->stat_fifo_path);
|
||||
pconfig->stat_fifo_path = strdup(value);
|
||||
}
|
||||
else if (MATCH("SERVER", "max_upload_size"))
|
||||
{
|
||||
pconfig->max_upload_size = atoi(value);
|
||||
}
|
||||
else if (MATCH("SERVER", "maxcon"))
|
||||
{
|
||||
pconfig->maxcon = atoi(value);
|
||||
}
|
||||
else if (MATCH("SERVER", "backlog"))
|
||||
{
|
||||
pconfig->backlog = atoi(value);
|
||||
}
|
||||
else if (MATCH("SERVER", "workers"))
|
||||
{
|
||||
pconfig->n_workers = atoi(value);
|
||||
}
|
||||
else if (MATCH("SERVER", "debug_enable"))
|
||||
{
|
||||
(void)setenv("ANTD_DEBUG", value, 1);
|
||||
pconfig->debug_enable = atoi(value);
|
||||
}
|
||||
else if (MATCH("SERVER", "scheduler_timeout"))
|
||||
{
|
||||
pconfig->scheduler_timeout = atoi(value);
|
||||
}
|
||||
#ifdef USE_ZLIB
|
||||
else if (MATCH("SERVER", "gzip_enable"))
|
||||
{
|
||||
pconfig->gzip_enable = atoi(value);
|
||||
}
|
||||
else if (MATCH("SERVER", "gzip_types"))
|
||||
{
|
||||
pconfig->gzip_types = split(value, ",");
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_OPENSSL
|
||||
else if (MATCH("SERVER", "ssl.cert"))
|
||||
{
|
||||
if (pconfig->sslcert)
|
||||
free(pconfig->sslcert);
|
||||
pconfig->sslcert = strdup(value);
|
||||
}
|
||||
else if (MATCH("SERVER", "ssl.key"))
|
||||
{
|
||||
if (pconfig->sslkey)
|
||||
free(pconfig->sslkey);
|
||||
pconfig->sslkey = strdup(value);
|
||||
}
|
||||
else if (MATCH("SERVER", "ssl.cipher"))
|
||||
{
|
||||
if (pconfig->ssl_cipher)
|
||||
free(pconfig->ssl_cipher);
|
||||
pconfig->ssl_cipher = strdup(value);
|
||||
}
|
||||
#endif
|
||||
else if (strcmp(section, "MIMES") == 0)
|
||||
{
|
||||
dput(pconfig->mimes, name, strdup(value));
|
||||
}
|
||||
else if (regex_match("PORT:\\s*([0-9]+)", section, 2, regex_matches))
|
||||
{
|
||||
memset(buf, '\0', sizeof(buf));
|
||||
memcpy(buf, section + regex_matches[1].rm_so, regex_matches[1].rm_eo - regex_matches[1].rm_so);
|
||||
port_config_t *p = dvalue(pconfig->ports, buf);
|
||||
if (!p)
|
||||
{
|
||||
p = (port_config_t *)malloc(sizeof(port_config_t));
|
||||
p->htdocs = NULL;
|
||||
p->plugins = NULL;
|
||||
p->sock = -1;
|
||||
p->type = ANTD_PROTO_ALL;
|
||||
p->rules = dict_n(1);
|
||||
dput(pconfig->ports, buf, p);
|
||||
p->port = atoi(buf);
|
||||
}
|
||||
if (strcmp(name, "htdocs") == 0)
|
||||
{
|
||||
if (stat(value, &st) == -1)
|
||||
{
|
||||
mkdirp(value, 0755);
|
||||
}
|
||||
p->htdocs = realpath(value, NULL);
|
||||
if (!p->htdocs)
|
||||
{
|
||||
ERROR("Unable to query real path for %s: %s", value, strerror(errno));
|
||||
p->htdocs = strdup(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Server root is %s", p->htdocs);
|
||||
}
|
||||
}
|
||||
else if (strcmp(name, "plugins") == 0)
|
||||
{
|
||||
p->plugins = strdup(value);
|
||||
}
|
||||
else if (strcmp(name, "ssl.enable") == 0)
|
||||
{
|
||||
p->usessl = atoi(value);
|
||||
if (p->usessl)
|
||||
pconfig->enable_ssl = 1;
|
||||
}
|
||||
else if (strcmp(name, "protocol") == 0)
|
||||
{
|
||||
if (strcmp(value, "ipv4") == 0)
|
||||
{
|
||||
p->type = ANTD_PROTO_IP_4;
|
||||
}
|
||||
else if (strcmp(value, "ipv6") == 0)
|
||||
{
|
||||
p->type = ANTD_PROTO_IP_6;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("Unknown IP protocol setting %s. Enable both.", value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// other thing should be rules
|
||||
dput(p->rules, name, strdup(value));
|
||||
}
|
||||
}
|
||||
// plugin configuration
|
||||
else if (regex_match("PLUGIN:\\s*(.*)", section, 2, regex_matches))
|
||||
{
|
||||
memset(buf, '\0', sizeof(buf));
|
||||
memcpy(buf, section + regex_matches[1].rm_so, regex_matches[1].rm_eo - regex_matches[1].rm_so);
|
||||
dictionary_t p = dvalue(pconfig->plugins, buf);
|
||||
if (!p)
|
||||
{
|
||||
p = dict();
|
||||
dput(pconfig->plugins, buf, p);
|
||||
}
|
||||
dput(p, name, strdup(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0; /* unknown section/name, error */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void load_config(const char *file)
|
||||
{
|
||||
g_server_config.ports = dict();
|
||||
g_server_config.plugins = dict();
|
||||
g_server_config.plugins_dir = strdup("plugins/");
|
||||
g_server_config.plugins_ext = strdup(".so");
|
||||
g_server_config.db_path = strdup("databases/");
|
||||
// g_server_config.htdocs = "htdocs/";
|
||||
g_server_config.tmpdir = strdup("/tmp/");
|
||||
g_server_config.stat_fifo_path = strdup("");
|
||||
g_server_config.n_workers = 4;
|
||||
g_server_config.backlog = 1000;
|
||||
g_server_config.handlers = dict();
|
||||
g_server_config.maxcon = 100;
|
||||
g_server_config.max_upload_size = 10000000; // 10Mb
|
||||
g_server_config.connection = 0;
|
||||
g_server_config.mimes = dict();
|
||||
g_server_config.enable_ssl = 0;
|
||||
g_server_config.sslcert = strdup("cert.pem");
|
||||
g_server_config.sslkey = strdup("key.pem");
|
||||
g_server_config.ssl_cipher = NULL;
|
||||
g_server_config.gzip_enable = 0;
|
||||
g_server_config.gzip_types = NULL;
|
||||
g_server_config.debug_enable = 0;
|
||||
g_server_config.scheduler_timeout = 30; // 30 s
|
||||
// put it default mimes
|
||||
for (int i = 0; _mimes[i].type != NULL; i++)
|
||||
{
|
||||
dput(g_server_config.mimes, _mimes[i].type, strdup(_mimes[i].ext));
|
||||
}
|
||||
if (ini_parse(file, config_handler, &g_server_config) < 0)
|
||||
{
|
||||
ERROR("Can't load '%s'. Used defaut configuration", file);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Using configuration : %s", file);
|
||||
#ifdef USE_OPENSSL
|
||||
LOG("SSL enable %d", g_server_config.enable_ssl);
|
||||
LOG("SSL cert %s", g_server_config.sslcert);
|
||||
LOG("SSL key %s", g_server_config.sslkey);
|
||||
/*if(!g_server_config.ssl_cipher)
|
||||
LOG("SSL Cipher suite: %s", "HIGH");
|
||||
else
|
||||
LOG("SSL Cipher suite: %s", g_server_config.ssl_cipher);*/
|
||||
#endif
|
||||
}
|
||||
set_mimes_list(g_server_config.mimes);
|
||||
#ifdef USE_ZLIB
|
||||
if(g_server_config.gzip_enable && g_server_config.gzip_types != NULL)
|
||||
{
|
||||
set_gzip_types(g_server_config.gzip_types);
|
||||
}
|
||||
#endif
|
||||
LOG("%d mimes entries found", g_server_config.mimes->size);
|
||||
// Init plugins if necessary
|
||||
init_plugins();
|
||||
}
|
||||
|
||||
void destroy_config()
|
||||
{
|
||||
chain_t it;
|
||||
freedict(g_server_config.handlers);
|
||||
if (g_server_config.plugins_dir)
|
||||
free(g_server_config.plugins_dir);
|
||||
if (g_server_config.plugins_ext)
|
||||
free(g_server_config.plugins_ext);
|
||||
if (g_server_config.db_path)
|
||||
free(g_server_config.db_path);
|
||||
if (g_server_config.tmpdir)
|
||||
free(g_server_config.tmpdir);
|
||||
if (g_server_config.ssl_cipher)
|
||||
free(g_server_config.ssl_cipher);
|
||||
if (g_server_config.gzip_types)
|
||||
{
|
||||
list_free(&g_server_config.gzip_types);
|
||||
#ifdef USE_ZLIB
|
||||
set_gzip_types(g_server_config.gzip_types);
|
||||
#endif
|
||||
}
|
||||
if (g_server_config.mimes)
|
||||
{
|
||||
freedict(g_server_config.mimes);
|
||||
set_mimes_list(NULL);
|
||||
}
|
||||
if (g_server_config.stat_fifo_path)
|
||||
free(g_server_config.stat_fifo_path);
|
||||
if (g_server_config.plugins)
|
||||
{
|
||||
for_each_assoc(it, g_server_config.plugins)
|
||||
{
|
||||
freedict((dictionary_t)it->value);
|
||||
it->value = NULL;
|
||||
}
|
||||
freedict(g_server_config.plugins);
|
||||
}
|
||||
if(g_server_config.sslcert)
|
||||
{
|
||||
free(g_server_config.sslcert);
|
||||
}
|
||||
if(g_server_config.sslkey)
|
||||
{
|
||||
free(g_server_config.sslkey);
|
||||
}
|
||||
if (g_server_config.ports)
|
||||
{
|
||||
port_config_t *cnf;
|
||||
for_each_assoc(it, g_server_config.ports)
|
||||
{
|
||||
cnf = (port_config_t *)it->value;
|
||||
if (cnf != NULL)
|
||||
{
|
||||
if (cnf->htdocs != NULL)
|
||||
free(cnf->htdocs);
|
||||
if (cnf->plugins)
|
||||
free(cnf->plugins);
|
||||
if (cnf->sock > 0)
|
||||
{
|
||||
close(cnf->sock);
|
||||
}
|
||||
freedict(cnf->rules);
|
||||
}
|
||||
}
|
||||
freedict(g_server_config.ports);
|
||||
}
|
||||
LOG("Unclosed connection: %d", g_server_config.connection);
|
||||
LOG("Config destroyed");
|
||||
}
|
52
config.h
Normal file
52
config.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include "lib/handle.h"
|
||||
|
||||
#ifndef CONFIG_FILE
|
||||
#define CONFIG_FILE "antd-config.ini"
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int port;
|
||||
int usessl;
|
||||
char *htdocs;
|
||||
char* plugins;
|
||||
int sock;
|
||||
antd_proto_t type;
|
||||
dictionary_t rules;
|
||||
} port_config_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
//int port;
|
||||
char *plugins_dir;
|
||||
char *plugins_ext;
|
||||
char *db_path;
|
||||
//char* htdocs;
|
||||
char *tmpdir;
|
||||
char *stat_fifo_path;
|
||||
dictionary_t handlers;
|
||||
int backlog;
|
||||
int maxcon;
|
||||
int connection;
|
||||
int n_workers;
|
||||
int scheduler_timeout;
|
||||
int max_upload_size;
|
||||
// ssl
|
||||
int enable_ssl;
|
||||
char *sslcert;
|
||||
char *sslkey;
|
||||
char *ssl_cipher;
|
||||
int gzip_enable;
|
||||
int debug_enable;
|
||||
list_t gzip_types;
|
||||
dictionary_t mimes;
|
||||
dictionary_t ports;
|
||||
dictionary_t plugins;
|
||||
// #endif
|
||||
} config_t;
|
||||
void load_config(const char *file);
|
||||
void destroy_config();
|
||||
#endif
|
@ -1,52 +0,0 @@
|
||||
[SERVER]
|
||||
; server port
|
||||
; use 443 if one want to use
|
||||
; SSL
|
||||
port=8080
|
||||
; plugin directory
|
||||
plugins=/opt/www/plugins/
|
||||
; plugin extension
|
||||
plugins_ext=.dylib
|
||||
; SQLITE database path
|
||||
database=/opt/www/database/
|
||||
; Website store here
|
||||
htdocs=/opt/www/htdocs
|
||||
; tmp dir
|
||||
tmpdir=/opt/www/tmp/
|
||||
; server backlocg
|
||||
backlog=5000
|
||||
; eable or disalbe SSL
|
||||
ssl.enable=0
|
||||
; if SSL is enable, one should specify
|
||||
; the SSL cert and key files
|
||||
; ssl.cert=fullchain.pem
|
||||
; ssl.key=privkey.pem
|
||||
|
||||
; This enable some plugins to be initialised at server startup
|
||||
[AUTOSTART]
|
||||
; to start a plugin at server statup use:
|
||||
;plugin = plugin_name_1
|
||||
;plugin = plugin_name_2, etc
|
||||
|
||||
; sever rules
|
||||
[RULES]
|
||||
; For example the following rule will
|
||||
; convert a request of type:
|
||||
; name.example.com?rq=1
|
||||
;TO:
|
||||
; example.com/name/?rq=1
|
||||
; this is helpful to redirect many sub domains
|
||||
; to a sub folder of the same server
|
||||
; ^([a-zA-Z][a-zA-Z0-9]*)\.[a-zA-Z0-9]+\..*$ = /<1><url>?<query>
|
||||
; Sytax: [regular expression on the original request]=[new request rule]
|
||||
|
||||
[FILEHANDLER]
|
||||
; specify a plugin for handling
|
||||
; a file type
|
||||
; lua page script
|
||||
ls = lua-api
|
||||
; pure lua script
|
||||
lua = lua-api
|
||||
; php and o ther scripting languages can be
|
||||
; handled by the cgi plugin
|
||||
; php = cgi
|
103
configure.ac
Normal file
103
configure.ac
Normal file
@ -0,0 +1,103 @@
|
||||
# initialise autoconf and set up some basic information about the program we’re packaging
|
||||
AC_INIT([antd], [2.0.0], [xsang.le@gmail.com])
|
||||
|
||||
# We’re going to use automake for this project
|
||||
AM_INIT_AUTOMAKE([subdir-objects])
|
||||
|
||||
# dependencies
|
||||
# C compiler
|
||||
AC_PROG_CC
|
||||
# libtool for linking
|
||||
AC_PROG_LIBTOOL
|
||||
# check if sqlite3 header exists
|
||||
|
||||
use_ssl=no
|
||||
# check if libssl header exists
|
||||
AC_CHECK_HEADER([openssl/ssl.h],[
|
||||
# check if the library exists
|
||||
AC_DEFINE([USE_OPENSSL], [1],[Use sqlite3])
|
||||
use_ssl=yes
|
||||
], [])
|
||||
AC_CHECK_LIB([ssl],[SSL_read],[], [
|
||||
if test "$use_ssl" = "yes"; then
|
||||
AC_MSG_ERROR([Unable to find libssl shared library])
|
||||
fi
|
||||
])
|
||||
|
||||
AC_CHECK_LIB([crypto],[ECDH_compute_key],[], [
|
||||
if test "$use_ssl" = "yes"; then
|
||||
AC_MSG_ERROR([Unable to find libcrypto shared library])
|
||||
fi
|
||||
])
|
||||
|
||||
# check for pthread
|
||||
AC_CHECK_LIB([pthread], [pthread_create], [], [
|
||||
AC_MSG_ERROR([libpthread is not found])])
|
||||
|
||||
# check for dl
|
||||
AC_CHECK_LIB([dl], [dlopen], [], [
|
||||
AC_MSG_ERROR([unable to find dlopen()])
|
||||
])
|
||||
|
||||
|
||||
# check for zlib
|
||||
use_zlib=no
|
||||
# check if libssl header exists
|
||||
AC_CHECK_HEADER([zlib.h],[
|
||||
# check if the library exists
|
||||
AC_DEFINE([USE_ZLIB], [1],[Use zlib])
|
||||
use_zlib=yes
|
||||
], [])
|
||||
AC_CHECK_LIB([z],[deflate],[], [
|
||||
if test "$use_zlib" = "yes"; then
|
||||
AC_MSG_ERROR([Unable to find zlib shared library])
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
AC_DEFINE([_GNU_SOURCE], [1],[Use GNU source])
|
||||
# AC_CANONICAL_HOST is needed to access the 'host_os' variable
|
||||
|
||||
# debug option
|
||||
#AC_ARG_ENABLE([debug],
|
||||
# [ --enable-debug Turn on debugging],
|
||||
# [case "${enableval}" in
|
||||
# yes) AC_DEFINE([DEBUG], [1],[Enable debug]) ;;
|
||||
# no) ;;
|
||||
# *) AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;;
|
||||
# esac],[debug=false])
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
build_linux=no
|
||||
build_windows=no
|
||||
build_mac=no
|
||||
# Detect the target system
|
||||
case "${host_os}" in
|
||||
linux*)
|
||||
AC_DEFINE([LINUX], [1],[Linux system])
|
||||
build_linux=yes
|
||||
;;
|
||||
darwin*)
|
||||
build_mac=yes
|
||||
AC_DEFINE([MACOS], [1],[MacOS system])
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(["OS $host_os is not supported"])
|
||||
;;
|
||||
esac
|
||||
# case for window:
|
||||
# cygwin*|mingw*)
|
||||
# build_windows=yes
|
||||
# ;;
|
||||
# Pass the conditionals to automake
|
||||
AM_CONDITIONAL([LINUX], [test "$build_linux" = "yes"])
|
||||
AM_CONDITIONAL([WINDOWS], [test "$build_windows" = "yes"])
|
||||
AM_CONDITIONAL([OSX], [test "$build_mac" = "yes"])
|
||||
|
||||
# find a file called Makefile.in, substitute placeholders
|
||||
# like @PACKAGE_VERSION@ with values like 0.1.0a,
|
||||
# and write the results to Makefile.
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
|
||||
# output the script:
|
||||
AC_OUTPUT
|
777
decode.c
Normal file
777
decode.c
Normal file
@ -0,0 +1,777 @@
|
||||
#ifdef USE_OPENSSL
|
||||
#include <openssl/sha.h>
|
||||
#else
|
||||
#include "lib/sha1.h"
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "decode.h"
|
||||
#include "lib/handle.h"
|
||||
#include "lib/utils.h"
|
||||
#include "lib/scheduler.h"
|
||||
#include "server.h"
|
||||
#include "lib/base64.h"
|
||||
#include "config.h"
|
||||
|
||||
#define WS_MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||
#define HEADER_MAX_SIZE 8192
|
||||
|
||||
extern config_t g_server_config;
|
||||
|
||||
static int rule_check(const char *k, const char *v, const char *host, const char *_url, const char *_query, char *buf)
|
||||
{
|
||||
// first perfom rule check on host, if not success, perform on url
|
||||
regmatch_t key_matches[10];
|
||||
regmatch_t val_matches[2];
|
||||
char *query = strdup(_query);
|
||||
char *url = strdup(_url);
|
||||
int ret;
|
||||
char *target;
|
||||
char *tmp, rep[10];
|
||||
int idx = 0;
|
||||
memset(rep, 0, 10);
|
||||
LOG("Verify %s=%s on %s or %s", k, v, url, host);
|
||||
// 1 group
|
||||
if (!host || !(ret = regex_match(k, host, 10, key_matches)))
|
||||
{
|
||||
target = url;
|
||||
ret = regex_match(k, url, 10, key_matches);
|
||||
}
|
||||
else
|
||||
target = (char *)host;
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
free(url);
|
||||
free(query);
|
||||
return 0;
|
||||
}
|
||||
LOG("Match found on %s", target);
|
||||
tmp = (char *)v;
|
||||
char *search = "<([a-zA-Z0-9]+)>";
|
||||
while ((ret = regex_match(search, tmp, 2, val_matches)))
|
||||
{
|
||||
memcpy(buf + idx, tmp, val_matches[1].rm_so - 1);
|
||||
idx += val_matches[1].rm_so - 1;
|
||||
memcpy(rep, tmp + val_matches[1].rm_so, val_matches[1].rm_eo - val_matches[1].rm_so);
|
||||
if (strcasecmp(rep, "url") == 0)
|
||||
{
|
||||
memcpy(buf + idx, url, strlen(url));
|
||||
idx += strlen(url);
|
||||
}
|
||||
else if (strcasecmp(rep, "query") == 0)
|
||||
{
|
||||
memcpy(buf + idx, query, strlen(query));
|
||||
idx += strlen(query);
|
||||
}
|
||||
else if (match_int(rep))
|
||||
{
|
||||
int i = atoi(rep);
|
||||
memcpy(buf + idx, target + key_matches[i].rm_so, key_matches[i].rm_eo - key_matches[i].rm_so);
|
||||
idx += key_matches[i].rm_eo - key_matches[i].rm_so;
|
||||
}
|
||||
else if (strcasecmp(rep, "break") == 0)
|
||||
{
|
||||
// ignore it
|
||||
LOG("Found break command, will break after this rule");
|
||||
}
|
||||
else
|
||||
{ // just keep it
|
||||
memcpy(buf + idx, tmp + val_matches[1].rm_so - 1, val_matches[1].rm_eo + 2 - val_matches[1].rm_so);
|
||||
idx += val_matches[1].rm_eo + 2 - val_matches[1].rm_so;
|
||||
}
|
||||
tmp += val_matches[1].rm_eo + 1;
|
||||
// break;
|
||||
}
|
||||
// now modify the match 2 group
|
||||
if (idx > 0)
|
||||
{
|
||||
if (tmp)
|
||||
{
|
||||
// copy the remainning of tmp
|
||||
memcpy(buf + idx, tmp, strlen(tmp));
|
||||
idx += strlen(tmp);
|
||||
}
|
||||
buf[idx] = '\0';
|
||||
}
|
||||
free(url);
|
||||
free(query);
|
||||
LOG("New URI is %s", buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *apply_rules(dictionary_t rules, const char *host, char *url)
|
||||
{
|
||||
// rule check
|
||||
char *query_string = url;
|
||||
while ((*query_string != '?') && (*query_string != '\0'))
|
||||
query_string++;
|
||||
if (*query_string == '?')
|
||||
{
|
||||
*query_string = '\0';
|
||||
query_string++;
|
||||
}
|
||||
// char* oldurl = strdup(url);
|
||||
chain_t it;
|
||||
char *k;
|
||||
char *v;
|
||||
int should_break = 0;
|
||||
for_each_assoc(it, rules)
|
||||
{
|
||||
k = it->key;
|
||||
if (it->value)
|
||||
{
|
||||
v = (char *)it->value;
|
||||
// 1 group
|
||||
if (regex_match("<break>$", v, 0, NULL))
|
||||
{
|
||||
should_break = 1;
|
||||
}
|
||||
if (rule_check(k, v, host, url, query_string, url))
|
||||
{
|
||||
query_string = url;
|
||||
|
||||
while ((*query_string != '?') && (*query_string != '\0'))
|
||||
query_string++;
|
||||
if (*query_string == '?')
|
||||
{
|
||||
*query_string = '\0';
|
||||
query_string++;
|
||||
}
|
||||
if (should_break)
|
||||
{
|
||||
i = rules->cap;
|
||||
LOG("Break rule check as matched found at %s -> %s", k, v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return strdup(query_string);
|
||||
}
|
||||
|
||||
static void ws_confirm_request(void *client, const char *key)
|
||||
{
|
||||
char buf[256];
|
||||
char rkey[128];
|
||||
char sha_d[20];
|
||||
char base64[64];
|
||||
strncpy(rkey, key, sizeof(rkey) - 1);
|
||||
int n = (int)sizeof(rkey) - (int)strlen(key);
|
||||
if (n < 0)
|
||||
n = 0;
|
||||
strncat(rkey, WS_MAGIC_STRING, n);
|
||||
#ifdef USE_OPENSSL
|
||||
SHA_CTX context;
|
||||
#else
|
||||
SHA1_CTX context;
|
||||
#endif
|
||||
|
||||
SHA1_Init(&context);
|
||||
SHA1_Update(&context, rkey, strlen(rkey));
|
||||
SHA1_Final((uint8_t *)sha_d, &context);
|
||||
Base64encode(base64, sha_d, 20);
|
||||
// send accept to client
|
||||
sprintf(buf, "HTTP/1.1 101 Switching Protocols\r\n");
|
||||
antd_send(client, buf, strlen(buf));
|
||||
sprintf(buf, "Upgrade: websocket\r\n");
|
||||
antd_send(client, buf, strlen(buf));
|
||||
sprintf(buf, "Connection: Upgrade\r\n");
|
||||
antd_send(client, buf, strlen(buf));
|
||||
sprintf(buf, "Sec-WebSocket-Accept: %s\r\n", base64);
|
||||
antd_send(client, buf, strlen(buf));
|
||||
sprintf(buf, "\r\n");
|
||||
antd_send(client, buf, strlen(buf));
|
||||
|
||||
LOG("%s", "Websocket is now enabled for plugin");
|
||||
}
|
||||
|
||||
static void *decode_request(void *data)
|
||||
{
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
dictionary_t headers = dvalue(rq->request, "REQUEST_HEADER");
|
||||
int ws = 0;
|
||||
char *ws_key = NULL;
|
||||
char *method = NULL;
|
||||
char *tmp;
|
||||
antd_task_t *task = NULL;
|
||||
ws_key = (char *)dvalue(headers, "Sec-WebSocket-Key");
|
||||
tmp = (char *)dvalue(headers, "Upgrade");
|
||||
if (tmp && strcasecmp(tmp, "websocket") == 0)
|
||||
ws = 1;
|
||||
method = (char *)dvalue(rq->request, "METHOD");
|
||||
task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
// antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE);
|
||||
if (EQU(method, "GET"))
|
||||
{
|
||||
// if(ctype) free(ctype);
|
||||
if (ws && ws_key != NULL)
|
||||
{
|
||||
ws_confirm_request(rq->client, ws_key);
|
||||
// insert wsocket flag to request
|
||||
// plugin should handle this ugraded connection
|
||||
// not the server
|
||||
dput(rq->request, "__web_socket__", strdup("1"));
|
||||
}
|
||||
// resolve task
|
||||
task->handle = resolve_request;
|
||||
return task;
|
||||
}
|
||||
else if (EQU(method, "HEAD") || EQU(method, "OPTIONS") || EQU(method, "DELETE"))
|
||||
{
|
||||
task->handle = resolve_request;
|
||||
return task;
|
||||
}
|
||||
else if (EQU(method, "POST") || EQU(method, "PUT") || EQU(method, "PATCH"))
|
||||
{
|
||||
task->handle = resolve_request;
|
||||
return task;
|
||||
}
|
||||
else
|
||||
{
|
||||
antd_error(rq->client, 501, "Request Method Not Implemented");
|
||||
return task;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current request is e reverse proxy
|
||||
* return a proxy task if this is the case
|
||||
*/
|
||||
static void *check_proxy(antd_request_t *rq, const char *path, const char *query)
|
||||
{
|
||||
char *pattern = "^(https?)://([^:]+):([0-9]+)(.*)$";
|
||||
antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
char buff[256];
|
||||
regmatch_t matches[5];
|
||||
int ret, size;
|
||||
ret = regex_match(pattern, path, 5, matches);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
free(task);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (matches[1].rm_eo - matches[1].rm_so == 5)
|
||||
{
|
||||
// https is not supported for now
|
||||
// TODO add https support
|
||||
antd_error(rq->client, 503, "Service Unavailable");
|
||||
return task;
|
||||
}
|
||||
// http proxy request
|
||||
size = matches[2].rm_eo - matches[2].rm_so < (int)sizeof(buff) ? matches[2].rm_eo - matches[2].rm_so : (int)sizeof(buff);
|
||||
(void)memcpy(buff, path + matches[2].rm_so, size);
|
||||
buff[size] = '\0';
|
||||
dput(rq->request, "PROXY_HOST", strdup(buff));
|
||||
|
||||
size = matches[3].rm_eo - matches[3].rm_so < (int)sizeof(buff) ? matches[3].rm_eo - matches[3].rm_so : (int)sizeof(buff);
|
||||
(void)memcpy(buff, path + matches[3].rm_so, size);
|
||||
buff[size] = '\0';
|
||||
dput(rq->request, "PROXY_PORT", strdup(buff));
|
||||
|
||||
dput(rq->request, "PROXY_PATH", strdup(path + matches[4].rm_so));
|
||||
dput(rq->request, "PROXY_QUERY", strdup(query));
|
||||
|
||||
task->handle = proxify;
|
||||
antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE | TASK_EVT_ON_WRITABLE);
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the cookie header to a dictionary
|
||||
* @param client The client socket
|
||||
* @return The Dictionary socket or NULL
|
||||
*/
|
||||
static void decode_cookie(const char *line, dictionary_t dic)
|
||||
{
|
||||
char *token, *token1;
|
||||
char *cpstr = strdup(line);
|
||||
char *orgcpy = cpstr;
|
||||
trim(cpstr, ' ');
|
||||
trim(cpstr, '\n');
|
||||
trim(cpstr, '\r');
|
||||
|
||||
while ((token = strsep(&cpstr, ";")))
|
||||
{
|
||||
trim(token, ' ');
|
||||
token1 = strsep(&token, "=");
|
||||
if (token1 && token && strlen(token) > 0)
|
||||
{
|
||||
dput(dic, token1, strdup(token));
|
||||
}
|
||||
}
|
||||
free(orgcpy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a query string (GET request or POST URL encoded) to
|
||||
* a dictionary of key-value
|
||||
* @param query : the query string
|
||||
* @return a dictionary of key-value
|
||||
*/
|
||||
static void decode_url_request(const char *query, dictionary_t dic)
|
||||
{
|
||||
if (query == NULL)
|
||||
return;
|
||||
// str_copy = ;
|
||||
char *token;
|
||||
if (strlen(query) == 0)
|
||||
return;
|
||||
char *str_copy = strdup(query);
|
||||
char *org_copy = str_copy;
|
||||
// dictionary dic = dict();
|
||||
while ((token = strsep(&str_copy, "&")))
|
||||
{
|
||||
char *key;
|
||||
char *val = NULL;
|
||||
if (strlen(token) > 0)
|
||||
{
|
||||
key = strsep(&token, "=");
|
||||
if (key && strlen(key) > 0)
|
||||
{
|
||||
val = strsep(&token, "=");
|
||||
if (!val)
|
||||
val = "";
|
||||
dput(dic, key, url_decode(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
free(org_copy);
|
||||
// return dic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the HTTP request header
|
||||
*/
|
||||
|
||||
void *decode_request_header(void *data)
|
||||
{
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
rq->client->state = ANTD_CLIENT_HEADER_DECODE;
|
||||
dictionary_t cookie = NULL;
|
||||
char *line;
|
||||
char *token;
|
||||
char *query = NULL;
|
||||
char *host = NULL;
|
||||
char buf[2 * BUFFLEN];
|
||||
int header_size = 0;
|
||||
int ret;
|
||||
char *url = (char *)dvalue(rq->request, "REQUEST_QUERY");
|
||||
dictionary_t xheader = dvalue(rq->request, "REQUEST_HEADER");
|
||||
dictionary_t request = dvalue(rq->request, "REQUEST_DATA");
|
||||
char *port_s = (char *)dvalue(rq->request, "SERVER_PORT");
|
||||
port_config_t *pcnf = (port_config_t *)dvalue(g_server_config.ports, port_s);
|
||||
antd_task_t *task = NULL;
|
||||
// first real all header
|
||||
// this for check if web socket is enabled
|
||||
|
||||
while (((ret = read_buf(rq->client, buf, sizeof(buf))) > 0) && strcmp("\r\n", buf))
|
||||
{
|
||||
header_size += ret;
|
||||
line = buf;
|
||||
trim(line, '\n');
|
||||
trim(line, '\r');
|
||||
token = strsep(&line, ":");
|
||||
trim(token, ' ');
|
||||
trim(line, ' ');
|
||||
if (token && line && strlen(line) > 0)
|
||||
{
|
||||
verify_header(token);
|
||||
dput(xheader, token, strdup(line));
|
||||
}
|
||||
if (token != NULL && strcasecmp(token, "Cookie") == 0)
|
||||
{
|
||||
if (!cookie)
|
||||
{
|
||||
cookie = dict();
|
||||
}
|
||||
decode_cookie(line, cookie);
|
||||
}
|
||||
else if (token != NULL && strcasecmp(token, "Host") == 0)
|
||||
{
|
||||
host = strdup(line);
|
||||
}
|
||||
if (header_size > HEADER_MAX_SIZE)
|
||||
{
|
||||
antd_error(rq->client, 413, "Payload Too Large");
|
||||
ERROR("Header size too large (%d): %d vs %d", rq->client->sock, header_size, HEADER_MAX_SIZE);
|
||||
task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
return task;
|
||||
}
|
||||
}
|
||||
if (ret == 0)
|
||||
{
|
||||
// antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE);
|
||||
task = antd_create_task(decode_request_header, (void *)rq, NULL, rq->client->last_io);
|
||||
antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE);
|
||||
return task;
|
||||
}
|
||||
// check for content length size
|
||||
line = (char *)dvalue(xheader, "Content-Length");
|
||||
if (line)
|
||||
{
|
||||
int clen = atoi(line);
|
||||
if (clen > g_server_config.max_upload_size)
|
||||
{
|
||||
antd_error(rq->client, 413, "Request body data is too large");
|
||||
// dirty fix, wait for message to be sent
|
||||
// 100 ms sleep
|
||||
usleep(100000);
|
||||
task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
return task;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_ZLIB
|
||||
// check for gzip
|
||||
line = (char *)dvalue(xheader, "Accept-Encoding");
|
||||
if (line)
|
||||
{
|
||||
if (regex_match("gzip", line, 0, NULL))
|
||||
{
|
||||
rq->client->z_level = ANTD_CGZ;
|
||||
}
|
||||
else if (regex_match("deflate", line, 0, NULL))
|
||||
{
|
||||
rq->client->z_level = ANTD_CDEFL;
|
||||
}
|
||||
else
|
||||
{
|
||||
rq->client->z_level = ANTD_CNONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rq->client->z_level = ANTD_CNONE;
|
||||
}
|
||||
#endif
|
||||
// if(line) free(line);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
strncat(buf, url, sizeof(buf) - 1);
|
||||
LOG("Original query (%d): %s", rq->client->sock, url);
|
||||
query = apply_rules(pcnf->rules, host, buf);
|
||||
LOG("Processed query: %s", query);
|
||||
if (cookie)
|
||||
dput(rq->request, "COOKIE", cookie);
|
||||
if (host)
|
||||
free(host);
|
||||
|
||||
// check if this is a reverse proxy ?
|
||||
task = check_proxy(rq, buf, query);
|
||||
if (task)
|
||||
{
|
||||
if (query)
|
||||
free(query);
|
||||
return task;
|
||||
}
|
||||
LOG("REQUEST_URI:%s", buf);
|
||||
dput(rq->request, "REQUEST_URI", url_decode(buf));
|
||||
if (query)
|
||||
{
|
||||
decode_url_request(query, request);
|
||||
dput(rq->request, "REQUEST_QUERY", query);
|
||||
//if(url)
|
||||
// free(url);
|
||||
}
|
||||
// header ok, now checkmethod
|
||||
task = antd_create_task(decode_request, (void *)rq, NULL, rq->client->last_io);
|
||||
antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE); //
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Decode post query string to string
|
||||
*/
|
||||
static char *post_data_decode(void *client, int len)
|
||||
{
|
||||
char *query = (char *)malloc((len + 1) * sizeof(char));
|
||||
char *ptr = query;
|
||||
int readlen = len > BUFFLEN ? BUFFLEN : len;
|
||||
int read = 0, stat = 1;
|
||||
while (readlen > 0 && stat >= 0)
|
||||
{
|
||||
stat = antd_recv_upto(client, ptr + read, readlen);
|
||||
if (stat > 0)
|
||||
{
|
||||
read += stat;
|
||||
readlen = (len - read) > BUFFLEN ? BUFFLEN : (len - read);
|
||||
}
|
||||
if (stat == 0)
|
||||
{
|
||||
if (difftime(time(NULL), ((antd_client_t *)client)->last_io) > MAX_IO_WAIT_TIME)
|
||||
{
|
||||
stat = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
usleep(POLL_EVENT_TO * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (read > 0)
|
||||
query[read] = '\0';
|
||||
else
|
||||
{
|
||||
free(query);
|
||||
query = NULL;
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
static void *decode_multi_part_request_data(void *data)
|
||||
{
|
||||
// loop through each part separated by the boundary
|
||||
char *line;
|
||||
char *part_name = NULL;
|
||||
char *part_file = NULL;
|
||||
char *file_path;
|
||||
char buf[BUFFLEN];
|
||||
char *field;
|
||||
int len;
|
||||
// dictionary dic = NULL;
|
||||
int fd = -1;
|
||||
char *token, *keytoken, *valtoken;
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE);
|
||||
char *boundary = (char *)dvalue(rq->request, "MULTI_PART_BOUNDARY");
|
||||
dictionary_t dic = (dictionary_t)dvalue(rq->request, "REQUEST_DATA");
|
||||
// search for content disposition:
|
||||
while (((len = read_buf(rq->client, buf, sizeof(buf))) > 0) && !strstr(buf, "Content-Disposition:"))
|
||||
;
|
||||
;
|
||||
if (len <= 0 || !strstr(buf, "Content-Disposition:"))
|
||||
{
|
||||
return task;
|
||||
}
|
||||
char *boundend = __s("%s--", boundary);
|
||||
line = buf;
|
||||
// extract parameters from header
|
||||
while ((token = strsep(&line, ";")))
|
||||
{
|
||||
keytoken = strsep(&token, "=");
|
||||
if (keytoken && strlen(keytoken) > 0)
|
||||
{
|
||||
trim(keytoken, ' ');
|
||||
valtoken = strsep(&token, "=");
|
||||
if (valtoken)
|
||||
{
|
||||
trim(valtoken, ' ');
|
||||
trim(valtoken, '\n');
|
||||
trim(valtoken, '\r');
|
||||
trim(valtoken, '\"');
|
||||
if (strcmp(keytoken, "name") == 0)
|
||||
{
|
||||
part_name = strdup(valtoken);
|
||||
}
|
||||
else if (strcmp(keytoken, "filename") == 0)
|
||||
{
|
||||
part_file = strdup(valtoken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
line = NULL;
|
||||
// get the binary data
|
||||
LOG("Part file: %s part name: %s", part_file, part_name);
|
||||
if (part_name != NULL)
|
||||
{
|
||||
// go to the beginning of data bock
|
||||
while ((len = read_buf(rq->client, buf, sizeof(buf))) > 0 && strncmp(buf, "\r\n", 2) != 0)
|
||||
;
|
||||
;
|
||||
|
||||
if (part_file == NULL)
|
||||
{
|
||||
/**
|
||||
* WARNING:
|
||||
* This allow only 1024 bytes of data (max),
|
||||
* out of this range, the data is cut out.
|
||||
* Need an efficient way to handle this
|
||||
*/
|
||||
len = read_buf(rq->client, buf, sizeof(buf));
|
||||
if (len > 0)
|
||||
{
|
||||
line = buf;
|
||||
trim(line, '\n');
|
||||
trim(line, '\r');
|
||||
trim(line, ' ');
|
||||
dput(dic, part_name, strdup(line));
|
||||
}
|
||||
// find the next boundary
|
||||
while ((len = read_buf(rq->client, buf, sizeof(buf))) > 0 && !strstr(buf, boundary))
|
||||
{
|
||||
line = buf;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
file_path = __s("%s/%s.%u", g_server_config.tmpdir, part_file, (unsigned)time(NULL));
|
||||
fd = open(file_path, O_WRONLY | O_CREAT, 0600);
|
||||
if (fd > 0)
|
||||
{
|
||||
int totalsize = 0, len = 0;
|
||||
// read until the next boundary
|
||||
// TODO: this is not efficient for big file
|
||||
// need a solution
|
||||
while ((len = read_buf(rq->client, buf, sizeof(buf))) > 0 && !strstr(buf, boundary))
|
||||
{
|
||||
len = guard_write(fd, buf, len);
|
||||
totalsize += len;
|
||||
}
|
||||
|
||||
// remove \r\n at the end
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
// fseek(fp,-2, SEEK_CUR);
|
||||
totalsize -= 2;
|
||||
int stat = ftruncate(fd, totalsize);
|
||||
LOG("Write %d bytes to %s", totalsize, file_path);
|
||||
UNUSED(stat);
|
||||
close(fd);
|
||||
line = buf;
|
||||
|
||||
field = __s("%s.file", part_name);
|
||||
dput(dic, field, strdup(part_file));
|
||||
free(field);
|
||||
field = __s("%s.tmp", part_name);
|
||||
dput(dic, field, strdup(file_path));
|
||||
free(field);
|
||||
field = __s("%s.size", part_name);
|
||||
dput(dic, field, __s("%d", totalsize));
|
||||
free(field);
|
||||
field = __s("%s.ext", part_name);
|
||||
dput(dic, field, ext(part_file));
|
||||
free(field);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("Cannot write file to :%s", file_path);
|
||||
}
|
||||
free(file_path);
|
||||
free(part_file);
|
||||
}
|
||||
free(part_name);
|
||||
}
|
||||
/**
|
||||
* The upload procedure may take time, the task access time should be updated
|
||||
* after the procedure finish
|
||||
*/
|
||||
task->access_time = rq->client->last_io;
|
||||
// check if end of request
|
||||
if (line && strstr(line, boundend))
|
||||
{
|
||||
// LOG("End request %s", boundend);
|
||||
free(boundend);
|
||||
// antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE);
|
||||
return task;
|
||||
}
|
||||
free(boundend);
|
||||
if (line && strstr(line, boundary))
|
||||
{
|
||||
// continue upload
|
||||
// antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE);
|
||||
task->handle = decode_multi_part_request_data;
|
||||
return task;
|
||||
}
|
||||
// antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE);
|
||||
return task;
|
||||
}
|
||||
/**
|
||||
* Decode the multi-part form data from the POST request
|
||||
* If it is a file upload, copy the file to tmp dir
|
||||
*/
|
||||
static void *decode_multi_part_request(void *data, const char *ctype)
|
||||
{
|
||||
char *boundary;
|
||||
char line[BUFFLEN];
|
||||
char *str_copy = (char *)ctype;
|
||||
int len;
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
// antd_task_bind_event(task, rq->client->sock, 0, );
|
||||
antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE);
|
||||
// dictionary dic = NULL;
|
||||
boundary = strsep(&str_copy, "="); // discard first part
|
||||
boundary = str_copy;
|
||||
if (boundary && strlen(boundary) > 0)
|
||||
{
|
||||
// dic = dict();
|
||||
trim(boundary, ' ');
|
||||
dput(rq->request, "MULTI_PART_BOUNDARY", strdup(boundary));
|
||||
// find first boundary
|
||||
while (((len = read_buf(rq->client, line, sizeof(line))) > 0) && !strstr(line, boundary))
|
||||
;
|
||||
if (len > 0)
|
||||
{
|
||||
task->handle = decode_multi_part_request_data;
|
||||
}
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
void *decode_post_request(void *data)
|
||||
{
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
rq->client->state = ANTD_CLIENT_RQ_DATA_DECODE;
|
||||
dictionary_t request = dvalue(rq->request, "REQUEST_DATA");
|
||||
dictionary_t headers = dvalue(rq->request, "REQUEST_HEADER");
|
||||
char *ctype = NULL;
|
||||
int clen = -1;
|
||||
char *tmp;
|
||||
antd_task_t *task = NULL;
|
||||
ctype = (char *)dvalue(headers, "Content-Type");
|
||||
tmp = (char *)dvalue(headers, "Content-Length");
|
||||
if (tmp)
|
||||
clen = atoi(tmp);
|
||||
char *method = (char *)dvalue(rq->request, "METHOD");
|
||||
task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
// antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE);
|
||||
if (!method || (!EQU(method, "POST") && !EQU(method, "PUT") && !EQU(method, "PATCH")))
|
||||
return task;
|
||||
if (ctype == NULL || clen == -1)
|
||||
{
|
||||
antd_error(rq->client, 400, "Bad Request, missing content description");
|
||||
return task;
|
||||
}
|
||||
// decide what to do with the data
|
||||
if (strstr(ctype, FORM_URL_ENCODE))
|
||||
{
|
||||
char *pquery = post_data_decode(rq->client, clen);
|
||||
if (pquery)
|
||||
{
|
||||
decode_url_request(pquery, request);
|
||||
free(pquery);
|
||||
}
|
||||
else if (clen > 0)
|
||||
{
|
||||
// WARN: this may not work on ssl socket
|
||||
// antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE | TASK_EVT_ON_WRITABLE);
|
||||
antd_error(rq->client, 400, "Bad Request, missing content data");
|
||||
return task;
|
||||
}
|
||||
}
|
||||
else if (strstr(ctype, FORM_MULTI_PART))
|
||||
{
|
||||
free(task);
|
||||
return decode_multi_part_request(rq, ctype);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*let plugin hande this data as we dont known how to deal with it*/
|
||||
dput(request, "HAS_RAW_BODY", strdup("true"));
|
||||
}
|
||||
antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE);
|
||||
return task;
|
||||
}
|
7
decode.h
Normal file
7
decode.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef DECODE_H
|
||||
#define DECODE_H
|
||||
|
||||
void *decode_request_header(void *data);
|
||||
void *decode_post_request(void *data);
|
||||
|
||||
#endif
|
BIN
dist/antd-1.0.4b.tar.gz
vendored
Normal file
BIN
dist/antd-1.0.4b.tar.gz
vendored
Normal file
Binary file not shown.
BIN
dist/antd-1.0.5b.tar.gz
vendored
Normal file
BIN
dist/antd-1.0.5b.tar.gz
vendored
Normal file
Binary file not shown.
BIN
dist/antd-1.0.6b.tar.gz
vendored
Normal file
BIN
dist/antd-1.0.6b.tar.gz
vendored
Normal file
Binary file not shown.
8
forward
8
forward
@ -1,8 +0,0 @@
|
||||
#!/bin/sh
|
||||
UNAME=`uname -s`
|
||||
|
||||
if [ "$UNAME" = "Darwin" ]; then
|
||||
DYLD_LIBRARY_PATH=$(dirname "$0")/plugins/ $(dirname "$0")/relay
|
||||
else
|
||||
LD_LIBRARY_PATH=$(dirname "$0")/plugins/ $(dirname "$0")/relay
|
||||
fi
|
1118
http_server.c
1118
http_server.c
File diff suppressed because it is too large
Load Diff
@ -1,48 +0,0 @@
|
||||
#ifndef HTTP_SERVER
|
||||
#define HTTP_SERVER
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include "libs/handle.h"
|
||||
#include "libs/scheduler.h"
|
||||
#include "plugin_manager.h"
|
||||
|
||||
#define PLUGIN_HANDLER "handle"
|
||||
#define WS_MAGIC_STRING "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
|
||||
#define CONFIG "config.ini"
|
||||
|
||||
config_t* config();
|
||||
void destroy_config();
|
||||
void load_config(const char* file);
|
||||
void* accept_request(void*);
|
||||
void* finish_request(void*);
|
||||
void cat(void*, FILE *);
|
||||
void cannot_execute(void*);
|
||||
//int get_line(int, char *, int);
|
||||
void not_found(void*);
|
||||
void* serve_file(void*);
|
||||
int startup(unsigned *);
|
||||
void unimplemented(void*);
|
||||
void badrequest(void*);
|
||||
int rule_check(const char*, const char*, const char* , const char* , const char* , char*);
|
||||
void ws_confirm_request(void*, const char*);
|
||||
char* post_url_decode(void* client,int len);
|
||||
void decode_url_request(const char* query, dictionary);
|
||||
void* decode_request_header(void* data);
|
||||
void* decode_request(void* data);
|
||||
void* decode_post_request(void* data);
|
||||
void* resolve_request(void* data);
|
||||
void* decode_multi_part_request(void*,const char*);
|
||||
void* decode_multi_part_request_data(void* data);
|
||||
dictionary decode_cookie(const char*);
|
||||
char* post_data_decode(void*,int);
|
||||
void set_nonblock(int);
|
||||
void* execute_plugin(void* data, const char *path);
|
||||
|
||||
#endif
|
596
httpd.c
596
httpd.c
@ -1,30 +1,46 @@
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#ifdef USE_OPENSSL
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include "server.h"
|
||||
#include "lib/scheduler.h"
|
||||
#include "lib/utils.h"
|
||||
#include "config.h"
|
||||
#include "plugin_manager.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include "http_server.h"
|
||||
#include "libs/ini.h"
|
||||
#define SEND_STAT(fd, buff, ret, ...) \
|
||||
snprintf(buff, BUFFLEN, ##__VA_ARGS__); \
|
||||
ret = write(fd, buff, strlen(buff));
|
||||
|
||||
extern config_t g_server_config;
|
||||
|
||||
static antd_scheduler_t *scheduler;
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
|
||||
// define the cipher suit used
|
||||
// dirty hack, this should be configured by the configuration file
|
||||
#define CIPHER_SUIT "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256"
|
||||
#define CIPHER_SUIT "HIGH"
|
||||
|
||||
static antd_scheduler_t scheduler;
|
||||
static int server_sock = -1;
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
static int ssl_session_ctx_id = 1;
|
||||
SSL_CTX *ctx;
|
||||
void init_openssl()
|
||||
{
|
||||
SSL_load_error_strings();
|
||||
static void init_openssl()
|
||||
{
|
||||
SSL_load_error_strings();
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
}
|
||||
|
||||
void cleanup_openssl()
|
||||
{
|
||||
EVP_cleanup();
|
||||
}
|
||||
|
||||
SSL_CTX *create_context()
|
||||
static SSL_CTX *create_context()
|
||||
{
|
||||
const SSL_METHOD *method;
|
||||
SSL_CTX *ctx;
|
||||
@ -32,202 +48,416 @@ SSL_CTX *create_context()
|
||||
method = SSLv23_server_method();
|
||||
|
||||
ctx = SSL_CTX_new(method);
|
||||
if (!ctx) {
|
||||
perror("Unable to create SSL context");
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
if (!ctx)
|
||||
{
|
||||
ERROR("Unable to create SSL context");
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void configure_context(SSL_CTX *ctx)
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
static unsigned char antd_protocols[] = {
|
||||
// TODO: add support to HTTP/2 protocol: 2,'h', '2',
|
||||
8, 'h', 't', 't', 'p', '/', '1', '.', '1'};
|
||||
static int alpn_advertise_protos_cb(SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg)
|
||||
{
|
||||
UNUSED(ssl);
|
||||
UNUSED(arg);
|
||||
*out = antd_protocols;
|
||||
*outlen = sizeof(antd_protocols);
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
static int alpn_select_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
|
||||
{
|
||||
UNUSED(ssl);
|
||||
UNUSED(arg);
|
||||
if (SSL_select_next_proto((unsigned char **)out, outlen, antd_protocols, sizeof(antd_protocols), in, inlen) == OPENSSL_NPN_NEGOTIATED)
|
||||
{
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("No protocol support overlap found between client and server\n");
|
||||
return SSL_TLSEXT_ERR_ALERT_FATAL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
static void configure_context(SSL_CTX *ctx)
|
||||
{
|
||||
#if defined(SSL_CTX_set_ecdh_auto)
|
||||
SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||
#else
|
||||
SSL_CTX_set_tmp_ecdh(ctx, EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
|
||||
#endif
|
||||
//SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||
/* Set some options and the session id.
|
||||
// SSL_CTX_set_ecdh_auto(ctx, 1);
|
||||
/* Set some options and the session id.
|
||||
* SSL_OP_NO_SSLv2: SSLv2 is insecure, disable it.
|
||||
* SSL_OP_NO_TICKET: We don't want TLS tickets used because this is an SSL server caching example.
|
||||
* It should be fine to use tickets in addition to server side caching.
|
||||
*/
|
||||
SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1|SSL_OP_NO_SSLv2|SSL_OP_NO_TICKET);
|
||||
SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_SSLv2 | SSL_OP_NO_TICKET);
|
||||
SSL_CTX_set_session_id_context(ctx, (void *)&ssl_session_ctx_id, sizeof(ssl_session_ctx_id));
|
||||
// set the cipher suit
|
||||
if (SSL_CTX_set_cipher_list(ctx, CIPHER_SUIT) != 1)
|
||||
const char *suit = g_server_config.ssl_cipher ? g_server_config.ssl_cipher : CIPHER_SUIT;
|
||||
LOG("Cirpher suit used: %s", suit);
|
||||
if (SSL_CTX_set_cipher_list(ctx, suit) != 1)
|
||||
{
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
ERROR("Fail to set ssl cirpher suit: %s", suit);
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/* Set the key and cert */
|
||||
/* use the full chain bundle of certificate */
|
||||
//if (SSL_CTX_use_certificate_file(ctx, server_config->sslcert, SSL_FILETYPE_PEM) <= 0) {
|
||||
if (SSL_CTX_use_certificate_chain_file(ctx, config()->sslcert) <= 0) {
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
/* use the full chain bundle of certificate */
|
||||
// if (SSL_CTX_use_certificate_file(ctx, server_config->sslcert, SSL_FILETYPE_PEM) <= 0) {
|
||||
if (SSL_CTX_use_certificate_chain_file(ctx, g_server_config.sslcert) <= 0)
|
||||
{
|
||||
ERROR("Fail to read SSL certificate chain file: %s", g_server_config.sslcert);
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_PrivateKey_file(ctx, config()->sslkey, SSL_FILETYPE_PEM) <= 0 ) {
|
||||
if (SSL_CTX_use_PrivateKey_file(ctx, g_server_config.sslkey, SSL_FILETYPE_PEM) <= 0)
|
||||
{
|
||||
ERROR("Fail to read SSL private file: %s", g_server_config.sslkey);
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (!SSL_CTX_check_private_key(ctx)) {
|
||||
LOG("Failed to validate cert \n");
|
||||
if (!SSL_CTX_check_private_key(ctx))
|
||||
{
|
||||
ERROR("Failed to validate SSL certificate");
|
||||
ERR_print_errors_fp(stderr);
|
||||
exit(EXIT_FAILURE);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
SSL_CTX_set_alpn_select_cb(ctx, alpn_select_cb, NULL);
|
||||
SSL_CTX_set_next_protos_advertised_cb(ctx, alpn_advertise_protos_cb, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void stop_serve(int dummy) {
|
||||
UNUSED(dummy);
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
//Blocks the SIG_IGN signal (by adding SIG_IGN to newMask)
|
||||
sigaddset(&mask, SIGINT);
|
||||
sigaddset(&mask, SIGPIPE);
|
||||
sigaddset(&mask, SIGABRT);
|
||||
sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||
antd_scheduler_destroy(&scheduler);
|
||||
unload_all_plugin();
|
||||
destroy_config();
|
||||
#ifdef USE_OPENSSL
|
||||
FIPS_mode_set(0);
|
||||
SSL_CTX_free(ctx);
|
||||
FIPS_mode_set(0);
|
||||
// DEPRECATED: CONF_modules_unload(1);
|
||||
EVP_cleanup();
|
||||
EVP_PBE_cleanup();
|
||||
// DEPRECATED:ENGINE_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
// DEPRECATED: ERR_remove_state(0);
|
||||
ERR_free_strings();
|
||||
#endif
|
||||
if(server_sock != -1)
|
||||
close(server_sock);
|
||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
static void stop_serve(int dummy)
|
||||
{
|
||||
// load the config first
|
||||
if(argc==1)
|
||||
load_config(CONFIG);
|
||||
else
|
||||
load_config(argv[1]);
|
||||
unsigned port = config()->port;
|
||||
int client_sock = -1;
|
||||
struct sockaddr_in client_name;
|
||||
socklen_t client_name_len = sizeof(client_name);
|
||||
char* client_ip = NULL;
|
||||
// ignore the broken PIPE error when writing
|
||||
//or reading to/from a closed socked connection
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGABRT, SIG_IGN);
|
||||
signal(SIGINT, stop_serve);
|
||||
|
||||
// close log server
|
||||
closelog();
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
// Blocks the SIG_IGN signal (by adding SIG_IGN to newMask)
|
||||
sigaddset(&mask, SIGINT);
|
||||
sigaddset(&mask, SIGPIPE);
|
||||
sigaddset(&mask, SIGABRT);
|
||||
sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||
antd_scheduler_destroy(scheduler);
|
||||
antd_unload_all_plugin();
|
||||
#ifdef USE_OPENSSL
|
||||
if( config()->usessl == 1 )
|
||||
{
|
||||
init_openssl();
|
||||
ctx = create_context();
|
||||
|
||||
configure_context(ctx);
|
||||
}
|
||||
|
||||
// DEPRECATED FIPS_mode_set(0);
|
||||
SSL_CTX_free(ctx);
|
||||
// DEPRECATED: CONF_modules_unload(1);
|
||||
EVP_cleanup();
|
||||
EVP_PBE_cleanup();
|
||||
// DEPRECATED:ENGINE_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
// DEPRECATED: ERR_remove_state(0);
|
||||
ERR_free_strings();
|
||||
#endif
|
||||
server_sock = startup(&port);
|
||||
LOG("httpd running on port %d\n", port);
|
||||
// default to 4 workers
|
||||
antd_scheduler_init(&scheduler, config()->n_workers);
|
||||
scheduler.validate_data = 1;
|
||||
scheduler.destroy_data = finish_request;
|
||||
// use blocking server_sock
|
||||
// make the scheduler wait for event on another thread
|
||||
// this allow to ged rid of high cpu usage on
|
||||
// endless loop without doing anything
|
||||
// set_nonblock(server_sock);
|
||||
pthread_t scheduler_th;
|
||||
if (pthread_create(&scheduler_th, NULL,(void *(*)(void *))antd_wait, (void*)&scheduler) != 0)
|
||||
{
|
||||
perror("pthread_create: cannot create worker\n");
|
||||
stop_serve(0);
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// reclaim data when exit
|
||||
pthread_detach(scheduler_th);
|
||||
}
|
||||
antd_task_t* task = NULL;
|
||||
while (scheduler.status)
|
||||
{
|
||||
client_sock = accept(server_sock,(struct sockaddr *)&client_name,&client_name_len);
|
||||
if (client_sock == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// just dump the scheduler when we have a connection
|
||||
antd_client_t* client = (antd_client_t*)malloc(sizeof(antd_client_t));
|
||||
antd_request_t* request = (antd_request_t*)malloc(sizeof(*request));
|
||||
request->client = client;
|
||||
request->request = dict();
|
||||
/*
|
||||
get the remote IP
|
||||
*/
|
||||
client->ip = NULL;
|
||||
if (client_name.sin_family == AF_INET)
|
||||
{
|
||||
client_ip = inet_ntoa(client_name.sin_addr);
|
||||
client->ip = strdup(client_ip);
|
||||
LOG("Client IP: %s\n", client_ip);
|
||||
//LOG("socket: %d\n", client_sock);
|
||||
}
|
||||
|
||||
// set timeout to socket
|
||||
set_nonblock(client_sock);
|
||||
/*struct timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 5000;
|
||||
|
||||
if (setsockopt (client_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout)) < 0)
|
||||
perror("setsockopt failed\n");
|
||||
|
||||
if (setsockopt (client_sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,sizeof(timeout)) < 0)
|
||||
perror("setsockopt failed\n");
|
||||
*/
|
||||
client->sock = client_sock;
|
||||
time(&client->last_io);
|
||||
#ifdef USE_OPENSSL
|
||||
client->ssl = NULL;
|
||||
client->status = 0;
|
||||
if(config()->usessl == 1)
|
||||
{
|
||||
client->ssl = (void*)SSL_new(ctx);
|
||||
if(!client->ssl) continue;
|
||||
SSL_set_fd((SSL*)client->ssl, client->sock);
|
||||
|
||||
/*if (SSL_accept((SSL*)client->ssl) <= 0) {
|
||||
LOG("EROOR accept\n");
|
||||
ERR_print_errors_fp(stderr);
|
||||
antd_close(client);
|
||||
continue;
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
config()->connection++;
|
||||
// create callback for the server
|
||||
task = antd_create_task(accept_request,(void*)request, finish_request, client->last_io);
|
||||
//task->type = LIGHT;
|
||||
antd_add_task(&scheduler, task);
|
||||
}
|
||||
|
||||
close(server_sock);
|
||||
|
||||
return(0);
|
||||
destroy_config();
|
||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
||||
exit(dummy);
|
||||
}
|
||||
|
||||
static void antd_monitor(port_config_t *pcnf, int sock)
|
||||
{
|
||||
antd_task_t *task = NULL;
|
||||
int client_sock = -1;
|
||||
antd_sockaddr_t client_name;
|
||||
socklen_t client_name_len = sizeof(client_name);
|
||||
char client_ip[INET6_ADDRSTRLEN] = {0};
|
||||
if (sock > 0)
|
||||
{
|
||||
client_sock = accept(sock, (struct sockaddr *)&client_name, &client_name_len);
|
||||
if (client_sock > 0)
|
||||
{
|
||||
// just dump the scheduler when we have a connection
|
||||
antd_client_t *client = (antd_client_t *)malloc(sizeof(antd_client_t));
|
||||
antd_request_t *request = (antd_request_t *)malloc(sizeof(*request));
|
||||
request->context = NULL;
|
||||
request->client = client;
|
||||
request->request = dict();
|
||||
client->zstream = NULL;
|
||||
client->z_level = ANTD_CNONE;
|
||||
|
||||
dictionary_t xheader = dict();
|
||||
dput(request->request, "REQUEST_HEADER", xheader);
|
||||
dput(request->request, "REQUEST_DATA", dict());
|
||||
dput(request->request, "SERVER_PORT", (void *)__s("%d", pcnf->port));
|
||||
dput(request->request, "SERVER_WWW_ROOT", (void *)strdup(pcnf->htdocs));
|
||||
/*
|
||||
get the remote IP
|
||||
*/
|
||||
if ( pcnf->type == ANTD_PROTO_IP_4 && client_name.addr4.sin_family == AF_INET)
|
||||
{
|
||||
inet_ntop(AF_INET,&client_name.addr4,client_ip,INET6_ADDRSTRLEN);
|
||||
}
|
||||
else if(client_name.addr6.sin6_family == AF_INET6)
|
||||
{
|
||||
inet_ntop(AF_INET6,&client_name.addr6,client_ip,INET6_ADDRSTRLEN);
|
||||
}
|
||||
if(client_ip[0] != '\0')
|
||||
{
|
||||
LOG("Connect to client IP: %s on port:%d (%d)", client_ip, pcnf->port, client_sock);
|
||||
// ip address
|
||||
dput(request->request, "REMOTE_ADDR", (void *)strdup(client_ip));
|
||||
// LOG("socket: %d\n", client_sock);
|
||||
}
|
||||
|
||||
// set timeout to socket
|
||||
set_nonblock(client_sock);
|
||||
|
||||
client->sock = client_sock;
|
||||
time(&client->last_io);
|
||||
client->ssl = NULL;
|
||||
client->state = ANTD_CLIENT_ACCEPT;
|
||||
client->z_status = 0;
|
||||
#ifdef USE_OPENSSL
|
||||
if (pcnf->usessl == 1)
|
||||
{
|
||||
client->ssl = (void *)SSL_new(ctx);
|
||||
if (!client->ssl)
|
||||
{
|
||||
finish_request(request);
|
||||
return;
|
||||
}
|
||||
SSL_set_fd((SSL *)client->ssl, client->sock);
|
||||
// this can be used in the protocol select callback to
|
||||
// set the protocol selected by the server
|
||||
if (!SSL_set_ex_data((SSL *)client->ssl, client->sock, client))
|
||||
{
|
||||
ERROR("Cannot set ex data to ssl client:%d", client->sock);
|
||||
}
|
||||
/*if (SSL_accept((SSL*)client->ssl) <= 0) {
|
||||
LOG("EROOR accept\n");
|
||||
ERR_print_errors_fp(stderr);
|
||||
antd_close(client);
|
||||
continue;
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
antd_scheduler_lock(scheduler);
|
||||
g_server_config.connection++;
|
||||
antd_scheduler_unlock(scheduler);
|
||||
// create callback for the server
|
||||
task = antd_create_task(accept_request, (void *)request, finish_request, client->last_io);
|
||||
antd_task_bind_event(task, client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE);
|
||||
antd_scheduler_add_task(scheduler, task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void antd_scheduler_ext_statistic(int fd, void *user_data)
|
||||
{
|
||||
antd_request_t *request = (antd_request_t *)user_data;
|
||||
chain_t it, it1;
|
||||
dictionary_t tmp;
|
||||
int ret;
|
||||
char buff[BUFFLEN];
|
||||
if (request == NULL)
|
||||
{
|
||||
SEND_STAT(fd, buff, ret, "Data is null\n");
|
||||
return;
|
||||
}
|
||||
// send client general infomation
|
||||
SEND_STAT(fd, buff, ret, "Client id: %d\n", request->client->sock);
|
||||
SEND_STAT(fd, buff, ret, "Last IO: %lu\n", (unsigned long)request->client->last_io);
|
||||
SEND_STAT(fd, buff, ret, "Current state: %d\n", request->client->state);
|
||||
SEND_STAT(fd, buff, ret, "z_level: %d\n", request->client->z_level);
|
||||
if (request->client->ssl)
|
||||
{
|
||||
SEND_STAT(fd, buff, ret, "SSL is enabled\n");
|
||||
}
|
||||
// send client request detail
|
||||
if (request->request)
|
||||
{
|
||||
for_each_assoc(it, request->request)
|
||||
{
|
||||
if (strcmp(it->key, "REQUEST_HEADER") == 0 ||
|
||||
strcmp(it->key, "REQUEST_DATA") == 0 ||
|
||||
strcmp(it->key, "COOKIE") == 0)
|
||||
{
|
||||
tmp = (dictionary_t)it->value;
|
||||
if (tmp)
|
||||
{
|
||||
for_each_assoc(it1, tmp)
|
||||
{
|
||||
SEND_STAT(fd, buff, ret, "%s: %s\n", it1->key, (char *)it1->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SEND_STAT(fd, buff, ret, "%s: %s\n", it->key, (char *)it->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
UNUSED(ret);
|
||||
}
|
||||
|
||||
void antd_scheduler_destroy_data(void *data)
|
||||
{
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
antd_client_t *proxy = (antd_client_t *)dvalue(rq->request, "PROXY_HANDLE");
|
||||
if (proxy)
|
||||
{
|
||||
close(proxy->sock);
|
||||
}
|
||||
finish_request(data);
|
||||
}
|
||||
|
||||
int antd_scheduler_validate_data(antd_task_t *task)
|
||||
{
|
||||
return !(difftime(time(NULL), task->access_time) > g_server_config.scheduler_timeout);
|
||||
}
|
||||
|
||||
int antd_task_data_id(void *data)
|
||||
{
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
if (!rq)
|
||||
return 0;
|
||||
return antd_scheduler_next_id(scheduler, rq->client->sock);
|
||||
/*UNUSED(data);
|
||||
return antd_scheduler_next_id(scheduler,0);*/
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
pthread_t sched_th;
|
||||
// startup port
|
||||
chain_t it;
|
||||
struct timeval timeout;
|
||||
port_config_t *pcnf;
|
||||
fd_set master_set, working_set;
|
||||
int status, maxfd = 0;
|
||||
int nlisten = 0;
|
||||
// load the config first
|
||||
#ifdef VERSION
|
||||
LOG("Antd server version: " VERSION);
|
||||
#endif
|
||||
if (argc == 1)
|
||||
load_config(CONFIG_FILE);
|
||||
else
|
||||
load_config(argv[1]);
|
||||
// ignore the broken PIPE error when writing
|
||||
// or reading to/from a closed socked connection
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGABRT, SIG_IGN);
|
||||
signal(SIGINT, stop_serve);
|
||||
signal(SIGTERM,stop_serve);
|
||||
// start syslog
|
||||
if (g_server_config.debug_enable == 1)
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_NOTICE));
|
||||
}
|
||||
else
|
||||
{
|
||||
setlogmask(LOG_UPTO(LOG_ERR));
|
||||
}
|
||||
openlog(SERVER_NAME, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_DAEMON);
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
if (g_server_config.enable_ssl == 1)
|
||||
{
|
||||
init_openssl();
|
||||
ctx = create_context();
|
||||
|
||||
configure_context(ctx);
|
||||
}
|
||||
|
||||
#endif
|
||||
// enable scheduler
|
||||
// default to 4 workers
|
||||
scheduler = antd_scheduler_init(g_server_config.n_workers, g_server_config.stat_fifo_path);
|
||||
if (scheduler == NULL)
|
||||
{
|
||||
ERROR("Unable to initialise scheduler. Exit");
|
||||
stop_serve(1);
|
||||
}
|
||||
FD_ZERO(&master_set);
|
||||
for_each_assoc(it, g_server_config.ports)
|
||||
{
|
||||
pcnf = (port_config_t *)it->value;
|
||||
if (pcnf)
|
||||
{
|
||||
if(pcnf->type == ANTD_PROTO_IP_4)
|
||||
{
|
||||
pcnf->sock = antd_listen(&pcnf->port,0,g_server_config.backlog);
|
||||
}
|
||||
else
|
||||
{
|
||||
pcnf->sock = antd_listen(&pcnf->port,1,g_server_config.backlog);
|
||||
}
|
||||
if (pcnf->sock > 0)
|
||||
{
|
||||
set_nonblock(pcnf->sock);
|
||||
FD_SET(pcnf->sock, &master_set);
|
||||
maxfd = pcnf->sock > maxfd ? pcnf->sock : maxfd;
|
||||
nlisten++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("Port %d is disabled", pcnf->port);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nlisten == 0)
|
||||
{
|
||||
ERROR("No port is listened, quit!!");
|
||||
stop_serve(1);
|
||||
}
|
||||
// Start scheduler
|
||||
if (pthread_create(&sched_th, NULL, (void *(*)(void *))antd_scheduler_wait, (void *)scheduler) != 0)
|
||||
{
|
||||
ERROR("pthread_create: cannot start scheduler thread");
|
||||
stop_serve(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// reclaim data when exit
|
||||
pthread_detach(sched_th);
|
||||
}
|
||||
|
||||
while (antd_scheduler_ok(scheduler))
|
||||
{
|
||||
if (g_server_config.connection > g_server_config.maxcon)
|
||||
{
|
||||
// ERROR("Reach max connection %d", g_server_config.connection);
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 10000; // 5 ms
|
||||
select(0, NULL, NULL, NULL, &timeout);
|
||||
continue;
|
||||
}
|
||||
FD_ZERO(&working_set);
|
||||
memcpy(&working_set, &master_set, sizeof(master_set));
|
||||
// blocking select
|
||||
status = select(maxfd + 1, &working_set, NULL, NULL, NULL);
|
||||
if (status < 0)
|
||||
{
|
||||
ERROR("select() error: %s", strerror(errno));
|
||||
break;
|
||||
}
|
||||
if (status == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for_each_assoc(it, g_server_config.ports)
|
||||
{
|
||||
pcnf = (port_config_t *)it->value;
|
||||
if (pcnf && pcnf->sock > 0 && FD_ISSET(pcnf->sock, &working_set))
|
||||
{
|
||||
antd_monitor(pcnf, pcnf->sock);
|
||||
}
|
||||
}
|
||||
}
|
||||
stop_serve(0);
|
||||
return 0;
|
||||
}
|
||||
|
106
lib/bst.c
Normal file
106
lib/bst.c
Normal file
@ -0,0 +1,106 @@
|
||||
#include<stdio.h>
|
||||
#include<stdlib.h>
|
||||
|
||||
#include "bst.h"
|
||||
|
||||
void bst_free_cb(bst_node_t* root, void (*cb)(void*))
|
||||
{
|
||||
if(root != NULL)
|
||||
{
|
||||
bst_free(root->left);
|
||||
bst_free(root->right);
|
||||
if(root->data && cb)
|
||||
{
|
||||
cb(root->data);
|
||||
}
|
||||
free(root);
|
||||
}
|
||||
}
|
||||
|
||||
bst_node_t* bst_insert(bst_node_t* root, int key, void* data)
|
||||
{
|
||||
if(root == NULL)
|
||||
{
|
||||
root = malloc(sizeof(bst_node_t));
|
||||
root->key = key;
|
||||
root->data = data;
|
||||
root->left = root->right = NULL;
|
||||
}
|
||||
else if(key < root->key)
|
||||
root->left = bst_insert(root->left, key, data);
|
||||
else if(key > root->key)
|
||||
root->right = bst_insert(root->right, key, data);
|
||||
else
|
||||
root->data = data;
|
||||
return root;
|
||||
}
|
||||
|
||||
bst_node_t* bst_find_min(bst_node_t* root)
|
||||
{
|
||||
if(root == NULL)
|
||||
return NULL;
|
||||
else if(root->left == NULL)
|
||||
return root;
|
||||
else
|
||||
return bst_find_min(root->left);
|
||||
}
|
||||
|
||||
bst_node_t* bst_find_max(bst_node_t* root)
|
||||
{
|
||||
if(root == NULL)
|
||||
return NULL;
|
||||
else if(root->right == NULL)
|
||||
return root;
|
||||
else
|
||||
return bst_find_max(root->right);
|
||||
}
|
||||
|
||||
bst_node_t* bst_find(bst_node_t* root, int x)
|
||||
{
|
||||
if(root == NULL)
|
||||
return NULL;
|
||||
else if(x < root->key)
|
||||
return bst_find(root->left, x);
|
||||
else if(x > root->key)
|
||||
return bst_find(root->right, x);
|
||||
else
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
bst_node_t* bst_delete(bst_node_t* root, int x)
|
||||
{
|
||||
bst_node_t* temp;
|
||||
if(root == NULL)
|
||||
return NULL;
|
||||
else if(x < root->key)
|
||||
root->left = bst_delete(root->left, x);
|
||||
else if(x > root->key)
|
||||
root->right = bst_delete(root->right, x);
|
||||
else if(root->left && root->right)
|
||||
{
|
||||
temp = bst_find_min(root->right);
|
||||
root->key = temp->key;
|
||||
root->data = temp->data;
|
||||
root->right = bst_delete(root->right, root->key);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = root;
|
||||
if(root->left == NULL)
|
||||
root = root->right;
|
||||
else if(root->right == NULL)
|
||||
root = root->left;
|
||||
free(temp);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
void bst_for_each(bst_node_t* root, void (*callback)(bst_node_t*, void **, int), void** args, int argc)
|
||||
{
|
||||
if(root == NULL)
|
||||
return;
|
||||
bst_for_each(root->left, callback, args, argc);
|
||||
callback(root, args, argc);
|
||||
bst_for_each(root->right, callback, args, argc);
|
||||
}
|
19
lib/bst.h
Normal file
19
lib/bst.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef BST_H
|
||||
#define BST_H 1
|
||||
#define bst_free(n) (bst_free_cb(n, NULL))
|
||||
typedef struct _tree_node
|
||||
{
|
||||
int key;
|
||||
void* data;
|
||||
struct _tree_node* left;
|
||||
struct _tree_node* right;
|
||||
} bst_node_t;
|
||||
|
||||
void bst_free_cb(bst_node_t* root, void (*callback)(void*));
|
||||
bst_node_t* bst_insert(bst_node_t* root, int key, void* data);
|
||||
bst_node_t* bst_find_min(bst_node_t* root);
|
||||
bst_node_t* bst_find_max(bst_node_t* root);
|
||||
bst_node_t* bst_find(bst_node_t* root, int x);
|
||||
bst_node_t* bst_delete(bst_node_t* root, int x);
|
||||
void bst_for_each(bst_node_t* root, void (*callback)(bst_node_t*, void **, int), void** args, int argc);
|
||||
#endif
|
170
lib/dictionary.c
Normal file
170
lib/dictionary.c
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 LE Xuan Sang xsang.le@gmail.com
|
||||
|
||||
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.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "utils.h"
|
||||
#include "dictionary.h"
|
||||
|
||||
dictionary_t dict()
|
||||
{
|
||||
return dict_n(DHASHSIZE);
|
||||
}
|
||||
dictionary_t dict_n(unsigned int size)
|
||||
{
|
||||
dictionary_t d = (dictionary_t)malloc(sizeof(struct __dict));
|
||||
if (!d)
|
||||
return NULL;
|
||||
d->map = (map_t)malloc(size * sizeof(chain_t));
|
||||
if (!d->map)
|
||||
{
|
||||
free(d);
|
||||
return NULL;
|
||||
}
|
||||
d->cap = size;
|
||||
d->size = 0;
|
||||
for (unsigned int i = 0; i < size; i++)
|
||||
d->map[i] = NULL;
|
||||
return d;
|
||||
}
|
||||
chain_t dlookup(dictionary_t dic, const char *key)
|
||||
{
|
||||
chain_t np;
|
||||
if (dic->map == NULL)
|
||||
return NULL;
|
||||
for (np = dic->map[hash(key, dic->cap)]; np != NULL; np = np->next)
|
||||
{
|
||||
if (!np || !np->key)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (strcmp(key, np->key) == 0)
|
||||
return np; /* found */
|
||||
}
|
||||
return NULL; /* not found */
|
||||
}
|
||||
chain_t __put_el_with_key(dictionary_t dic, const char *key)
|
||||
{
|
||||
chain_t np, it;
|
||||
unsigned hashval;
|
||||
if (dic->map == NULL)
|
||||
return NULL;
|
||||
if ((np = dlookup(dic, key)) == NULL)
|
||||
{ /* not found */
|
||||
np = (chain_t)malloc(sizeof(*np));
|
||||
if (np == NULL || (np->key = strdup(key)) == NULL)
|
||||
return NULL;
|
||||
np->value = NULL;
|
||||
hashval = hash(key, dic->cap);
|
||||
it = dic->map[hashval];
|
||||
while (it && it->next != NULL)
|
||||
{
|
||||
it = it->next;
|
||||
}
|
||||
if (it == NULL)
|
||||
{
|
||||
np->next = dic->map[hashval];
|
||||
dic->map[hashval] = np;
|
||||
}
|
||||
else
|
||||
{
|
||||
it->next = np;
|
||||
np->next = NULL;
|
||||
}
|
||||
|
||||
dic->size++;
|
||||
}
|
||||
// found
|
||||
return np;
|
||||
}
|
||||
chain_t dput(dictionary_t dic, const char *key, void *value)
|
||||
{
|
||||
chain_t np = __put_el_with_key(dic, key);
|
||||
if (np == NULL)
|
||||
{
|
||||
if (value)
|
||||
free(value);
|
||||
return NULL;
|
||||
}
|
||||
if (np->value && value)
|
||||
free(np->value);
|
||||
np->value = value;
|
||||
return np;
|
||||
}
|
||||
chain_t dremove(dictionary_t dic, const char *key)
|
||||
{
|
||||
if (dic->map == NULL)
|
||||
return 0;
|
||||
int hashval = hash(key, dic->cap);
|
||||
chain_t np = dic->map[hashval];
|
||||
if (np != NULL && strcmp(key, np->key) == 0)
|
||||
{
|
||||
dic->size--;
|
||||
dic->map[hashval] = np->next;
|
||||
np->next = NULL;
|
||||
return np;
|
||||
}
|
||||
for (np = dic->map[hashval]; np != NULL; np = np->next)
|
||||
if (np->next != NULL && strcmp(key, np->next->key) == 0)
|
||||
{
|
||||
chain_t ret = np->next;
|
||||
np->next = np->next->next; /* found */
|
||||
dic->size--;
|
||||
ret->next = NULL;
|
||||
return ret;
|
||||
}
|
||||
return NULL; /* not found */
|
||||
}
|
||||
void *dvalue(dictionary_t dic, const char *key)
|
||||
{
|
||||
chain_t as = dlookup(dic, key);
|
||||
if (as == NULL)
|
||||
return NULL;
|
||||
return as->value;
|
||||
}
|
||||
|
||||
void free_association(chain_t *asoc)
|
||||
{
|
||||
|
||||
while ((*asoc) != NULL)
|
||||
{
|
||||
chain_t a = *asoc;
|
||||
(*asoc) = (*asoc)->next;
|
||||
|
||||
if (a->key)
|
||||
{
|
||||
//printf("Free key %s\n", a->key);
|
||||
free(a->key);
|
||||
if (a->value)
|
||||
free(a->value);
|
||||
}
|
||||
free(a);
|
||||
}
|
||||
}
|
||||
|
||||
void freedict(dictionary_t dic)
|
||||
{
|
||||
for (unsigned int i = 0; i < dic->cap; i++)
|
||||
free_association(&(dic->map[i]));
|
||||
free(dic->map);
|
||||
free(dic);
|
||||
}
|
@ -24,27 +24,37 @@ THE SOFTWARE.
|
||||
#ifndef DICTIONARY_H
|
||||
#define DICTIONARY_H
|
||||
|
||||
#include "utils.h"
|
||||
#define for_each_assoc(assoc, dic) \
|
||||
for(int i = 0; i < HASHSIZE; i++) \
|
||||
for(assoc = dic[i];assoc!= NULL; assoc = assoc->next)
|
||||
#define DHASHSIZE 16
|
||||
#define for_each_assoc(assoc, dic) \
|
||||
for (unsigned int i = 0; i < dic->cap; i++) \
|
||||
for (assoc = dic->map[i]; assoc != NULL; assoc = assoc->next)
|
||||
|
||||
/**
|
||||
* Dictionary for header
|
||||
*/
|
||||
typedef struct __assoc{
|
||||
struct __assoc *next;
|
||||
char *key;
|
||||
void* value;
|
||||
typedef struct __assoc
|
||||
{
|
||||
struct __assoc *next;
|
||||
char *key;
|
||||
void *value;
|
||||
//char *value;
|
||||
} * association;
|
||||
} * chain_t;
|
||||
|
||||
typedef association* dictionary;
|
||||
dictionary dict();
|
||||
association dlookup(dictionary,const char*);
|
||||
void* dvalue(dictionary, const char*);
|
||||
association dput(dictionary,const char*, void*);
|
||||
int dremove(dictionary, const char*);
|
||||
void freedict(dictionary);
|
||||
typedef chain_t *map_t;
|
||||
|
||||
typedef struct __dict
|
||||
{
|
||||
unsigned int cap;
|
||||
map_t map;
|
||||
unsigned int size;
|
||||
} * dictionary_t;
|
||||
|
||||
dictionary_t dict();
|
||||
dictionary_t dict_n(unsigned int n);
|
||||
chain_t dlookup(dictionary_t, const char *);
|
||||
void *dvalue(dictionary_t, const char *);
|
||||
chain_t dput(dictionary_t, const char *, void *);
|
||||
chain_t dremove(dictionary_t, const char *);
|
||||
void freedict(dictionary_t);
|
||||
|
||||
#endif
|
1034
lib/handle.c
Normal file
1034
lib/handle.c
Normal file
File diff suppressed because it is too large
Load Diff
99
lib/handle.h
Normal file
99
lib/handle.h
Normal file
@ -0,0 +1,99 @@
|
||||
#ifndef HANDLE_H
|
||||
#define HANDLE_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "dictionary.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#define SERVER_NAME "Antd"
|
||||
#define IS_POST(method) (strcmp(method, "POST") == 0)
|
||||
#define IS_GET(method) (strcmp(method, "GET") == 0)
|
||||
#define R_STR(d, k) ((char *)dvalue(d, k))
|
||||
#define R_INT(d, k) (atoi(dvalue(d, k)))
|
||||
#define R_FLOAT(d, k) ((double)atof(dvalue(d, k)))
|
||||
#define R_PTR(d, k) (dvalue(d, k))
|
||||
#define __RESULT__ "{\"result\":%d,\"msg\":\"%s\"}"
|
||||
#define FORM_URL_ENCODE "application/x-www-form-urlencoded"
|
||||
#define FORM_MULTI_PART "multipart/form-data"
|
||||
#define MAX_IO_WAIT_TIME 5 // second
|
||||
|
||||
#define ANTD_CLIENT_ACCEPT 0x0
|
||||
#define ANTD_CLIENT_HANDSHAKE 0x1
|
||||
#define ANTD_CLIENT_HEADER_DECODE 0x2
|
||||
#define ANTD_CLIENT_PLUGIN_EXEC 0x3
|
||||
#define ANTD_CLIENT_PROTO_CHECK 0x4
|
||||
#define ANTD_CLIENT_RESOLVE_REQUEST 0x5
|
||||
#define ANTD_CLIENT_SERVE_FILE 0x6
|
||||
#define ANTD_CLIENT_RQ_DATA_DECODE 0x7
|
||||
#define ANTD_CLIENT_PROXY_MONITOR 0x8
|
||||
|
||||
#define MAX_PATH_LEN 256
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ANTD_CGZ,
|
||||
ANTD_CDEFL,
|
||||
ANTD_CNONE
|
||||
} antd_compress_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
ANTD_PROTO_IP_4,
|
||||
ANTD_PROTO_IP_6,
|
||||
ANTD_PROTO_ALL
|
||||
} antd_proto_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int sock;
|
||||
void *ssl;
|
||||
int state;
|
||||
time_t last_io;
|
||||
// compress
|
||||
antd_compress_t z_level;
|
||||
void *zstream;
|
||||
int z_status;
|
||||
} antd_client_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
antd_client_t *client;
|
||||
dictionary_t request;
|
||||
antd_plugin_ctx_t * context;
|
||||
} antd_request_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
dictionary_t header;
|
||||
list_t cookie;
|
||||
int status;
|
||||
|
||||
} antd_response_header_t;
|
||||
|
||||
void set_nonblock(int socket);
|
||||
//void set_block(int socket);
|
||||
#ifdef USE_ZLIB
|
||||
int compressable(char *ctype);
|
||||
void set_gzip_types(list_t list);
|
||||
#endif
|
||||
void antd_send_header(void *, antd_response_header_t *);
|
||||
const char *get_status_str(int stat);
|
||||
int __t(void *, const char *, ...);
|
||||
int __b(void *, const unsigned char *, int);
|
||||
int __f(void *, const char *);
|
||||
|
||||
int upload(const char *, const char *);
|
||||
|
||||
/*Default function for plugin*/
|
||||
void antd_error(void *client, int status, const char *msg);
|
||||
|
||||
int ws_enable(dictionary_t);
|
||||
int read_buf(void *sock, char *buf, int i);
|
||||
int antd_send(void *source, const void *data, int len);
|
||||
int antd_recv(void *source, void *data, int len);
|
||||
int antd_recv_upto(void* src, void* data, int len);
|
||||
int antd_close(void *source);
|
||||
void destroy_request(void *data);
|
||||
#endif
|
@ -67,7 +67,7 @@ int ini_parse_file(FILE* file,
|
||||
|
||||
/* Maximum line length for any line in INI file. */
|
||||
#ifndef INI_MAX_LINE
|
||||
#define INI_MAX_LINE 200
|
||||
#define INI_MAX_LINE 512
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
199
lib/list.c
Normal file
199
lib/list.c
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 LE Xuan Sang xsang.le@gmail.com
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "utils.h"
|
||||
|
||||
list_t list_init()
|
||||
{
|
||||
list_t ret = (list_t)malloc(sizeof *ret);
|
||||
ret->type = LIST_TYPE_NIL;
|
||||
ret->next = NULL;
|
||||
return ret;
|
||||
}
|
||||
void list_put(list_t* l, item_t it)
|
||||
{
|
||||
if(*l == NULL || (*l)->type == LIST_TYPE_NIL)
|
||||
{
|
||||
if(*l != NULL)
|
||||
free(*l);
|
||||
*l = it;
|
||||
return ;
|
||||
}
|
||||
item_t np = list_last(*l);
|
||||
np->next = it;
|
||||
}
|
||||
void list_put_special(list_t* l, const char* str)
|
||||
{
|
||||
item_t v;
|
||||
if(IS_INT(str))
|
||||
{
|
||||
v = list_item(LIST_TYPE_INT);
|
||||
v->value.i = atoi(str);
|
||||
}
|
||||
else if(IS_FLOAT(str))
|
||||
{
|
||||
v = list_item(LIST_TYPE_DOUBLE);
|
||||
v->value.d = (double)atof(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
v = list_item(LIST_TYPE_POINTER);
|
||||
v->value.ptr = strdup(str);
|
||||
}
|
||||
list_put(l,v);
|
||||
}
|
||||
item_t list_last(list_t l)
|
||||
{
|
||||
item_t p = l;
|
||||
while(p && p->next != NULL)
|
||||
p = p->next;
|
||||
return p;
|
||||
}
|
||||
int list_remove(list_t* l,int idx)
|
||||
{
|
||||
if(l==NULL) return 0;
|
||||
if(*l == NULL) return 0;
|
||||
item_t it;
|
||||
if(idx <0 || idx >= list_size(*l)) return 0;
|
||||
if(idx == 0)
|
||||
{
|
||||
it = *l;
|
||||
*l=(*l)->next;
|
||||
it->next = NULL;
|
||||
list_free(&it);
|
||||
return 1;
|
||||
}
|
||||
item_t np = list_at(*l,idx-1);
|
||||
if(np == NULL) return 0;
|
||||
if(np->next != NULL)
|
||||
{
|
||||
item_t it = np->next;
|
||||
it->next = NULL;
|
||||
list_free(&it);
|
||||
np->next = np->next->next;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
int list_size(list_t l)
|
||||
{
|
||||
if(l == NULL || l->type == LIST_TYPE_NIL) return 0;
|
||||
int i=0;
|
||||
item_t np = l;
|
||||
while(np)
|
||||
{
|
||||
np = np->next;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
item_t list_at(list_t l,int idx)
|
||||
{
|
||||
if(l == NULL || idx<0 || idx>= list_size(l))
|
||||
return NULL;
|
||||
int i=0;
|
||||
item_t np = l;
|
||||
while(np)
|
||||
{
|
||||
if(i==idx)
|
||||
return np;
|
||||
np = np->next;
|
||||
i++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
item_t list_item(int type)
|
||||
{
|
||||
item_t ret = (item_t)malloc(sizeof *ret);
|
||||
ret->type = type;
|
||||
ret->next = NULL;
|
||||
return ret;
|
||||
}
|
||||
list_t split(const char* str, const char* delim)
|
||||
{
|
||||
if(str == NULL || delim == NULL) return NULL;
|
||||
char* str_cpy = (char*)str;
|
||||
char* token;
|
||||
list_t l = list_init();
|
||||
while((token = strsep(&str_cpy,delim)))
|
||||
{
|
||||
trim(token, ' ');
|
||||
if(strlen(token) > 0)
|
||||
{
|
||||
list_put_special(&l, token);
|
||||
}
|
||||
}
|
||||
if(l->type== LIST_TYPE_NIL)
|
||||
{
|
||||
free(l);
|
||||
return NULL;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
void list_put_i(list_t* l,int v)
|
||||
{
|
||||
item_t it = list_item(LIST_TYPE_INT);
|
||||
it->value.i = v;
|
||||
list_put(l,it);
|
||||
}
|
||||
void list_put_d(list_t* l,double v)
|
||||
{
|
||||
item_t it = list_item(LIST_TYPE_DOUBLE);
|
||||
it->value.d = v;
|
||||
list_put(l,it);
|
||||
}
|
||||
|
||||
void list_put_ptr(list_t* l,void* v)
|
||||
{
|
||||
item_t it = list_item(LIST_TYPE_POINTER);
|
||||
it->value.ptr = v;
|
||||
list_put(l,it);
|
||||
}
|
||||
void list_put_array(list_t* l,list_t v)
|
||||
{
|
||||
item_t it = list_item(LIST_TYPE_ARRAY);
|
||||
it->value.array = v;
|
||||
list_put(l,it);
|
||||
}
|
||||
// unliked dictionary
|
||||
// user have the responsibility to free
|
||||
// pointer data
|
||||
void list_free(list_t *l)
|
||||
{
|
||||
item_t curr;
|
||||
while ((curr = (*l)) != NULL) {
|
||||
(*l) = (*l)->next;
|
||||
if(curr->type == LIST_TYPE_ARRAY && curr->value.array)
|
||||
list_free(&curr->value.array);
|
||||
else if( curr->type == LIST_TYPE_POINTER && curr->value.ptr)
|
||||
free(curr->value.ptr);
|
||||
free (curr);
|
||||
}
|
||||
}
|
||||
int list_empty(list_t l)
|
||||
{
|
||||
return l== NULL || l->type == LIST_TYPE_NIL;
|
||||
}
|
@ -23,39 +23,42 @@ THE SOFTWARE.
|
||||
*/
|
||||
#ifndef LIST_H
|
||||
#define LIST_H
|
||||
#include "utils.h"
|
||||
#define list item
|
||||
|
||||
|
||||
#define LIST_TYPE_ARRAY 0x5
|
||||
#define LIST_TYPE_POINTER 0x4
|
||||
#define LIST_TYPE_DOUBLE 0x2
|
||||
#define LIST_TYPE_INT 0x1
|
||||
#define LIST_TYPE_NIL 0x0
|
||||
|
||||
#define list_for_each(item, list) \
|
||||
for(item = list;item!= NULL && item->type != LIST_TYPE_NIL; item = item->next)
|
||||
|
||||
typedef struct __item{
|
||||
int type;
|
||||
union{
|
||||
int i;
|
||||
int b;
|
||||
char* s;
|
||||
double d;
|
||||
char* date;
|
||||
char* b64;
|
||||
void* ptr;
|
||||
struct __item* array;
|
||||
} value;
|
||||
struct __item* next;
|
||||
}*item;
|
||||
}*item_t;
|
||||
typedef item_t list_t;
|
||||
list_t list_init();
|
||||
void list_put(list_t*,item_t);
|
||||
void list_put_i(list_t*,int);
|
||||
void list_put_d(list_t*,double);
|
||||
void list_put_ptr(list_t*,void*);
|
||||
void list_put_array(list_t*,list_t);
|
||||
|
||||
list list_init();
|
||||
void list_put(list*,item);
|
||||
void list_put_i(list*,int);
|
||||
void list_put_d(list*,double);
|
||||
void list_put_b(list*,int);
|
||||
void list_put_b64(list*,const char*);
|
||||
void list_put_date(list*,const char*);
|
||||
void list_put_s(list*,const char*);
|
||||
void list_put_array(list*,list);
|
||||
item list_last(list);
|
||||
int list_remove(list,int);
|
||||
int list_size(list);
|
||||
item list_at(list,int);
|
||||
int list_empty(list);
|
||||
item list_item(int type);
|
||||
list split(const char*, const char*);
|
||||
char* as_string(list);
|
||||
void list_put_special(list*, const char*);
|
||||
void list_free(list *);
|
||||
item_t list_last(list_t);
|
||||
int list_remove(list_t*,int);
|
||||
int list_size(list_t);
|
||||
item_t list_at(list_t,int);
|
||||
int list_empty(list_t);
|
||||
item_t list_item(int type);
|
||||
list_t split(const char*, const char*);
|
||||
void list_put_special(list_t*, const char*);
|
||||
void list_free(list_t *);
|
||||
#endif
|
51
lib/plugin.c
Normal file
51
lib/plugin.c
Normal file
@ -0,0 +1,51 @@
|
||||
#include "plugin_ctx.h"
|
||||
#include "plugin.h"
|
||||
|
||||
|
||||
const char * antd_plugin_basedir(antd_plugin_ctx_t * ctx)
|
||||
{
|
||||
return ctx->basedir;
|
||||
}
|
||||
const char * antd_plugin_tmpdir(antd_plugin_ctx_t * ctx)
|
||||
{
|
||||
return ctx->tmpdir;
|
||||
}
|
||||
const char * antd_plugin_confdir(antd_plugin_ctx_t *ctx)
|
||||
{
|
||||
if(ctx->confdir == NULL)
|
||||
{
|
||||
struct stat st;
|
||||
ctx->confdir = __s("%s%s%s", ctx->basedir,DIR_SEP, ctx->name);
|
||||
if (stat(ctx->confdir, &st) == -1)
|
||||
mkdir(ctx->confdir, 0755);
|
||||
}
|
||||
return ctx->confdir;
|
||||
}
|
||||
const char * antd_plugin_name(antd_plugin_ctx_t *ctx)
|
||||
{
|
||||
return ctx->name;
|
||||
}
|
||||
void antd_plugin_set_status(antd_plugin_ctx_t * ctx, int stat)
|
||||
{
|
||||
ctx->status = stat;
|
||||
}
|
||||
int antd_plugin_status(antd_plugin_ctx_t * ctx)
|
||||
{
|
||||
return ctx->status;
|
||||
}
|
||||
void antd_plugin_use_raw_body(antd_plugin_ctx_t * ctx)
|
||||
{
|
||||
ctx->raw_body = 1;
|
||||
}
|
||||
int antd_plugin_is_raw_body(antd_plugin_ctx_t *ctx)
|
||||
{
|
||||
return ctx->raw_body == 1;
|
||||
}
|
||||
void* antd_plugin_data(antd_plugin_ctx_t* ctx)
|
||||
{
|
||||
return ctx->data;
|
||||
}
|
||||
dictionary_t antd_plugin_config(antd_plugin_ctx_t* ctx)
|
||||
{
|
||||
return ctx->config;
|
||||
}
|
41
lib/plugin.h
Normal file
41
lib/plugin.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef PLUGIN_H
|
||||
#define PLUGIN_H
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define ANTD_PLUGIN_READY 0x0
|
||||
#define ANTD_PLUGIN_PANNIC 0x1
|
||||
#define ANTD_PLUGIN_INIT 0x2
|
||||
|
||||
#define PLUGIN_INIT "create"
|
||||
#define PLUGIN_HANDLE "handle"
|
||||
#define PLUGIN_DROP "drop"
|
||||
|
||||
#define DEF_PLUGIN_INTERFACE(name, param, ret) ret##name(param)
|
||||
|
||||
#include "utils.h"
|
||||
#include "dictionary.h"
|
||||
|
||||
#define PLUGIN_PANIC(ctx, a, ...) \
|
||||
ERROR("%s: " a, antd_plugin_name(ctx), ##__VA_ARGS__); \
|
||||
antd_plugin_set_status(ctx, ANTD_PLUGIN_PANNIC);
|
||||
|
||||
typedef struct _plugin_ctx_t antd_plugin_ctx_t;
|
||||
|
||||
const char *antd_plugin_basedir(antd_plugin_ctx_t *);
|
||||
const char *antd_plugin_tmpdir(antd_plugin_ctx_t *);
|
||||
const char *antd_plugin_confdir(antd_plugin_ctx_t *);
|
||||
const char *antd_plugin_name(antd_plugin_ctx_t *);
|
||||
void antd_plugin_set_status(antd_plugin_ctx_t *, int);
|
||||
int antd_plugin_status(antd_plugin_ctx_t *);
|
||||
void antd_plugin_use_raw_body(antd_plugin_ctx_t *);
|
||||
int antd_plugin_is_raw_body(antd_plugin_ctx_t *);
|
||||
void *antd_plugin_data(antd_plugin_ctx_t *);
|
||||
dictionary_t antd_plugin_config(antd_plugin_ctx_t*);
|
||||
|
||||
/*Default interfaces shall be implemented by plugin*/
|
||||
void *create(antd_plugin_ctx_t *);
|
||||
void drop(antd_plugin_ctx_t *);
|
||||
void *handle(void *);
|
||||
|
||||
#endif
|
18
lib/plugin_ctx.h
Normal file
18
lib/plugin_ctx.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef PLUGIN_CTX_H
|
||||
#define PLUGIN_CTX_H
|
||||
#include "handle.h"
|
||||
struct _plugin_ctx_t
|
||||
{
|
||||
char * name;
|
||||
const char * tmpdir;
|
||||
const char * basedir;
|
||||
char * confdir;
|
||||
int raw_body;
|
||||
int status;
|
||||
dictionary_t config;
|
||||
void * data;
|
||||
void *(*handle)(void *);
|
||||
void *(*create)(struct _plugin_ctx_t *);
|
||||
void (*drop)(struct _plugin_ctx_t *);
|
||||
} ;
|
||||
#endif
|
902
lib/scheduler.c
Normal file
902
lib/scheduler.c
Normal file
@ -0,0 +1,902 @@
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include "scheduler.h"
|
||||
#include "utils.h"
|
||||
#include "bst.h"
|
||||
|
||||
#define MAX_VALIDITY_INTERVAL 30
|
||||
#define MAX_NAME_SZ 255
|
||||
|
||||
// callback definition
|
||||
struct _antd_callback_t
|
||||
{
|
||||
void *(*handle)(void *);
|
||||
struct _antd_callback_t *next;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int flags;
|
||||
int fd;
|
||||
struct timeval stamp;
|
||||
int timeout; // seconds
|
||||
antd_task_t *task;
|
||||
} antd_task_evt_item_t;
|
||||
|
||||
struct _antd_queue_item_t
|
||||
{
|
||||
union
|
||||
{
|
||||
antd_task_evt_item_t *evt;
|
||||
antd_task_t *task;
|
||||
void *raw_ptr;
|
||||
};
|
||||
struct _antd_queue_item_t *next;
|
||||
};
|
||||
|
||||
typedef struct _antd_queue_item_t *antd_queue_item_t;
|
||||
|
||||
typedef antd_queue_item_t antd_queue_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int id;
|
||||
pthread_t tid;
|
||||
antd_task_t* current_task;
|
||||
void *manager;
|
||||
} antd_worker_t;
|
||||
|
||||
struct _antd_scheduler_t
|
||||
{
|
||||
// data lock
|
||||
pthread_mutex_t scheduler_lock;
|
||||
pthread_mutex_t worker_lock;
|
||||
pthread_mutex_t pending_lock;
|
||||
// event handle
|
||||
sem_t *scheduler_sem;
|
||||
sem_t *worker_sem;
|
||||
// worker and data
|
||||
bst_node_t *task_queue;
|
||||
antd_queue_t workers_queue;
|
||||
uint8_t status; // 0 stop, 1 working
|
||||
antd_worker_t *workers;
|
||||
int n_workers;
|
||||
int pending_task;
|
||||
int id_allocator;
|
||||
char stat_fifo[MAX_NAME_SZ];
|
||||
char sched_name[MAX_NAME_SZ];
|
||||
char worker_hub[MAX_NAME_SZ];
|
||||
int stat_fd;
|
||||
pthread_t stat_tid;
|
||||
};
|
||||
|
||||
static antd_callback_t *callback_of(void *(*callback)(void *));
|
||||
static void antd_execute_task(antd_scheduler_t *, antd_task_t *);
|
||||
static void antd_task_schedule(antd_scheduler_t *scheduler, antd_task_t *task);
|
||||
static void destroy_task(void *data);
|
||||
|
||||
static void set_nonblock(int fd)
|
||||
{
|
||||
int flags;
|
||||
flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags == -1)
|
||||
{
|
||||
ERROR("Unable to set flag");
|
||||
}
|
||||
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
}
|
||||
|
||||
static void enqueue(antd_queue_t *q, void *data)
|
||||
{
|
||||
antd_queue_item_t it = *q;
|
||||
while (it && it->next != NULL)
|
||||
it = it->next;
|
||||
antd_queue_item_t new_it = (antd_queue_item_t)malloc(sizeof *new_it);
|
||||
new_it->raw_ptr = data;
|
||||
new_it->next = NULL;
|
||||
if (!it) // first task
|
||||
{
|
||||
*q = new_it;
|
||||
}
|
||||
else
|
||||
{
|
||||
it->next = new_it;
|
||||
}
|
||||
}
|
||||
|
||||
static void stop(antd_scheduler_t *scheduler)
|
||||
{
|
||||
scheduler->status = 0;
|
||||
// unlock all idle workers if any
|
||||
for (int i = 0; i < scheduler->n_workers; i++)
|
||||
sem_post(scheduler->worker_sem);
|
||||
if (scheduler->scheduler_sem)
|
||||
sem_post(scheduler->scheduler_sem);
|
||||
for (int i = 0; i < scheduler->n_workers; i++)
|
||||
if (scheduler->workers[i].id != -1)
|
||||
pthread_join(scheduler->workers[i].tid, NULL);
|
||||
if (scheduler->workers)
|
||||
free(scheduler->workers);
|
||||
if(scheduler->stat_tid)
|
||||
(void)pthread_cancel(scheduler->stat_tid);
|
||||
// destroy all the mutex
|
||||
pthread_mutex_destroy(&scheduler->scheduler_lock);
|
||||
pthread_mutex_destroy(&scheduler->worker_lock);
|
||||
pthread_mutex_destroy(&scheduler->pending_lock);
|
||||
sem_unlink(scheduler->sched_name);
|
||||
sem_unlink(scheduler->worker_hub);
|
||||
sem_close(scheduler->scheduler_sem);
|
||||
sem_close(scheduler->worker_sem);
|
||||
}
|
||||
|
||||
static antd_queue_item_t dequeue(antd_queue_t *q)
|
||||
{
|
||||
antd_queue_item_t it = *q;
|
||||
if (it)
|
||||
{
|
||||
*q = it->next;
|
||||
it->next = NULL;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
static antd_callback_t *callback_of(void *(*callback)(void *))
|
||||
{
|
||||
antd_callback_t *cb = NULL;
|
||||
if (callback)
|
||||
{
|
||||
cb = (antd_callback_t *)malloc(sizeof *cb);
|
||||
cb->handle = callback;
|
||||
cb->next = NULL;
|
||||
}
|
||||
return cb;
|
||||
}
|
||||
|
||||
static void free_callback(antd_callback_t *cb)
|
||||
{
|
||||
antd_callback_t *it = cb;
|
||||
antd_callback_t *curr;
|
||||
while (it)
|
||||
{
|
||||
curr = it;
|
||||
it = it->next;
|
||||
free(curr);
|
||||
}
|
||||
}
|
||||
|
||||
static void enqueue_callback(antd_callback_t *cb, antd_callback_t *el)
|
||||
{
|
||||
antd_callback_t *it = cb;
|
||||
while (it && it->next != NULL)
|
||||
it = it->next;
|
||||
if (!it)
|
||||
return; // this should not happend
|
||||
it->next = el;
|
||||
}
|
||||
|
||||
static void execute_callback(antd_scheduler_t *scheduler, antd_task_t *task)
|
||||
{
|
||||
antd_callback_t *cb = task->callback;
|
||||
if (cb)
|
||||
{
|
||||
// call the first come call back
|
||||
task->handle = cb->handle;
|
||||
task->callback = task->callback->next;
|
||||
free(cb);
|
||||
//antd_task_bind_event(task, 0, 0, TASK_EVT_ALWAY_ON);
|
||||
antd_scheduler_add_task(scheduler, task);
|
||||
}
|
||||
else
|
||||
{
|
||||
destroy_task(task);
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_queue(antd_queue_t q, int is_task)
|
||||
{
|
||||
antd_queue_item_t it, curr;
|
||||
it = q;
|
||||
while (it)
|
||||
{
|
||||
if (is_task)
|
||||
{
|
||||
// first free the task
|
||||
destroy_task(it->task);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (it->raw_ptr)
|
||||
{
|
||||
free(it->raw_ptr);
|
||||
it->raw_ptr = NULL;
|
||||
}
|
||||
}
|
||||
// then free the placeholder
|
||||
curr = it;
|
||||
it = it->next;
|
||||
free(curr);
|
||||
}
|
||||
}
|
||||
static void *work(antd_worker_t *worker)
|
||||
{
|
||||
antd_scheduler_t *scheduler = (antd_scheduler_t *)worker->manager;
|
||||
while (scheduler->status)
|
||||
{
|
||||
antd_queue_item_t it;
|
||||
pthread_mutex_lock(&scheduler->worker_lock);
|
||||
it = dequeue(&scheduler->workers_queue);
|
||||
pthread_mutex_unlock(&scheduler->worker_lock);
|
||||
// execute the task
|
||||
//LOG("task executed by worker %d\n", worker->pid);
|
||||
// no task to execute, just sleep wait
|
||||
if (!it)
|
||||
{
|
||||
//LOG("Worker %d goes to idle state\n", worker->id);
|
||||
sem_wait(scheduler->worker_sem);
|
||||
}
|
||||
else
|
||||
{
|
||||
worker->current_task = it->task;
|
||||
//LOG("task executed by worker %d\n", worker->id);
|
||||
antd_execute_task(scheduler, it->task);
|
||||
free(it);
|
||||
worker->current_task = NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static void antd_task_dump(int fd, antd_task_t* task, char* buffer)
|
||||
{
|
||||
if (task == NULL || fd < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int ret;
|
||||
// send statistic on task data
|
||||
snprintf(buffer, MAX_NAME_SZ, "---- Task %d created at: %lu ----\n", task->id, task->stamp);
|
||||
ret = write(fd, buffer, strlen(buffer));
|
||||
|
||||
// send statistic on task data
|
||||
snprintf(buffer, MAX_NAME_SZ, "Access time: %lu\nn", (unsigned long)task->access_time);
|
||||
ret = write(fd, buffer, strlen(buffer));
|
||||
|
||||
snprintf(buffer, MAX_NAME_SZ, "Current time: %lu\n", (unsigned long)time(NULL));
|
||||
ret = write(fd, buffer, strlen(buffer));
|
||||
|
||||
if (task->handle)
|
||||
{
|
||||
snprintf(buffer, MAX_NAME_SZ, "Has handle: yes\n");
|
||||
ret = write(fd, buffer, strlen(buffer));
|
||||
}
|
||||
|
||||
if (task->callback)
|
||||
{
|
||||
snprintf(buffer, MAX_NAME_SZ, "Has callback: yes\n");
|
||||
ret = write(fd, buffer, strlen(buffer));
|
||||
}
|
||||
UNUSED(ret);
|
||||
// now print all task data statistic
|
||||
antd_scheduler_ext_statistic(fd, task->data);
|
||||
}
|
||||
static void print_static_info(bst_node_t *node, void **args, int argc)
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
char *buffer = args[0];
|
||||
int *fdp = args[1];
|
||||
antd_task_t *task = (antd_task_t *)node->data;
|
||||
antd_task_dump(*fdp, task, buffer);
|
||||
}
|
||||
static void *statistic(antd_scheduler_t *scheduler)
|
||||
{
|
||||
struct pollfd pfd;
|
||||
int ret;
|
||||
char buffer[MAX_NAME_SZ];
|
||||
void *argc[2];
|
||||
while (scheduler->status)
|
||||
{
|
||||
if (scheduler->stat_fd == -1)
|
||||
{
|
||||
scheduler->stat_fd = open(scheduler->stat_fifo, O_WRONLY);
|
||||
if (scheduler->stat_fd == -1)
|
||||
{
|
||||
ERROR("Unable to open FIFO %s: %s", scheduler->stat_fifo, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_nonblock(scheduler->stat_fd);
|
||||
}
|
||||
}
|
||||
argc[0] = buffer;
|
||||
argc[1] = &scheduler->stat_fd;
|
||||
pfd.fd = scheduler->stat_fd;
|
||||
pfd.events = POLLOUT;
|
||||
ret = poll(&pfd, 1, -1);
|
||||
switch (ret)
|
||||
{
|
||||
case -1:
|
||||
ERROR("Error on select(): %s\n", strerror(errno));
|
||||
close(scheduler->stat_fd);
|
||||
return NULL;
|
||||
|
||||
case 0:
|
||||
break;
|
||||
// we have data
|
||||
default:
|
||||
if (pfd.revents & POLLOUT)
|
||||
{
|
||||
if (scheduler->pending_task > 0)
|
||||
{
|
||||
pthread_mutex_lock(&scheduler->scheduler_lock);
|
||||
// write statistic data
|
||||
snprintf(buffer, MAX_NAME_SZ, "Pending task: %d. Detail:\n", scheduler->pending_task);
|
||||
ret = write(scheduler->stat_fd, buffer, strlen(buffer));
|
||||
|
||||
bst_for_each(scheduler->task_queue, print_static_info, argc, 2);
|
||||
|
||||
pthread_mutex_unlock(&scheduler->scheduler_lock);
|
||||
|
||||
// write worker current task
|
||||
for (int i = 0; i < scheduler->n_workers; i++)
|
||||
{
|
||||
snprintf(buffer, MAX_NAME_SZ, "Worker: %d. Detail:\n", i);
|
||||
ret = write(scheduler->stat_fd, buffer, strlen(buffer));
|
||||
if(scheduler->workers[i].current_task)
|
||||
{
|
||||
antd_task_dump(scheduler->stat_fd, scheduler->workers[i].current_task, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
ret = close(scheduler->stat_fd);
|
||||
scheduler->stat_fd = -1;
|
||||
usleep(5000);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = write(scheduler->stat_fd, ".", 1);
|
||||
if (ret == -1)
|
||||
{
|
||||
ret = close(scheduler->stat_fd);
|
||||
scheduler->stat_fd = -1;
|
||||
usleep(5000);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = write(scheduler->stat_fd, "\b", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = close(scheduler->stat_fd);
|
||||
scheduler->stat_fd = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* else
|
||||
{
|
||||
ret = write(scheduler->stat_fd, ".", 1);
|
||||
if(ret == -1)
|
||||
{
|
||||
ret = close(scheduler->stat_fd);
|
||||
scheduler->stat_fd = -1;
|
||||
}
|
||||
} */
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
Main API methods
|
||||
init the main scheduler
|
||||
*/
|
||||
|
||||
antd_scheduler_t *antd_scheduler_init(int n, const char *stat_name)
|
||||
{
|
||||
antd_scheduler_t *scheduler = (antd_scheduler_t *)malloc(sizeof(antd_scheduler_t));
|
||||
scheduler->n_workers = n;
|
||||
scheduler->status = 1;
|
||||
scheduler->workers_queue = NULL;
|
||||
scheduler->pending_task = 0;
|
||||
scheduler->stat_fd = -1;
|
||||
scheduler->id_allocator = 0;
|
||||
scheduler->stat_tid = 0;
|
||||
int pid = getpid();
|
||||
snprintf(scheduler->sched_name,MAX_NAME_SZ, "scheduler.%d",pid);
|
||||
snprintf(scheduler->worker_hub,MAX_NAME_SZ, "worker.%d",pid);
|
||||
(void)memset(scheduler->stat_fifo, 0, MAX_NAME_SZ);
|
||||
if (stat_name)
|
||||
{
|
||||
(void)strncpy(scheduler->stat_fifo, stat_name, MAX_NAME_SZ - 1);
|
||||
}
|
||||
// init semaphore
|
||||
|
||||
scheduler->scheduler_sem = sem_open(scheduler->sched_name, O_CREAT, 0644, 0);
|
||||
if (scheduler->scheduler_sem == SEM_FAILED)
|
||||
{
|
||||
ERROR("Cannot open semaphore for scheduler: %s", strerror(errno));
|
||||
free(scheduler);
|
||||
return NULL;
|
||||
}
|
||||
scheduler->worker_sem = sem_open(scheduler->worker_hub, O_CREAT, 0600, 0);
|
||||
if (!scheduler->worker_sem)
|
||||
{
|
||||
ERROR("Cannot open semaphore for workers");
|
||||
free(scheduler);
|
||||
return NULL;
|
||||
}
|
||||
// init lock
|
||||
pthread_mutex_init(&scheduler->scheduler_lock, NULL);
|
||||
pthread_mutex_init(&scheduler->worker_lock, NULL);
|
||||
pthread_mutex_init(&scheduler->pending_lock, NULL);
|
||||
scheduler->task_queue = NULL;
|
||||
// create scheduler.workers
|
||||
if (n > 0)
|
||||
{
|
||||
scheduler->workers = (antd_worker_t *)malloc(n * (sizeof(antd_worker_t)));
|
||||
if (!scheduler->workers)
|
||||
{
|
||||
ERROR("Cannot allocate memory for worker");
|
||||
free(scheduler);
|
||||
return NULL;
|
||||
}
|
||||
for (int i = 0; i < scheduler->n_workers; i++)
|
||||
{
|
||||
scheduler->workers[i].id = -1;
|
||||
scheduler->workers[i].manager = (void *)scheduler;
|
||||
scheduler->workers[i].current_task = NULL;
|
||||
if (pthread_create(&scheduler->workers[i].tid, NULL, (void *(*)(void *))work, (void *)&scheduler->workers[i]) != 0)
|
||||
{
|
||||
ERROR("pthread_create: cannot create worker: %s", strerror(errno));
|
||||
free(scheduler);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
scheduler->workers[i].id = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
// delete the fifo if any
|
||||
if (scheduler->stat_fifo[0] != '\0')
|
||||
{
|
||||
LOG("Statistic fifo at: %s", scheduler->stat_fifo);
|
||||
(void)remove(scheduler->stat_fifo);
|
||||
// create the fifo file
|
||||
if (mkfifo(scheduler->stat_fifo, 0666) == -1)
|
||||
{
|
||||
ERROR("Unable to create statistic FIFO %s: %s", scheduler->stat_fifo, strerror(errno));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pthread_create(&scheduler->stat_tid, NULL, (void *(*)(void *))statistic, scheduler) != 0)
|
||||
{
|
||||
ERROR("pthread_create: cannot create statistic thread: %s", strerror(errno));
|
||||
scheduler->stat_tid = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG("Antd scheduler initialized with %d worker", scheduler->n_workers);
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
static void destroy_task(void *data)
|
||||
{
|
||||
antd_task_t *task = (antd_task_t *)data;
|
||||
if (!task)
|
||||
return;
|
||||
if (task->callback)
|
||||
{
|
||||
free_callback(task->callback);
|
||||
task->callback = NULL;
|
||||
}
|
||||
if (task->events)
|
||||
{
|
||||
destroy_queue(task->events, 0);
|
||||
task->events = NULL;
|
||||
}
|
||||
if (task)
|
||||
free(task);
|
||||
}
|
||||
|
||||
/*
|
||||
destroy all pending task
|
||||
pthread_mutex_lock(&scheduler.queue_lock);
|
||||
*/
|
||||
void antd_scheduler_destroy(antd_scheduler_t *scheduler)
|
||||
{
|
||||
if (!scheduler)
|
||||
return;
|
||||
// free all the chains
|
||||
stop(scheduler);
|
||||
LOG("Destroy remaining queue");
|
||||
bst_free_cb(scheduler->task_queue, destroy_task);
|
||||
scheduler->task_queue = NULL;
|
||||
destroy_queue(scheduler->workers_queue, 1);
|
||||
free(scheduler);
|
||||
}
|
||||
|
||||
/*
|
||||
create a task
|
||||
*/
|
||||
antd_task_t *antd_create_task(void *(*handle)(void *), void *data, void *(*callback)(void *), time_t atime)
|
||||
{
|
||||
antd_task_t *task = (antd_task_t *)malloc(sizeof(antd_task_t));
|
||||
task->stamp = (unsigned long)time(NULL);
|
||||
task->data = data;
|
||||
task->handle = handle;
|
||||
task->id = antd_task_data_id(data);
|
||||
task->callback = callback_of(callback);
|
||||
task->access_time = atime;
|
||||
task->events = NULL;
|
||||
return task;
|
||||
}
|
||||
|
||||
/*
|
||||
scheduling a task
|
||||
*/
|
||||
void antd_scheduler_add_task(antd_scheduler_t *scheduler, antd_task_t *task)
|
||||
{
|
||||
if (task->id == 0)
|
||||
task->id = antd_scheduler_next_id(scheduler, task->id);
|
||||
pthread_mutex_lock(&scheduler->scheduler_lock);
|
||||
scheduler->task_queue = bst_insert(scheduler->task_queue, task->id, (void *)task);
|
||||
pthread_mutex_unlock(&scheduler->scheduler_lock);
|
||||
pthread_mutex_lock(&scheduler->pending_lock);
|
||||
scheduler->pending_task++;
|
||||
pthread_mutex_unlock(&scheduler->pending_lock);
|
||||
// wake up the scheduler if idle
|
||||
sem_post(scheduler->scheduler_sem);
|
||||
}
|
||||
|
||||
static void antd_execute_task(antd_scheduler_t *scheduler, antd_task_t *task)
|
||||
{
|
||||
if (!task)
|
||||
return;
|
||||
// execute the task
|
||||
void *ret = (*(task->handle))(task->data);
|
||||
// check the return data if it is a new task
|
||||
if (!ret)
|
||||
{
|
||||
// call the first callback
|
||||
execute_callback(scheduler, task);
|
||||
}
|
||||
else
|
||||
{
|
||||
antd_task_t *rtask = (antd_task_t *)ret;
|
||||
if (task->callback)
|
||||
{
|
||||
if (rtask->callback)
|
||||
{
|
||||
enqueue_callback(rtask->callback, task->callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
rtask->callback = task->callback;
|
||||
}
|
||||
task->callback = NULL;
|
||||
}
|
||||
if (!rtask->handle)
|
||||
{
|
||||
// call the first callback
|
||||
execute_callback(scheduler, rtask);
|
||||
destroy_task(task);
|
||||
}
|
||||
else
|
||||
{
|
||||
antd_scheduler_add_task(scheduler, rtask);
|
||||
destroy_task(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int antd_scheduler_busy(antd_scheduler_t *scheduler)
|
||||
{
|
||||
return scheduler->pending_task != 0;
|
||||
}
|
||||
|
||||
void antd_scheduler_lock(antd_scheduler_t *sched)
|
||||
{
|
||||
pthread_mutex_lock(&sched->scheduler_lock);
|
||||
}
|
||||
|
||||
void antd_scheduler_unlock(antd_scheduler_t *sched)
|
||||
{
|
||||
pthread_mutex_unlock(&sched->scheduler_lock);
|
||||
}
|
||||
|
||||
static void antd_task_schedule(antd_scheduler_t *scheduler, antd_task_t *task)
|
||||
{
|
||||
// no task
|
||||
if (!task)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&scheduler->pending_lock);
|
||||
scheduler->pending_task--;
|
||||
pthread_mutex_unlock(&scheduler->pending_lock);
|
||||
// has the task now
|
||||
// validate the task
|
||||
//if (scheduler->validate_data && difftime(time(NULL), it->task->access_time) > MAX_VALIDITY_INTERVAL && it->task->priority == N_PRIORITY - 1)
|
||||
if (antd_scheduler_validate_data(task) == 0)
|
||||
{
|
||||
// data task is not valid
|
||||
LOG("Task is no longer valid and will be killed");
|
||||
antd_scheduler_destroy_data(task->data);
|
||||
destroy_task(task);
|
||||
return;
|
||||
}
|
||||
|
||||
if (scheduler->n_workers <= 0)
|
||||
{
|
||||
// do it by myself
|
||||
antd_execute_task(scheduler, task);
|
||||
}
|
||||
else
|
||||
{
|
||||
// delegate to other workers by
|
||||
//pushing to the worker queue
|
||||
pthread_mutex_lock(&scheduler->worker_lock);
|
||||
enqueue(&scheduler->workers_queue, task);
|
||||
pthread_mutex_unlock(&scheduler->worker_lock);
|
||||
// wake up idle worker
|
||||
sem_post(scheduler->worker_sem);
|
||||
}
|
||||
}
|
||||
|
||||
static void task_polls_collect(bst_node_t* node, void** argv, int argc)
|
||||
{
|
||||
UNUSED(argc);
|
||||
antd_task_evt_item_t* it = (antd_task_evt_item_t*)node->data;
|
||||
struct pollfd* pfds = (struct pollfd*)argv[0];
|
||||
if(it)
|
||||
{
|
||||
pfds[node->key].fd = it->fd;
|
||||
if(it->flags & TASK_EVT_ON_READABLE)
|
||||
{
|
||||
pfds[node->key].events |= POLLIN;
|
||||
}
|
||||
if(it->flags & TASK_EVT_ON_WRITABLE)
|
||||
{
|
||||
pfds[node->key].events |= POLLOUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
static void antd_deploy_task(bst_node_t* node, void** argv, int argc)
|
||||
{
|
||||
UNUSED(argc);
|
||||
if(!node || !node->data)
|
||||
return;
|
||||
antd_scheduler_t* sched = (antd_scheduler_t*) argv[0];
|
||||
antd_task_t* task = node->data;
|
||||
pthread_mutex_lock(&sched->scheduler_lock);
|
||||
sched->task_queue = bst_delete(sched->task_queue, task->id);
|
||||
pthread_mutex_unlock(&sched->scheduler_lock);
|
||||
antd_task_schedule(sched, task);
|
||||
}
|
||||
static void task_event_collect(bst_node_t* node, void** argv, int argc)
|
||||
{
|
||||
UNUSED(argc);
|
||||
antd_task_t* task = (antd_task_t*) node->data;
|
||||
bst_node_t** exec_list = (bst_node_t**) argv[0];
|
||||
bst_node_t** poll_list = (bst_node_t**) argv[1];
|
||||
struct timeval now;
|
||||
int* pollsize = (int*) argv[2];
|
||||
if(!task->events)
|
||||
{
|
||||
*exec_list = bst_insert(*exec_list,task->id, task);
|
||||
return;
|
||||
}
|
||||
antd_queue_item_t it = task->events;
|
||||
while(it)
|
||||
{
|
||||
if((it->evt->flags & TASK_EVT_ALWAY_ON) || antd_scheduler_validate_data(task) == 0 )
|
||||
{
|
||||
*exec_list = bst_insert(*exec_list,task->id, task);
|
||||
}
|
||||
else if(it->evt->flags & TASK_EVT_ON_TIMEOUT)
|
||||
{
|
||||
// check if timeout
|
||||
gettimeofday(&now, NULL);
|
||||
//do stuff
|
||||
int diff = (int)(((now.tv_sec - it->evt->stamp.tv_sec) * 1000000 + now.tv_usec - it->evt->stamp.tv_usec) / 1000);
|
||||
if( diff >= it->evt->timeout )
|
||||
{
|
||||
*exec_list = bst_insert(*exec_list,task->id, task);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*poll_list = bst_insert(*poll_list, *pollsize, it->evt);
|
||||
*pollsize = (*pollsize)+1;
|
||||
}
|
||||
it = it->next;
|
||||
}
|
||||
}
|
||||
|
||||
void antd_task_bind_event(antd_task_t *task, int fd, int timeout, int flags)
|
||||
{
|
||||
antd_task_evt_item_t *eit = (antd_task_evt_item_t *)malloc(sizeof(antd_task_evt_item_t));
|
||||
eit->fd = fd;
|
||||
eit->timeout = timeout;
|
||||
eit->flags = flags;
|
||||
eit->task = task;
|
||||
gettimeofday(&eit->stamp, NULL);
|
||||
enqueue(&task->events, eit);
|
||||
}
|
||||
|
||||
void *antd_scheduler_wait(void *ptr)
|
||||
{
|
||||
int pollsize, ready;
|
||||
void *argv[3];
|
||||
//antd_queue_t exec_list = NULL;
|
||||
bst_node_t* poll_list = NULL;
|
||||
bst_node_t* scheduled_list = NULL;
|
||||
antd_task_evt_item_t *eit = NULL;
|
||||
bst_node_t* node, *task_node = NULL;
|
||||
struct pollfd *pfds = NULL;
|
||||
antd_scheduler_t *scheduler = (antd_scheduler_t *)ptr;
|
||||
|
||||
while (scheduler->status)
|
||||
{
|
||||
pollsize = 0;
|
||||
argv[0] = &scheduled_list;
|
||||
argv[1] = &poll_list;
|
||||
argv[2] = &pollsize;
|
||||
pthread_mutex_lock(&scheduler->scheduler_lock);
|
||||
bst_for_each(scheduler->task_queue, task_event_collect, argv, 3);
|
||||
pthread_mutex_unlock(&scheduler->scheduler_lock);
|
||||
// schedule exec list first
|
||||
/*it = exec_list;
|
||||
while(it)
|
||||
{
|
||||
if(it->task)
|
||||
{
|
||||
|
||||
pthread_mutex_lock(&scheduler->scheduler_lock);
|
||||
scheduler->task_queue = bst_delete(scheduler->task_queue, it->task->id);
|
||||
pthread_mutex_unlock(&scheduler->scheduler_lock);
|
||||
antd_task_schedule(scheduler, it->task);
|
||||
}
|
||||
curr = it;
|
||||
it = it->next;
|
||||
free(curr);
|
||||
}*/
|
||||
// Detect event on pollist
|
||||
if(pollsize > 0)
|
||||
{
|
||||
pfds = (struct pollfd*)malloc(pollsize*sizeof(struct pollfd));
|
||||
memset(pfds, 0, pollsize*sizeof(struct pollfd));
|
||||
if(pfds)
|
||||
{
|
||||
argv[0] = pfds;
|
||||
bst_for_each(poll_list,task_polls_collect, argv, 1);
|
||||
// now poll event
|
||||
ready = poll(pfds, pollsize, POLL_EVENT_TO);
|
||||
if(ready == -1)
|
||||
{
|
||||
// this should not happends
|
||||
ERROR("Unable to poll: %s", strerror(errno));
|
||||
// TODO: exit ?
|
||||
}
|
||||
else if(ready > 0)
|
||||
{
|
||||
for (int i = 0; i < pollsize; i++)
|
||||
{
|
||||
// find the event
|
||||
task_node = NULL;
|
||||
eit = NULL;
|
||||
node = bst_find(poll_list,i);
|
||||
if(node)
|
||||
{
|
||||
eit = (antd_task_evt_item_t *)node->data;
|
||||
}
|
||||
if(eit)
|
||||
{
|
||||
if( ((eit->flags & TASK_EVT_ON_READABLE) && (pfds[i].revents & POLLIN))
|
||||
|| ( (eit->flags & TASK_EVT_ON_WRITABLE) && (pfds[i].revents & POLLOUT))
|
||||
) {
|
||||
// event triggered schedule the task
|
||||
pthread_mutex_lock(&scheduler->scheduler_lock);
|
||||
task_node = bst_find(scheduler->task_queue, eit->task->id);
|
||||
pthread_mutex_unlock(&scheduler->scheduler_lock);
|
||||
if(task_node)
|
||||
scheduled_list = bst_insert(scheduled_list, eit->task->id, eit->task);
|
||||
//antd_task_schedule(scheduler, eit->task);
|
||||
}
|
||||
else if( (pfds[i].revents & POLLERR) || (pfds[i].revents & POLLHUP)) {
|
||||
// task is no longer available
|
||||
ERROR("Poll: Task %d is no longer valid. Remove it", eit->task->id);
|
||||
eit->task->access_time = 0;
|
||||
eit->task->handle = NULL;
|
||||
/*
|
||||
antd_scheduler_destroy_data(eit->task->data);
|
||||
eit->task->data = NULL;*/
|
||||
scheduled_list = bst_insert(scheduled_list, eit->task->id, eit->task);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(pfds);
|
||||
}
|
||||
}
|
||||
if(scheduled_list)
|
||||
{
|
||||
argv[0] = scheduler;
|
||||
bst_for_each(scheduled_list, antd_deploy_task, argv, 1);
|
||||
bst_free(scheduled_list);
|
||||
scheduled_list = NULL;
|
||||
}
|
||||
bst_free(poll_list);
|
||||
poll_list = NULL;
|
||||
|
||||
if (!scheduler->task_queue)
|
||||
{
|
||||
// reset id allocator
|
||||
//scheduler->id_allocator=0;
|
||||
// no task found, go to idle state
|
||||
sem_wait(scheduler->scheduler_sem);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int antd_scheduler_ok(antd_scheduler_t *scheduler)
|
||||
{
|
||||
return scheduler->status;
|
||||
}
|
||||
int antd_scheduler_next_id(antd_scheduler_t *sched, int input)
|
||||
{
|
||||
int id = input;
|
||||
if(sched->id_allocator < 0)
|
||||
{
|
||||
sched->id_allocator = 0;
|
||||
}
|
||||
pthread_mutex_lock(&sched->scheduler_lock);
|
||||
if (id == 0)
|
||||
{
|
||||
sched->id_allocator++;
|
||||
id = sched->id_allocator;
|
||||
}
|
||||
|
||||
while (bst_find(sched->task_queue, id) != NULL)
|
||||
{
|
||||
sched->id_allocator++;
|
||||
id = sched->id_allocator;
|
||||
}
|
||||
pthread_mutex_unlock(&sched->scheduler_lock);
|
||||
return id;
|
||||
}
|
||||
void antd_scheduler_ext_statistic(int fd, void *data)
|
||||
{
|
||||
UNUSED(fd);
|
||||
UNUSED(data);
|
||||
}
|
||||
int antd_scheduler_validate_data(antd_task_t *task)
|
||||
{
|
||||
UNUSED(task);
|
||||
return !(difftime(time(NULL), task->access_time) > MAX_VALIDITY_INTERVAL);
|
||||
}
|
||||
void antd_scheduler_destroy_data(void *data)
|
||||
{
|
||||
UNUSED(data);
|
||||
}
|
||||
|
||||
int antd_task_data_id(void *data)
|
||||
{
|
||||
UNUSED(data);
|
||||
intptr_t ptr = (intptr_t)data;
|
||||
return (int)ptr;
|
||||
}
|
117
lib/scheduler.h
Normal file
117
lib/scheduler.h
Normal file
@ -0,0 +1,117 @@
|
||||
#ifndef ANT_SCHEDULER
|
||||
#define ANT_SCHEDULER
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
// define the event
|
||||
#define TASK_EVT_ALWAY_ON 0x01
|
||||
#define TASK_EVT_ON_READABLE 0x02
|
||||
#define TASK_EVT_ON_WRITABLE 0x04
|
||||
#define TASK_EVT_ON_TIMEOUT 0x08
|
||||
#define POLL_EVENT_TO 10 // ms
|
||||
|
||||
typedef struct _antd_scheduler_t antd_scheduler_t;
|
||||
typedef struct _antd_callback_t antd_callback_t;
|
||||
typedef struct _antd_queue_item_t* antd_task_evt_list_t;
|
||||
typedef struct
|
||||
{
|
||||
/**
|
||||
* task id
|
||||
*/
|
||||
int id;
|
||||
/**
|
||||
* creation time of a task
|
||||
*/
|
||||
unsigned long stamp;
|
||||
/**
|
||||
* Last access time of
|
||||
* task data
|
||||
*/
|
||||
time_t access_time;
|
||||
/**
|
||||
* the handle and callback
|
||||
*/
|
||||
void *(*handle)(void *);
|
||||
antd_callback_t *callback;
|
||||
/**
|
||||
* The task events
|
||||
* each task must be binded to
|
||||
* one or more event, otherwise it will be
|
||||
* rejected by the scheduler
|
||||
* */
|
||||
antd_task_evt_list_t events;
|
||||
/**
|
||||
* user data if any
|
||||
*/
|
||||
void *data;
|
||||
} antd_task_t;
|
||||
/*
|
||||
* nit the main scheduler
|
||||
*/
|
||||
antd_scheduler_t *antd_scheduler_init(int, const char *stat_name);
|
||||
/*
|
||||
* destroy all pending task
|
||||
*/
|
||||
void antd_scheduler_destroy(antd_scheduler_t *);
|
||||
|
||||
/**
|
||||
* create a task
|
||||
* parameter:
|
||||
* - handle
|
||||
* - data
|
||||
* - callback
|
||||
* - last data access time
|
||||
*/
|
||||
antd_task_t *antd_create_task(void *(*handle)(void *), void *data, void *(*callback)(void *), time_t);
|
||||
|
||||
/**
|
||||
* ALWAY_ON flag doest not need a file descriptor, it will be executed immediately by the scheduler
|
||||
* ANY file descriptor should work with READABLE and WRITABLE flags, including timerfd for precision timeout
|
||||
* Timeout flag (in seconds precision): val is the number of seconds
|
||||
*
|
||||
* File descriptor close operation is not handled by the scheduler
|
||||
*
|
||||
* */
|
||||
void antd_task_bind_event(antd_task_t* task, int fd, int timeout, int flags);
|
||||
/**
|
||||
* add a task
|
||||
*/
|
||||
void antd_scheduler_add_task(antd_scheduler_t *, antd_task_t *);
|
||||
|
||||
/**
|
||||
* check if scheduler is busy
|
||||
*/
|
||||
int antd_scheduler_busy(antd_scheduler_t *);
|
||||
/**
|
||||
* get scheduler status
|
||||
* */
|
||||
int antd_scheduler_ok(antd_scheduler_t *scheduler);
|
||||
/**
|
||||
*
|
||||
* wait for event
|
||||
*/
|
||||
void *antd_scheduler_wait(void *);
|
||||
|
||||
/**
|
||||
* lock the scheduler
|
||||
* */
|
||||
void antd_scheduler_lock(antd_scheduler_t *);
|
||||
/**
|
||||
* Get next valid task id
|
||||
* */
|
||||
int antd_scheduler_next_id(antd_scheduler_t *sched, int input);
|
||||
/**
|
||||
* unlock the scheduler
|
||||
* */
|
||||
void antd_scheduler_unlock(antd_scheduler_t *);
|
||||
|
||||
/**
|
||||
* weak functions that should be overridden by the application
|
||||
* that user the scheduler as library
|
||||
*/
|
||||
void __attribute__((weak)) antd_scheduler_ext_statistic(int fd, void *data);
|
||||
int __attribute__((weak)) antd_scheduler_validate_data(antd_task_t *task);
|
||||
void __attribute__((weak)) antd_scheduler_destroy_data(void *data);
|
||||
int __attribute__((weak)) antd_task_data_id(void *data);
|
||||
#endif
|
@ -77,6 +77,8 @@ A million repetitions of "a"
|
||||
*/
|
||||
|
||||
/* #define SHA1HANDSOFF */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sha1.h"
|
||||
|
||||
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
|
@ -3,8 +3,6 @@
|
||||
|
||||
#ifndef __SHA1_H
|
||||
#define __SHA1_H
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
750
lib/utils.c
Normal file
750
lib/utils.c
Normal file
@ -0,0 +1,750 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 LE Xuan Sang xsang.le@gmail.com
|
||||
|
||||
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.
|
||||
*/
|
||||
#include "utils.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h> //hostent
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
#include <openssl/sha.h>
|
||||
#else
|
||||
#include "sha1.h"
|
||||
#endif
|
||||
|
||||
#include "dictionary.h"
|
||||
// #include <time.h>
|
||||
|
||||
static dictionary_t g_mime_list = NULL;
|
||||
|
||||
/**
|
||||
* Trim a string by a character on both ends
|
||||
* @param str The target string
|
||||
* @param delim the delim
|
||||
*/
|
||||
void trim(char *str, const char delim)
|
||||
{
|
||||
if (!str || strlen(str) == 0)
|
||||
return;
|
||||
char *p = str;
|
||||
int l = strlen(p);
|
||||
while (l > 0 && p[l - 1] == delim)
|
||||
p[--l] = 0;
|
||||
while (*p && (*p) == delim)
|
||||
++p, --l;
|
||||
memmove(str, p, l + 1);
|
||||
}
|
||||
|
||||
void removeAll(const char *path, int mode)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *dir;
|
||||
char *file;
|
||||
struct stat st;
|
||||
if (stat(path, &st) == 0)
|
||||
{
|
||||
if (S_ISDIR(st.st_mode))
|
||||
{
|
||||
d = opendir(path);
|
||||
if (d)
|
||||
{
|
||||
while ((dir = readdir(d)) != NULL)
|
||||
{
|
||||
if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0)
|
||||
continue;
|
||||
file = __s("%s/%s", path, dir->d_name);
|
||||
|
||||
removeAll(file, 1);
|
||||
free(file);
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
}
|
||||
if (mode)
|
||||
remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
// WARNING:
|
||||
// TODO: remove it, this function is not thread-safe
|
||||
|
||||
void timestr(time_t time, char *buf, int len, char *format, int gmt)
|
||||
{
|
||||
struct tm t;
|
||||
if (gmt)
|
||||
{
|
||||
gmtime_r(&time, &t);
|
||||
}
|
||||
else
|
||||
{
|
||||
localtime_r(&time, &t);
|
||||
}
|
||||
strftime(buf, len, format, &t);
|
||||
}
|
||||
void server_time(char *buf, int len)
|
||||
{
|
||||
timestr(time(NULL), buf, len, "%a, %d %b %Y %H:%M:%S", 0);
|
||||
}
|
||||
/**
|
||||
* Get extension of a file name
|
||||
* @param file The file name
|
||||
* @return the extension
|
||||
*/
|
||||
char *ext(const char *file)
|
||||
{
|
||||
char *token, *ltoken = "";
|
||||
if (file == NULL)
|
||||
return NULL;
|
||||
char *str_cpy = strdup(file);
|
||||
char *str_org = str_cpy;
|
||||
if (!strstr(str_cpy, "."))
|
||||
{
|
||||
free(str_org);
|
||||
return NULL;
|
||||
}
|
||||
if (*file == '.')
|
||||
trim(str_cpy, '.');
|
||||
|
||||
while ((token = strsep(&str_cpy, ".")) && strlen(token) > 0)
|
||||
{
|
||||
ltoken = token;
|
||||
}
|
||||
char *ext = strdup(ltoken);
|
||||
free(str_org);
|
||||
return ext;
|
||||
}
|
||||
/*get mime file info from extension*/
|
||||
mime_t mime_from_ext(const char *ex)
|
||||
{
|
||||
dictionary_t mime_list = mimes_list();
|
||||
mime_t ret = (mime_t){"application/octet-stream", NULL};
|
||||
if (!mime_list)
|
||||
return ret;
|
||||
chain_t it;
|
||||
char *pattern = __s("(^\\s*%s\\s*,)|(\\s*,\\s*%s\\s*,\\s*)|(^\\s*%s\\s*$)|(,\\s*%s\\s*$)", ex, ex, ex, ex);
|
||||
if (pattern)
|
||||
{
|
||||
for_each_assoc(it, mime_list)
|
||||
{
|
||||
|
||||
if (regex_match(pattern, it->value, 0, NULL))
|
||||
{
|
||||
ret.type = it->key;
|
||||
ret.ext = it->value;
|
||||
free(pattern);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
free(pattern);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void verify_header(char *k)
|
||||
{
|
||||
k[0] = toupper(k[0]);
|
||||
int len = strlen(k);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (k[i] == '-' && i < len - 1)
|
||||
{
|
||||
k[i + 1] = toupper(k[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dictionary_t mimes_list()
|
||||
{
|
||||
return g_mime_list;
|
||||
}
|
||||
|
||||
void set_mimes_list(dictionary_t dict)
|
||||
{
|
||||
g_mime_list = dict;
|
||||
}
|
||||
|
||||
/*get mime file info from type*/
|
||||
mime_t mime_from_type(const char *type)
|
||||
{
|
||||
dictionary_t mime_list = mimes_list();
|
||||
mime_t ret = (mime_t){NULL, NULL};
|
||||
if (!mime_list)
|
||||
return ret;
|
||||
chain_t it = dlookup(mime_list, type);
|
||||
if (it)
|
||||
{
|
||||
ret.type = it->key;
|
||||
ret.ext = it->value;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Get correct HTTP mime type of a file
|
||||
* This is a minimalistic mime type list supported
|
||||
* by the server
|
||||
* @param file File name
|
||||
* @return The HTTP Mime Type
|
||||
*/
|
||||
char *mime(const char *file)
|
||||
{
|
||||
char *ex = ext(file);
|
||||
if (!ex)
|
||||
return "application/octet-stream";
|
||||
mime_t m = mime_from_ext(ex);
|
||||
if (ex)
|
||||
{
|
||||
free(ex);
|
||||
}
|
||||
return (char *)m.type;
|
||||
}
|
||||
|
||||
int match_int(const char *search)
|
||||
{
|
||||
return regex_match("^[-+]?[0-9]+$", search, 0, NULL);
|
||||
}
|
||||
int match_float(const char *search)
|
||||
{
|
||||
return regex_match("^[+-]?[0-9]*\\.[0-9]+$", search, 0, NULL);
|
||||
}
|
||||
/*
|
||||
regmatch_t matches[MAX_MATCHES];
|
||||
if (regexec(&exp, sz, MAX_MATCHES, matches, 0) == 0) {
|
||||
memcpy(buff, sz + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
|
||||
printf("group1: %s\n", buff);
|
||||
}
|
||||
*/
|
||||
int regex_match(const char *expr, const char *search, int msize, regmatch_t *matches)
|
||||
{
|
||||
regex_t regex;
|
||||
int reti;
|
||||
char msgbuf[100];
|
||||
int ret;
|
||||
/* Compile regular expression */
|
||||
reti = regcomp(®ex, expr, REG_ICASE | REG_EXTENDED);
|
||||
if (reti)
|
||||
{
|
||||
// ERROR("Could not compile regex: %s",expr);
|
||||
regerror(reti, ®ex, msgbuf, sizeof(msgbuf));
|
||||
ERROR("Regex match failed: %s", msgbuf);
|
||||
// return 0;
|
||||
}
|
||||
|
||||
/* Execute regular expression */
|
||||
reti = regexec(®ex, search, msize, matches, 0);
|
||||
if (!reti)
|
||||
{
|
||||
// LOG("Match");
|
||||
ret = 1;
|
||||
}
|
||||
else if (reti == REG_NOMATCH)
|
||||
{
|
||||
// LOG("No match");
|
||||
ret = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
regerror(reti, ®ex, msgbuf, sizeof(msgbuf));
|
||||
// ERROR("Regex match failed: %s\n", msgbuf);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
regfree(®ex);
|
||||
return ret;
|
||||
}
|
||||
char *url_decode(const char *str)
|
||||
{
|
||||
if (!str)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
char *pstr = (char *)str, *buf = malloc(strlen(str) + 1), *pbuf = buf;
|
||||
|
||||
while (*pstr)
|
||||
{
|
||||
if (*pstr == '%')
|
||||
{
|
||||
if (pstr[1] && pstr[2])
|
||||
{
|
||||
*pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
|
||||
pstr += 2;
|
||||
}
|
||||
}
|
||||
else if (*pstr == '+')
|
||||
{
|
||||
*pbuf++ = ' ';
|
||||
}
|
||||
else
|
||||
{
|
||||
*pbuf++ = *pstr;
|
||||
}
|
||||
pstr++;
|
||||
}
|
||||
*pbuf = '\0';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *url_encode(const char *str)
|
||||
{
|
||||
char *pstr = (char *)str, *buf = malloc(strlen(str) * 3 + 1), *pbuf = buf;
|
||||
while (*pstr)
|
||||
{
|
||||
if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
|
||||
*pbuf++ = *pstr;
|
||||
else if (*pstr == ' ')
|
||||
*pbuf++ = '+';
|
||||
else
|
||||
*pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
|
||||
pstr++;
|
||||
}
|
||||
*pbuf = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
char from_hex(char ch)
|
||||
{
|
||||
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
|
||||
}
|
||||
|
||||
char to_hex(char code)
|
||||
{
|
||||
static char hex[] = "0123456789abcdef";
|
||||
return hex[code & 15];
|
||||
}
|
||||
unsigned hash(const char *key, int hash_size)
|
||||
{
|
||||
unsigned hashval = simple_hash(key);
|
||||
return hashval % hash_size;
|
||||
}
|
||||
unsigned simple_hash(const char *key)
|
||||
{
|
||||
unsigned hashval;
|
||||
for (hashval = 0; *key != '\0'; key++)
|
||||
hashval = *key + 31 * hashval;
|
||||
return hashval;
|
||||
}
|
||||
int _exist(const char *f)
|
||||
{
|
||||
struct stat st;
|
||||
return !(stat(f, &st) == -1);
|
||||
}
|
||||
int is_file(const char *f)
|
||||
{
|
||||
int st = is_dir(f);
|
||||
if (st == -1)
|
||||
return -1;
|
||||
else
|
||||
return st == 0;
|
||||
}
|
||||
int is_dir(const char *f)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(f, &st) == -1)
|
||||
return -1; // unknow
|
||||
else if (st.st_mode & S_IFDIR)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
// These vars will contain the hash
|
||||
|
||||
void md5(uint8_t *initial_msg, size_t initial_len, char *buff)
|
||||
{
|
||||
uint32_t h0, h1, h2, h3;
|
||||
char tmp[80];
|
||||
uint8_t *msg = NULL;
|
||||
uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
|
||||
// Use binary integer part of the sines of integers (in radians) as constants// Initialize variables:
|
||||
uint32_t k[] = {
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
||||
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
||||
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
||||
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
|
||||
|
||||
h0 = 0x67452301;
|
||||
h1 = 0xefcdab89;
|
||||
h2 = 0x98badcfe;
|
||||
h3 = 0x10325476;
|
||||
|
||||
// Pre-processing: adding a single 1 bit
|
||||
// append "1" bit to message
|
||||
/* Notice: the input bytes are considered as bits strings,
|
||||
where the first bit is the most significant bit of the byte.[37] */
|
||||
|
||||
// Pre-processing: padding with zeros
|
||||
// append "0" bit until message length in bit ≡ 448 (mod 512)
|
||||
// append length mod (2 pow 64) to message
|
||||
|
||||
int new_len;
|
||||
for (new_len = initial_len * 8 + 1; new_len % 512 != 448; new_len++)
|
||||
;
|
||||
new_len /= 8;
|
||||
|
||||
msg = calloc(new_len + 64, 1); // also appends "0" bits
|
||||
// (we alloc also 64 extra bytes...)
|
||||
memcpy(msg, initial_msg, initial_len);
|
||||
msg[initial_len] = 128; // write the "1" bit
|
||||
|
||||
uint32_t bits_len = 8 * initial_len; // note, we append the len
|
||||
memcpy(msg + new_len, &bits_len, 4); // in bits at the end of the buffer
|
||||
|
||||
// Process the message in successive 512-bit chunks:
|
||||
// for each 512-bit chunk of message:
|
||||
int offset;
|
||||
for (offset = 0; offset < new_len; offset += (512 / 8))
|
||||
{
|
||||
|
||||
// break chunk into sixteen 32-bit words w[j], 0 ≤ j ≤ 15
|
||||
uint32_t *w = (uint32_t *)(msg + offset);
|
||||
|
||||
// Initialize hash value for this chunk:
|
||||
uint32_t a = h0;
|
||||
uint32_t b = h1;
|
||||
uint32_t c = h2;
|
||||
uint32_t d = h3;
|
||||
|
||||
// Main loop:
|
||||
uint32_t i;
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
|
||||
uint32_t f, g;
|
||||
|
||||
if (i < 16)
|
||||
{
|
||||
f = (b & c) | ((~b) & d);
|
||||
g = i;
|
||||
}
|
||||
else if (i < 32)
|
||||
{
|
||||
f = (d & b) | ((~d) & c);
|
||||
g = (5 * i + 1) % 16;
|
||||
}
|
||||
else if (i < 48)
|
||||
{
|
||||
f = b ^ c ^ d;
|
||||
g = (3 * i + 5) % 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
f = c ^ (b | (~d));
|
||||
g = (7 * i) % 16;
|
||||
}
|
||||
|
||||
uint32_t temp = d;
|
||||
d = c;
|
||||
c = b;
|
||||
// printf("rotateLeft(%x + %x + %x + %x, %d)\n", a, f, k[i], w[g], r[i]);
|
||||
b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]);
|
||||
a = temp;
|
||||
}
|
||||
|
||||
// Add this chunk's hash to result so far:
|
||||
|
||||
h0 += a;
|
||||
h1 += b;
|
||||
h2 += c;
|
||||
h3 += d;
|
||||
}
|
||||
uint8_t *p;
|
||||
p = (uint8_t *)&h0;
|
||||
sprintf(tmp, "%02x%02x%02x%02x", p[0], p[1], p[2], p[3]); //, h0
|
||||
strcpy(buff, tmp);
|
||||
p = (uint8_t *)&h1;
|
||||
sprintf(tmp, "%02x%02x%02x%02x", p[0], p[1], p[2], p[3]); //, h1)
|
||||
strcat(buff, tmp);
|
||||
p = (uint8_t *)&h2;
|
||||
sprintf(tmp, "%02x%02x%02x%02x", p[0], p[1], p[2], p[3]); // , h2
|
||||
strcat(buff, tmp);
|
||||
p = (uint8_t *)&h3;
|
||||
sprintf(tmp, "%02x%02x%02x%02x", p[0], p[1], p[2], p[3]); // , h3
|
||||
strcat(buff, tmp);
|
||||
// cleanup
|
||||
free(msg);
|
||||
}
|
||||
void sha1(const char *text, char *out)
|
||||
{
|
||||
uint8_t d[20];
|
||||
#ifdef USE_OPENSSL
|
||||
SHA_CTX context;
|
||||
#else
|
||||
SHA1_CTX context;
|
||||
#endif
|
||||
SHA1_Init(&context);
|
||||
SHA1_Update(&context, text, strlen(text));
|
||||
SHA1_Final(d, &context);
|
||||
digest_to_hex(d, out);
|
||||
}
|
||||
|
||||
char *__s(const char *fstring, ...)
|
||||
{
|
||||
char *data;
|
||||
va_list arguments;
|
||||
int dlen;
|
||||
va_start(arguments, fstring);
|
||||
dlen = vsnprintf(0, 0, fstring, arguments) + 1;
|
||||
va_end(arguments);
|
||||
if ((data = (char *)malloc(dlen * sizeof(char))) != 0)
|
||||
{
|
||||
va_start(arguments, fstring);
|
||||
vsnprintf(data, dlen, fstring, arguments);
|
||||
va_end(arguments);
|
||||
return data;
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
int mkdirp(const char *path, mode_t mode)
|
||||
{
|
||||
char tmp[BUFFLEN];
|
||||
if (strlen(path) > BUFFLEN)
|
||||
{
|
||||
ERROR("mkdirp: Path is too long %s", path);
|
||||
return -1;
|
||||
}
|
||||
char *p = NULL;
|
||||
size_t len;
|
||||
int stat;
|
||||
snprintf(tmp, sizeof(tmp), "%s", path);
|
||||
len = strlen(tmp);
|
||||
if (tmp[len - 1] == '/')
|
||||
tmp[len - 1] = 0;
|
||||
for (p = tmp + 1; *p; p++)
|
||||
{
|
||||
if (*p == '/')
|
||||
{
|
||||
*p = 0;
|
||||
stat = mkdir(tmp, mode);
|
||||
if (stat == -1 && errno != EEXIST)
|
||||
return stat;
|
||||
*p = '/';
|
||||
}
|
||||
}
|
||||
return mkdir(path, mode);
|
||||
}
|
||||
|
||||
int guard_read(int fd, void *buffer, size_t size)
|
||||
{
|
||||
int n = 0;
|
||||
int read_len;
|
||||
int st;
|
||||
while (n != (int)size)
|
||||
{
|
||||
read_len = (int)size - n;
|
||||
st = read(fd, buffer + n, read_len);
|
||||
if (st == -1)
|
||||
{
|
||||
ERROR("Unable to read from #%d: %s", fd, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (st == 0)
|
||||
{
|
||||
ERROR("Endpoint %d is closed", fd);
|
||||
return -1;
|
||||
}
|
||||
n += st;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int guard_write(int fd, void *buffer, size_t size)
|
||||
{
|
||||
int n = 0;
|
||||
int write_len;
|
||||
int st;
|
||||
while (n != (int)size)
|
||||
{
|
||||
write_len = (int)size - n;
|
||||
st = write(fd, buffer + n, write_len);
|
||||
if (st == -1)
|
||||
{
|
||||
ERROR("Unable to write to #%d: %s", fd, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (st == 0)
|
||||
{
|
||||
ERROR("Endpoint %d is closed", fd);
|
||||
return -1;
|
||||
}
|
||||
n += st;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
send a request
|
||||
*/
|
||||
int antd_request_socket(const char *ip, int port)
|
||||
{
|
||||
int sockfd;
|
||||
struct sockaddr_in dest;
|
||||
if (!ip)
|
||||
{
|
||||
ERROR("Invalid IP address");
|
||||
return -1;
|
||||
}
|
||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
ERROR("Socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
/*struct linger lingerStruct;
|
||||
lingerStruct.l_onoff = 0; // turn lingering off for sockets
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &lingerStruct, sizeof(lingerStruct));*/
|
||||
|
||||
bzero(&dest, sizeof(dest));
|
||||
dest.sin_family = AF_INET;
|
||||
dest.sin_port = htons(port);
|
||||
if (inet_aton(ip, &dest.sin_addr) == 0)
|
||||
{
|
||||
ERROR("[%s] inet_aton: %s", ip, strerror(errno));
|
||||
close(sockfd);
|
||||
return -1;
|
||||
}
|
||||
if (connect(sockfd, (struct sockaddr *)&dest, sizeof(dest)) != 0)
|
||||
{
|
||||
close(sockfd);
|
||||
ERROR("Connect:%s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
char *ip_from_hostname(const char *hostname)
|
||||
{
|
||||
struct hostent *he;
|
||||
struct in_addr **addr_list;
|
||||
int i;
|
||||
if (!hostname)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if ((he = gethostbyname(hostname)) == NULL)
|
||||
{
|
||||
// get the host info
|
||||
ERROR("gethostbyname:%s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
addr_list = (struct in_addr **)he->h_addr_list;
|
||||
|
||||
for (i = 0; addr_list[i] != NULL; i++)
|
||||
{
|
||||
// Return the first one;
|
||||
return inet_ntoa(*addr_list[i]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int antd_listen(unsigned *port, int ipv6, int backlog)
|
||||
{
|
||||
int httpd = 0;
|
||||
antd_sockaddr_t name;
|
||||
memset(&name, 0, sizeof(name));
|
||||
struct sockaddr *ptr;
|
||||
// TODO: allow to set listen address
|
||||
if (ipv6)
|
||||
{
|
||||
name.addr6.sin6_port = htons(*port);
|
||||
name.addr6.sin6_family = AF_INET6;
|
||||
name.addr6.sin6_addr = in6addr_any;
|
||||
httpd = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
ptr = (struct sockaddr *)&name.addr6;
|
||||
}
|
||||
else
|
||||
{
|
||||
name.addr4.sin_port = htons(*port);
|
||||
name.addr4.sin_family = AF_INET;
|
||||
name.addr4.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
httpd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
ptr = (struct sockaddr *)&name.addr4;
|
||||
}
|
||||
|
||||
if (httpd == -1)
|
||||
{
|
||||
ERROR("Port %d - socket: %s", *port, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) == -1)
|
||||
{
|
||||
ERROR("Unable to set reuse address on port %d - setsockopt: %s", *port, strerror(errno));
|
||||
}
|
||||
|
||||
if (bind(httpd, ptr, sizeof(name)) < 0)
|
||||
{
|
||||
ERROR("Port %d -bind: %s", *port, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (*port == 0) /* if dynamically allocating a port */
|
||||
{
|
||||
socklen_t namelen = sizeof(name);
|
||||
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
|
||||
{
|
||||
ERROR("Port %d - getsockname: %s", *port, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (ipv6)
|
||||
{
|
||||
*port = ntohs(name.addr6.sin6_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
*port = ntohs(name.addr4.sin_port);
|
||||
}
|
||||
}
|
||||
|
||||
if (listen(httpd, backlog) < 0)
|
||||
{
|
||||
ERROR("Port %d - listen: %s", *port, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
LOG("%s Listen on port %d", ipv6 ? "IPv6" : "IPv4", *port);
|
||||
return (httpd);
|
||||
}
|
@ -23,26 +23,18 @@ THE SOFTWARE.
|
||||
*/
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <ctype.h>
|
||||
#include <regex.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#ifdef USE_OPENSSL
|
||||
#include <openssl/sha.h>
|
||||
#else
|
||||
#include "sha1.h"
|
||||
#endif
|
||||
#include "base64.h"
|
||||
|
||||
#include <regex.h>
|
||||
#include <syslog.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "dictionary.h"
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define LEFTROTATE(x, c) (((x) << (c)) | ((x) >> (32 - (c))))
|
||||
#define EQU(a,b) (strcmp(a,b) == 0)
|
||||
#define IEQU(a,b) (strcasecmp(a,b) == 0)
|
||||
@ -52,46 +44,41 @@ THE SOFTWARE.
|
||||
#define DIR_SEP "/"
|
||||
#define true 1
|
||||
#define false 0
|
||||
#ifdef DEBUG
|
||||
#define LOG(a,...) printf("%s:%d: " a, __FILE__, \
|
||||
|
||||
#define LOG(a,...) syslog (LOG_NOTICE,"ANTD_LOG@[%s:%d]: " a "\n", __FILE__, \
|
||||
__LINE__, ##__VA_ARGS__)
|
||||
#define ERROR(a,...) syslog (LOG_ERR, "ANTD_ERROR@[%s:%d]: " a "\n", __FILE__, \
|
||||
__LINE__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define LOG(a,...) do{}while(0)
|
||||
#endif
|
||||
// add this to the utils
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
#define BUFFLEN 1024
|
||||
#define HASHSIZE 1024
|
||||
#define DHASHSIZE 50
|
||||
|
||||
#define RPC_TYPE_ARRAY 601//hash("array")
|
||||
#define RPC_TYPE_BASE64 335//hash("base64")
|
||||
#define RPC_TYPE_BOOL 40//hash("boolean")
|
||||
#define RPC_TYPE_DOUBLE 977//hash("double")
|
||||
#define RPC_TYPE_DATE 49//hash("dateTime.iso8601")
|
||||
#define RPC_TYPE_INT 1007//hash("int")
|
||||
#define RPC_TYPE_I4 235//hash("i4")
|
||||
#define RPC_TYPE_STRING 17//hash("string")
|
||||
#define RPC_TYPE_NIL 529//hash("nil")
|
||||
|
||||
typedef struct{
|
||||
const char* type;
|
||||
const char** ext;
|
||||
int bin;
|
||||
const char* ext;
|
||||
} mime_t;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct sockaddr_in6 addr6;
|
||||
struct sockaddr_in addr4;
|
||||
} antd_sockaddr_t;
|
||||
|
||||
dictionary_t mimes_list();
|
||||
void set_mimes_list(dictionary_t);
|
||||
char* __s(const char*,...);
|
||||
void trim(char*,const char);
|
||||
void removeAll(const char* path,int mode);
|
||||
char* __time(time_t time);
|
||||
char* server_time();
|
||||
void timestr(time_t time, char* buf,int len,char* format, int gmt);
|
||||
void server_time(char* , int );
|
||||
char* ext(const char*);
|
||||
char* mime(const char*);
|
||||
int is_bin(const char*);
|
||||
int match_int(const char*);
|
||||
int match_float(const char*);
|
||||
int regex_match(const char*,const char*, int, regmatch_t*);
|
||||
int mkdirp(const char* path,mode_t mode);
|
||||
char *url_decode(const char *str);
|
||||
char *url_encode(const char *str);
|
||||
char from_hex(char ch);
|
||||
@ -106,4 +93,11 @@ int _exist(const char* f);
|
||||
void md5(uint8_t *, size_t , char*);
|
||||
void sha1(const char*, char*);
|
||||
void digest_to_hex(const uint8_t *, char *);
|
||||
void verify_header(char* k);
|
||||
int guard_read(int fd, void* buffer, size_t size);
|
||||
int guard_write(int fd, void* buffer, size_t size);
|
||||
char* ip_from_hostname(const char *hostname);
|
||||
|
||||
int antd_request_socket(const char *ip, int port);
|
||||
int antd_listen(unsigned *port, int ipv6, int backlog);
|
||||
#endif
|
563
lib/ws.c
Normal file
563
lib/ws.c
Normal file
@ -0,0 +1,563 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#ifdef USE_OPENSSL
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "handle.h"
|
||||
|
||||
#include "ws.h"
|
||||
#define CONN_TIME_OUT_S 3
|
||||
#define BITV(v, i) ((v & (1 << i)) >> i)
|
||||
#define MAX_BUFF 1024
|
||||
#define PREFERRED_WS_CIPHERS "HIGH:!aNULL:!kRSA:!SRP:!PSK:!CAMELLIA:!RC4:!MD5:!DSS"
|
||||
#define CLIENT_RQ "GET /%s HTTP/1.1\r\nHost: %s\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n"
|
||||
#define SERVER_WS_KEY "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
|
||||
|
||||
static void ws_gen_mask_key(ws_msg_header_t *header)
|
||||
{
|
||||
int r = rand();
|
||||
header->mask_key[0] = (r >> 24) & 0xFF;
|
||||
header->mask_key[1] = (r >> 16) & 0xFF;
|
||||
header->mask_key[2] = (r >> 8) & 0xFF;
|
||||
header->mask_key[3] = r & 0xFF;
|
||||
}
|
||||
/**
|
||||
* Read a frame header
|
||||
* based on this header, we'll decide
|
||||
* the appropriate handle for frame data
|
||||
*/
|
||||
ws_msg_header_t *ws_read_header(void *client)
|
||||
{
|
||||
|
||||
uint8_t byte = 0;
|
||||
uint8_t bytes[8];
|
||||
ws_msg_header_t *header = (ws_msg_header_t *)malloc(sizeof(*header));
|
||||
|
||||
// get first byte
|
||||
if (antd_recv(client, &byte, sizeof(byte)) <= 0)
|
||||
goto fail;
|
||||
if (BITV(byte, 6) || BITV(byte, 5) || BITV(byte, 4))
|
||||
goto fail; // all RSV bit must be 0
|
||||
|
||||
// printf("FIN: %d, RSV1: %d, RSV2: %d, RSV3:%d, opcode:%d\n", BITV(byte,7), BITV(byte,6), BITV(byte,5), BITV(byte,4),(byte & 0x0F) );
|
||||
// find and opcode
|
||||
header->fin = BITV(byte, 7);
|
||||
header->opcode = (byte & 0x0F);
|
||||
|
||||
// get next byte
|
||||
if (antd_recv(client, &byte, sizeof(byte)) <= 0)
|
||||
goto fail;
|
||||
|
||||
// printf("MASK: %d paylen:%d\n", BITV(byte,7), (byte & 0x7F));
|
||||
// check mask bit, should be 1
|
||||
header->mask = BITV(byte, 7);
|
||||
/*if(!BITV(byte,7))
|
||||
{
|
||||
// close the connection with protocol error
|
||||
ws_close(client, 1002);
|
||||
goto fail;
|
||||
}*/
|
||||
// get the data length of the frame
|
||||
int len = (byte & 0x7F);
|
||||
if (len <= 125)
|
||||
{
|
||||
header->plen = len;
|
||||
}
|
||||
else if (len == 126)
|
||||
{
|
||||
if (antd_recv(client, bytes, 2 * sizeof(uint8_t)) <= 0)
|
||||
goto fail;
|
||||
header->plen = (bytes[0] << 8) + bytes[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// read only last 4 byte
|
||||
if (antd_recv(client, bytes, 8 * sizeof(uint8_t)) <= 0)
|
||||
goto fail;
|
||||
header->plen = (bytes[4] << 24) + (bytes[5] << 16) + (bytes[6] << 8) + bytes[7];
|
||||
}
|
||||
// printf("len: %d\n", header->plen);
|
||||
// last step is to get the maskey
|
||||
if (header->mask)
|
||||
if (antd_recv(client, header->mask_key, 4 * sizeof(uint8_t)) <= 0)
|
||||
goto fail;
|
||||
// printf("key 0: %d key 1: %d key2:%d, key3: %d\n",header->mask_key[0],header->mask_key[1],header->mask_key[2], header->mask_key[3] );
|
||||
|
||||
// check wheather it is a ping or a close message
|
||||
// process it and return NULL
|
||||
// otherwise return the header
|
||||
// return the header
|
||||
switch (header->opcode)
|
||||
{
|
||||
case WS_CLOSE: // client requests to close the connection
|
||||
// send back a close message
|
||||
UNUSED(ws_send_close(client, 1000, header->mask ? 0 : 1));
|
||||
// goto fail;
|
||||
break;
|
||||
|
||||
case WS_PING: // client send a ping
|
||||
// send back a pong message
|
||||
UNUSED(ws_pong(client, header, header->mask ? 0 : 1));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return header;
|
||||
|
||||
fail:
|
||||
free(header);
|
||||
return NULL;
|
||||
}
|
||||
/**
|
||||
* Read data from client
|
||||
* and unmask data using the key
|
||||
*/
|
||||
int ws_read_data(void *client, ws_msg_header_t *header, int len, uint8_t *data)
|
||||
{
|
||||
// if len == -1 ==> read all remaining data to 'data';
|
||||
if (header->plen == 0)
|
||||
return 0;
|
||||
int dlen = (len == -1 || len > (int)header->plen) ? (int)header->plen : len;
|
||||
if ((dlen = antd_recv(client, data, dlen)) <= 0)
|
||||
return -1;
|
||||
header->plen = header->plen - dlen;
|
||||
// unmask received data
|
||||
if (header->mask)
|
||||
for (int i = 0; i < dlen; ++i)
|
||||
data[i] = data[i] ^ header->mask_key[i % 4];
|
||||
data[dlen] = '\0';
|
||||
return dlen;
|
||||
}
|
||||
int _send_header(void *client, ws_msg_header_t header)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
uint8_t bytes[8];
|
||||
for (int i = 0; i < 8; i++)
|
||||
bytes[i] = 0;
|
||||
// first byte |FIN|000|opcode|
|
||||
byte = (header.fin << 7) + header.opcode;
|
||||
// printf("BYTE: %d\n", byte);
|
||||
if (antd_send(client, &byte, 1) != 1)
|
||||
return -1;
|
||||
// second byte, payload length
|
||||
// mask may be 0 or 1
|
||||
// if(header.mask == 1)
|
||||
// printf("Data is masked\n");
|
||||
if (header.plen <= 125)
|
||||
{
|
||||
byte = (header.mask << 7) + header.plen;
|
||||
if (antd_send(client, &byte, 1) != 1)
|
||||
return -1;
|
||||
}
|
||||
else if (header.plen < 65536) // 16 bits
|
||||
{
|
||||
byte = (header.mask << 7) + 126;
|
||||
bytes[0] = (header.plen) >> 8;
|
||||
bytes[1] = (header.plen) & 0x00FF;
|
||||
if (antd_send(client, &byte, 1) != 1)
|
||||
return -1;
|
||||
if (antd_send(client, bytes, 2) != 2)
|
||||
return -1;
|
||||
}
|
||||
else // > 16 bits
|
||||
{
|
||||
byte = (header.mask << 7) + 127;
|
||||
bytes[4] = (header.plen) >> 24;
|
||||
bytes[5] = ((header.plen) >> 16) & 0x00FF;
|
||||
bytes[6] = ((header.plen) >> 8) & 0x00FF;
|
||||
bytes[7] = (header.plen) & 0x00FF;
|
||||
if (antd_send(client, &byte, 1) != 1)
|
||||
return -1;
|
||||
if (antd_send(client, bytes, 8) != 8)
|
||||
return -1;
|
||||
}
|
||||
// send mask key
|
||||
if (header.mask)
|
||||
{
|
||||
if (antd_send(client, header.mask_key, 4) != 4)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* Send a frame to client
|
||||
*/
|
||||
int ws_send_frame(void *client, uint8_t *data, ws_msg_header_t header)
|
||||
{
|
||||
uint8_t *masked;
|
||||
masked = data;
|
||||
int ret;
|
||||
if (header.mask)
|
||||
{
|
||||
ws_gen_mask_key(&header);
|
||||
masked = (uint8_t *)malloc(header.plen);
|
||||
for (int i = 0; i < (int)header.plen; ++i)
|
||||
masked[i] = data[i] ^ header.mask_key[i % 4];
|
||||
}
|
||||
if (_send_header(client, header) != 0)
|
||||
return -1;
|
||||
if (header.opcode == WS_TEXT)
|
||||
ret = antd_send(client, (char *)masked, header.plen);
|
||||
else
|
||||
ret = antd_send(client, (uint8_t *)masked, header.plen);
|
||||
if (masked && header.mask)
|
||||
free(masked);
|
||||
if (ret != (int)header.plen)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* send a text data frame to client
|
||||
*/
|
||||
int ws_send_text(void *client, const char *data, int mask)
|
||||
{
|
||||
ws_msg_header_t header;
|
||||
header.fin = 1;
|
||||
header.opcode = WS_TEXT;
|
||||
header.mask = mask;
|
||||
header.plen = strlen(data);
|
||||
//_send_header(client,header);
|
||||
// send(client, data, header.plen,0);
|
||||
return ws_send_frame(client, (uint8_t *)data, header);
|
||||
}
|
||||
/**
|
||||
* send a single binary data fram to client
|
||||
* not tested yet, but should work
|
||||
*/
|
||||
int ws_send_binary(void *client, uint8_t *data, int l, int mask)
|
||||
{
|
||||
ws_msg_header_t header;
|
||||
header.fin = 1;
|
||||
header.opcode = WS_BIN;
|
||||
header.plen = l;
|
||||
header.mask = mask;
|
||||
return ws_send_frame(client, data, header);
|
||||
//_send_header(client,header);
|
||||
// send(client, data, header.plen,0);
|
||||
}
|
||||
/*
|
||||
* send a file as binary data
|
||||
*/
|
||||
int ws_send_file(void *client, const char *file, int mask)
|
||||
{
|
||||
uint8_t buff[1024];
|
||||
FILE *ptr;
|
||||
ptr = fopen(file, "rb");
|
||||
if (!ptr)
|
||||
{
|
||||
return ws_send_close(client, 1011, mask);
|
||||
}
|
||||
|
||||
ws_msg_header_t header;
|
||||
size_t size;
|
||||
int first_frame = 1;
|
||||
int ret = 0;
|
||||
// ws_send_frame(client,buff,header);
|
||||
header.mask = mask;
|
||||
while (!feof(ptr))
|
||||
{
|
||||
size = fread(buff, 1, 1024, ptr);
|
||||
if (feof(ptr))
|
||||
header.fin = 1;
|
||||
else
|
||||
header.fin = 0;
|
||||
// clear opcode
|
||||
if (first_frame)
|
||||
{
|
||||
header.opcode = WS_BIN;
|
||||
first_frame = 0;
|
||||
}
|
||||
else
|
||||
header.opcode = 0;
|
||||
header.plen = size;
|
||||
// printf("FIN: %d OC:%d\n", header.fin, header.opcode);
|
||||
ret += ws_send_frame(client, buff, header);
|
||||
}
|
||||
fclose(ptr);
|
||||
if (ret != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* Not tested yet
|
||||
* but should work
|
||||
*/
|
||||
int ws_pong(void *client, ws_msg_header_t *oheader, int mask)
|
||||
{
|
||||
ws_msg_header_t pheader;
|
||||
int ret;
|
||||
pheader.fin = 1;
|
||||
pheader.opcode = WS_PONG;
|
||||
pheader.plen = oheader->plen;
|
||||
pheader.mask = mask;
|
||||
uint8_t *data = (uint8_t *)malloc(oheader->plen);
|
||||
if (!data)
|
||||
return -1;
|
||||
|
||||
if (ws_read_data(client, oheader, pheader.plen, data) == -1)
|
||||
{
|
||||
ERROR("Cannot read ping data %d", pheader.plen);
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
ret = ws_send_frame(client, data, pheader);
|
||||
free(data);
|
||||
//_send_header(client, pheader);
|
||||
// send(client, data, len, 0);
|
||||
return ret;
|
||||
}
|
||||
int ws_ping(void *client, const char *echo, int mask)
|
||||
{
|
||||
ws_msg_header_t pheader;
|
||||
pheader.fin = 1;
|
||||
pheader.opcode = WS_PING;
|
||||
pheader.plen = strlen(echo);
|
||||
pheader.mask = mask;
|
||||
return ws_send_frame(client, (uint8_t *)echo, pheader);
|
||||
}
|
||||
/*
|
||||
* Not tested yet, but should work
|
||||
*/
|
||||
int ws_send_close(void *client, unsigned int status, int mask)
|
||||
{
|
||||
// printf("CLOSED\n");
|
||||
ws_msg_header_t header;
|
||||
header.fin = 1;
|
||||
header.opcode = WS_CLOSE;
|
||||
header.plen = 2;
|
||||
header.mask = mask;
|
||||
uint8_t bytes[2];
|
||||
bytes[0] = status >> 8;
|
||||
bytes[1] = status & 0xFF;
|
||||
/*if(mask)
|
||||
{
|
||||
// XOR itself
|
||||
header.mask_key[0] = bytes[0];
|
||||
header.mask_key[1] = bytes[1];
|
||||
bytes[0] = bytes[1] ^ bytes[1];
|
||||
}*/
|
||||
return ws_send_frame(client, bytes, header);
|
||||
//_send_header(client, header);
|
||||
// send(client,bytes,2,0);
|
||||
}
|
||||
|
||||
void ws_client_close(ws_client_t *wsclient)
|
||||
{
|
||||
antd_close(wsclient->antdsock);
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
if (wsclient->ssl_ctx)
|
||||
{
|
||||
SSL_CTX_free(wsclient->ssl_ctx);
|
||||
// DEPRECATED: FIPS_mode_set(0);
|
||||
// DEPRECATED: CONF_modules_unload(1);
|
||||
EVP_cleanup();
|
||||
EVP_PBE_cleanup();
|
||||
// DEPRECATED:ENGINE_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
// DEPRECATED: ERR_remove_state(0);
|
||||
ERR_free_strings();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// this is for the client side, not use for now
|
||||
int ws_client_connect(ws_client_t *wsclient, ws_port_config_t pcnf)
|
||||
{
|
||||
char *ip = ip_from_hostname(wsclient->host);
|
||||
if (ip == NULL)
|
||||
return -1;
|
||||
int sock = antd_request_socket(ip, pcnf.port);
|
||||
if (sock <= 0)
|
||||
{
|
||||
ERROR("Cannot request socket");
|
||||
return -1;
|
||||
}
|
||||
// time out setting
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = CONN_TIME_OUT_S;
|
||||
timeout.tv_usec = 0; // 3 s
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0)
|
||||
ERROR("setsockopt failed:%s", strerror(errno));
|
||||
|
||||
if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0)
|
||||
ERROR("setsockopt failed:%s", strerror(errno));
|
||||
|
||||
// will be free
|
||||
wsclient->antdsock->sock = sock;
|
||||
wsclient->antdsock->z_status = 0;
|
||||
wsclient->antdsock->last_io = time(NULL);
|
||||
wsclient->antdsock->zstream = NULL;
|
||||
#ifdef USE_OPENSSL
|
||||
if (pcnf.usessl)
|
||||
{
|
||||
SSL_library_init();
|
||||
SSL_load_error_strings();
|
||||
ERR_load_crypto_strings();
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
const SSL_METHOD *method;
|
||||
unsigned long ssl_err = 0;
|
||||
method = SSLv23_client_method();
|
||||
ssl_err = ERR_get_error();
|
||||
if (!method)
|
||||
{
|
||||
ERROR("SSLv23_method: %s", ERR_error_string(ssl_err, NULL));
|
||||
return -1;
|
||||
}
|
||||
wsclient->ssl_ctx = SSL_CTX_new(method);
|
||||
ssl_err = ERR_get_error();
|
||||
if (!wsclient->ssl_ctx)
|
||||
{
|
||||
ERROR("SSL_CTX_new: %s", ERR_error_string(ssl_err, NULL));
|
||||
return -1;
|
||||
}
|
||||
// configure the context
|
||||
#if defined(SSL_CTX_set_ecdh_auto)
|
||||
SSL_CTX_set_ecdh_auto(wsclient->ssl_ctx, 1);
|
||||
#else
|
||||
SSL_CTX_set_tmp_ecdh(wsclient->ssl_ctx, EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
|
||||
#endif
|
||||
SSL_CTX_set_options(wsclient->ssl_ctx, SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_SSLv2 | SSL_OP_NO_TICKET);
|
||||
// set the cipher suit
|
||||
const char *suit = wsclient->ciphersuit ? wsclient->ciphersuit : PREFERRED_WS_CIPHERS;
|
||||
// const char* suit = "AES128-SHA";
|
||||
if (SSL_CTX_set_cipher_list(wsclient->ssl_ctx, suit) != 1)
|
||||
{
|
||||
ssl_err = ERR_get_error();
|
||||
// TODO Close the context
|
||||
ERROR("SSL_CTX_set_cipher_list: %s", ERR_error_string(ssl_err, NULL));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wsclient->sslcert && wsclient->sslkey)
|
||||
{
|
||||
if (SSL_CTX_use_certificate_file(wsclient->ssl_ctx, wsclient->sslcert, SSL_FILETYPE_PEM) <= 0)
|
||||
{
|
||||
ssl_err = ERR_get_error();
|
||||
ERROR("SSL_CTX_use_certificate_file: %s", ERR_error_string(ssl_err, NULL));
|
||||
return -1;
|
||||
}
|
||||
if (wsclient->sslpasswd)
|
||||
SSL_CTX_set_default_passwd_cb_userdata(wsclient->ssl_ctx, (void *)wsclient->sslpasswd);
|
||||
if (SSL_CTX_use_PrivateKey_file(wsclient->ssl_ctx, wsclient->sslkey, SSL_FILETYPE_PEM) <= 0)
|
||||
{
|
||||
ssl_err = ERR_get_error();
|
||||
ERROR("SSL_CTX_use_PrivateKey_file: %s", ERR_error_string(ssl_err, NULL));
|
||||
return -1;
|
||||
}
|
||||
if (SSL_CTX_check_private_key(wsclient->ssl_ctx) == 0)
|
||||
{
|
||||
ssl_err = ERR_get_error();
|
||||
ERROR("SSL_CTX_check_private_key: %s", ERR_error_string(ssl_err, NULL));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
//
|
||||
|
||||
// validate
|
||||
if (wsclient->verify_location)
|
||||
{
|
||||
SSL_CTX_set_verify(wsclient->ssl_ctx, SSL_VERIFY_PEER, NULL);
|
||||
SSL_CTX_set_verify_depth(wsclient->ssl_ctx, 5);
|
||||
if (!SSL_CTX_load_verify_locations(wsclient->ssl_ctx, wsclient->verify_location, NULL))
|
||||
{
|
||||
ssl_err = ERR_get_error();
|
||||
// TODO Close the context
|
||||
ERROR("SSL_CTX_load_verify_locations: %s", ERR_error_string(ssl_err, NULL));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SSL_CTX_set_verify(wsclient->ssl_ctx, SSL_VERIFY_NONE, NULL);
|
||||
}
|
||||
|
||||
wsclient->antdsock->ssl = (void *)SSL_new(wsclient->ssl_ctx);
|
||||
if (!wsclient->antdsock->ssl)
|
||||
{
|
||||
ssl_err = ERR_get_error();
|
||||
ERROR("SSL_new: %s", ERR_error_string(ssl_err, NULL));
|
||||
return -1;
|
||||
}
|
||||
SSL_set_fd((SSL *)wsclient->antdsock->ssl, wsclient->antdsock->sock);
|
||||
int stat, ret;
|
||||
ERR_clear_error();
|
||||
while ((ret = SSL_connect(wsclient->antdsock->ssl)) <= 0)
|
||||
{
|
||||
stat = SSL_get_error(wsclient->antdsock->ssl, ret);
|
||||
switch (stat)
|
||||
{
|
||||
case SSL_ERROR_WANT_READ:
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
case SSL_ERROR_NONE:
|
||||
continue;
|
||||
default:
|
||||
ERR_print_errors_fp(stderr);
|
||||
ERROR("Error performing SSL handshake %d", stat);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ws_open_handshake(ws_client_t *client)
|
||||
{
|
||||
char buf[MAX_BUFF];
|
||||
// now send ws request handshake
|
||||
sprintf(buf, CLIENT_RQ, client->resource, client->host);
|
||||
// printf("Send %s\n", buf);
|
||||
int size = antd_send(client->antdsock, buf, strlen(buf));
|
||||
if (size != (int)strlen(buf))
|
||||
{
|
||||
ERROR("Cannot send request \n");
|
||||
return -1;
|
||||
}
|
||||
// now verify if server accept the socket
|
||||
size = read_buf(client->antdsock, buf, MAX_BUFF);
|
||||
char *token;
|
||||
int done = 0;
|
||||
while (size > 0 && strcmp("\r\n", buf))
|
||||
{
|
||||
char *line = buf;
|
||||
token = strsep(&line, ":");
|
||||
trim(token, ' ');
|
||||
if (token != NULL && strcasecmp(token, "Sec-WebSocket-Accept") == 0)
|
||||
{
|
||||
token = strsep(&line, ":");
|
||||
trim(token, ' ');
|
||||
trim(token, '\n');
|
||||
trim(token, '\r');
|
||||
if (strcasecmp(token, SERVER_WS_KEY) == 0)
|
||||
{
|
||||
// LOG("Handshake sucessfull\n");
|
||||
done = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR("WS handshake, Wrong key %s vs %s", token, SERVER_WS_KEY);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// if(line) free(line);
|
||||
size = read_buf(client->antdsock, buf, MAX_BUFF);
|
||||
}
|
||||
if (done)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
65
lib/ws.h
Normal file
65
lib/ws.h
Normal file
@ -0,0 +1,65 @@
|
||||
#ifndef WS_H
|
||||
#define WS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define WS_TEXT 0x1
|
||||
#define WS_BIN 0x2
|
||||
#define WS_CLOSE 0x8
|
||||
#define WS_PING 0x9
|
||||
#define WS_PONG 0xA
|
||||
#define ws_t(c, d) (ws_send_text(c, d, 0))
|
||||
#define ws_b(c, d, z) (ws_send_binary(c, d, z, 0))
|
||||
#define ws_f(c, f) (ws_send_file(c, f, 0))
|
||||
#define ws_close(c, r) (ws_send_close(c, r, 0))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int port;
|
||||
int usessl;
|
||||
int sock;
|
||||
antd_proto_t type;
|
||||
} ws_port_config_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t fin;
|
||||
uint8_t opcode;
|
||||
unsigned int plen;
|
||||
uint8_t mask;
|
||||
uint8_t mask_key[4];
|
||||
} ws_msg_header_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *host;
|
||||
const char *resource;
|
||||
antd_client_t *antdsock;
|
||||
// ssl
|
||||
const char *sslcert;
|
||||
const char *sslkey;
|
||||
const char *sslpasswd;
|
||||
const char *ciphersuit;
|
||||
const char *verify_location;
|
||||
void *ssl_ctx;
|
||||
} ws_client_t;
|
||||
|
||||
ws_msg_header_t *ws_read_header(void *);
|
||||
int ws_send_frame(void *, uint8_t *, ws_msg_header_t);
|
||||
int ws_pong(void *client, ws_msg_header_t *, int mask);
|
||||
|
||||
int ws_ping(void *client, const char *echo, int mask);
|
||||
|
||||
int ws_send_text(void *client, const char *data, int mask);
|
||||
int ws_send_close(void *client, unsigned int status, int mask);
|
||||
int ws_send_file(void *client, const char *file, int mask);
|
||||
int ws_send_binary(void *client, uint8_t *data, int l, int mask);
|
||||
|
||||
int ws_read_data(void *, ws_msg_header_t *, int, uint8_t *);
|
||||
|
||||
// client
|
||||
|
||||
void ws_client_close(ws_client_t *wsclient);
|
||||
int ws_client_connect(ws_client_t *wsclient, ws_port_config_t pcnf);
|
||||
int ws_open_handshake(ws_client_t *client);
|
||||
#endif
|
260
libs/dbhelper.c
260
libs/dbhelper.c
@ -1,260 +0,0 @@
|
||||
#include "dbhelper.h"
|
||||
|
||||
sqlite3 * database(const char* file)
|
||||
{
|
||||
sqlite3* db;
|
||||
int rc = sqlite3_open(file,&db);
|
||||
if (rc != SQLITE_OK) {
|
||||
LOG( "Cannot open database: %s %s\n",file, sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return NULL;
|
||||
}
|
||||
return db;
|
||||
}
|
||||
void dbclose(sqlite3* db)
|
||||
{
|
||||
sqlite3_close(db);
|
||||
}
|
||||
int dbquery(sqlite3* db,const char* sql, int (*call_back)())
|
||||
{
|
||||
char *err_msg = 0;
|
||||
sqlite3_mutex_enter(sqlite3_db_mutex(db));
|
||||
int rc = sqlite3_exec(db,sql,call_back,0,&err_msg);
|
||||
sqlite3_mutex_leave(sqlite3_db_mutex(db));
|
||||
if(rc != SQLITE_OK)
|
||||
{
|
||||
LOG("Cannot query : '%s' [%s]\n", sql,err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
dbfield __field()
|
||||
{
|
||||
dbfield ret = malloc(sizeof *ret);
|
||||
ret->name = NULL;
|
||||
ret->value = NULL;
|
||||
ret->next = NULL;
|
||||
return ret;
|
||||
}
|
||||
dbrecord __record()
|
||||
{
|
||||
dbrecord ret = malloc(sizeof *ret);
|
||||
ret->fields = NULL;
|
||||
ret->idx = 0;
|
||||
ret->next = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
char* __name_list(const dbfield f)
|
||||
{
|
||||
char* sql = f->name;
|
||||
for(dbfield p=f->next;p!=NULL;p=p->next)
|
||||
{
|
||||
sql = __s("%s,%s",sql,p->name);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
char* __value_list(const dbfield f)
|
||||
{
|
||||
char* sql = __s("'%s'",f->value);
|
||||
for(dbfield p=f->next;p!=NULL;p=p->next)
|
||||
{
|
||||
sql = __s("%s,'%s'",sql,p->value);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
char* __name_value_list(const dbfield f)
|
||||
{
|
||||
char* sql = __s("%s='%s'", f->name,f->value);
|
||||
for(dbfield p=f->next;p!=NULL;p=p->next)
|
||||
{
|
||||
sql = __s("%s,%s='%s'",sql, p->name,f->value);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
void add_record(dbrecord* r,dbfield f)
|
||||
{
|
||||
dbrecord new_r = malloc(sizeof *new_r);
|
||||
new_r->fields = f;
|
||||
new_r->idx = 1;
|
||||
new_r->next = NULL;
|
||||
if((*r)->idx == 0)
|
||||
{
|
||||
freerecord(&(*r));
|
||||
*r = new_r;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbrecord* temp;
|
||||
for(temp = r;(*temp)->next!=NULL;temp=&((*temp)->next))
|
||||
{
|
||||
(*temp)->idx++;
|
||||
}
|
||||
(*temp)->next = new_r;
|
||||
}
|
||||
//new_r->next = *r;
|
||||
//*r = new_r;
|
||||
}
|
||||
|
||||
void add_field(dbfield* field,const char* name, const char* value)
|
||||
{
|
||||
dbfield new_field = malloc(sizeof *new_field);
|
||||
new_field->name = strdup(name);
|
||||
new_field->value = strdup(value);
|
||||
new_field->next = *field;
|
||||
*field = new_field;
|
||||
}
|
||||
char* value_of(const dbfield f,const char* key)
|
||||
{
|
||||
for(dbfield t = f; t != NULL; t=t->next)
|
||||
if(strcmp(t->name,key)==0)
|
||||
return t->value;
|
||||
return NULL;
|
||||
}
|
||||
int dbinsert(sqlite3* db,const char* table, const dbfield fields)
|
||||
{
|
||||
char* sqlprefix = "INSERT INTO %s (%s) VALUES (%s)";
|
||||
char* name_list = __name_list(fields);
|
||||
char* value_list = __value_list(fields);
|
||||
char* sql = __s(sqlprefix,table,name_list,value_list);
|
||||
int ret = dbquery(db,sql,NULL);
|
||||
free(name_list);
|
||||
free(value_list);
|
||||
free(sql);
|
||||
|
||||
if(ret == 0)
|
||||
return -1;
|
||||
return sqlite3_last_insert_rowid(db);
|
||||
}
|
||||
dbrecord dball(sqlite3* db,const char* table)
|
||||
{
|
||||
return dbselect(db,table,"1=%s","1");
|
||||
}
|
||||
dbrecord dbselect(sqlite3* db, const char* table, const char* fname,const char* fstring,...)
|
||||
{
|
||||
char* sql;
|
||||
char* prefix = "SELECT %s FROM %s WHERE %s";
|
||||
char* cond;
|
||||
va_list arguments;
|
||||
int dlen;
|
||||
sqlite3_stmt *statement;
|
||||
dbrecord records = __record();
|
||||
|
||||
va_start( arguments, fstring);
|
||||
dlen = vsnprintf(0,0,fstring,arguments) + 1;
|
||||
va_end(arguments);
|
||||
cond = (char*) malloc(dlen*sizeof(char));
|
||||
va_start(arguments, fstring);
|
||||
vsnprintf(cond, dlen, fstring, arguments);
|
||||
va_end(arguments);
|
||||
sql = __s(prefix, fname,table,cond);
|
||||
|
||||
if(sqlite3_prepare_v2(db, sql, -1, &statement, 0) == SQLITE_OK)
|
||||
{
|
||||
int cols = sqlite3_column_count(statement);
|
||||
int result = 0;
|
||||
while((result = sqlite3_step(statement)) == SQLITE_ROW)
|
||||
{
|
||||
dbfield fields = __field();
|
||||
for(int col = 0; col < cols; col++)
|
||||
{
|
||||
const char *value = (const char*)sqlite3_column_text(statement, col);
|
||||
const char *name = sqlite3_column_name(statement, col);
|
||||
add_field(&fields,name,(value!=0)?value:"");
|
||||
}
|
||||
add_record(&records,fields);
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Can not select:%s [%s]\n",sql,sqlite3_errmsg(db));
|
||||
}
|
||||
|
||||
free(cond);
|
||||
free(sql);
|
||||
return records;
|
||||
}
|
||||
int hastable(sqlite3* db,const char* table)
|
||||
{
|
||||
//char * prefix = __s("type='table' and name='%s'",table);
|
||||
dbrecord rc = dbselect(db,"sqlite_master","*","type='table' and name='%s'", table);
|
||||
//free(prefix);
|
||||
if(!rc) return 0;
|
||||
if(!rc->fields)
|
||||
{
|
||||
freerecord(&rc);
|
||||
return 0;
|
||||
}
|
||||
freerecord(&rc);
|
||||
return 1;
|
||||
}
|
||||
int dbupdate(sqlite3* db,const char* table,const dbfield field,const char* fstring,...)
|
||||
{
|
||||
char* sql;
|
||||
char* prefix = "UPDATE %s SET %s WHERE (%s)";
|
||||
char* cond;
|
||||
char* list;
|
||||
va_list arguments;
|
||||
int dlen;
|
||||
va_start( arguments, fstring);
|
||||
dlen = vsnprintf(0,0,fstring,arguments) + 1;
|
||||
va_end(arguments);
|
||||
cond = (char*) malloc(dlen*sizeof(char));
|
||||
va_start(arguments, fstring);
|
||||
vsnprintf(cond, dlen, fstring, arguments);
|
||||
va_end(arguments);
|
||||
|
||||
list = __name_value_list(field);
|
||||
sql = __s(prefix,table,list,cond);
|
||||
int ret = dbquery(db,sql,NULL);
|
||||
free(cond);
|
||||
free(list);
|
||||
free(sql);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dbdelete(sqlite3* db,const char* table,const char* fstring,...)
|
||||
{
|
||||
char* sql;
|
||||
char* prefix = "DELETE FROM %s WHERE (%s)";
|
||||
char* cond;
|
||||
va_list arguments;
|
||||
int dlen;
|
||||
va_start( arguments, fstring);
|
||||
dlen = vsnprintf(0,0,fstring,arguments) + 1;
|
||||
va_end(arguments);
|
||||
cond = (char*) malloc(dlen*sizeof(char));
|
||||
va_start(arguments, fstring);
|
||||
vsnprintf(cond, dlen, fstring, arguments);
|
||||
va_end(arguments);
|
||||
sql = __s(prefix,table,cond);
|
||||
int ret = dbquery(db,sql,NULL);
|
||||
free(cond);
|
||||
free(sql);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void freerecord(dbrecord * r)
|
||||
{
|
||||
dbrecord curr;
|
||||
while ((curr = (*r)) != NULL) {
|
||||
(*r) = (*r)->next;
|
||||
if(curr->fields)
|
||||
freefield(&(curr->fields));
|
||||
free (curr);
|
||||
}
|
||||
}
|
||||
void freefield(dbfield *f)
|
||||
{
|
||||
dbfield curr;
|
||||
while( (curr = (*f)) != NULL )
|
||||
{
|
||||
(*f) = (*f)->next;
|
||||
if(curr->name) free(curr->name);
|
||||
if(curr->value) free(curr->value);
|
||||
free(curr);
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
#ifndef DB_HELPER
|
||||
#define DB_HELPER
|
||||
#include <sqlite3.h>
|
||||
#include "utils.h"
|
||||
|
||||
sqlite3 * database(const char*);
|
||||
typedef struct _dbfield
|
||||
{
|
||||
char* name;
|
||||
char* value;
|
||||
struct _dbfield* next;
|
||||
} *dbfield ;
|
||||
|
||||
typedef struct _dbrecord
|
||||
{
|
||||
dbfield fields;
|
||||
int idx;
|
||||
struct _dbrecord* next;
|
||||
} * dbrecord;
|
||||
|
||||
int dbquery(sqlite3*,const char*, int (*)());
|
||||
int dbinsert(sqlite3*,const char*,const dbfield);
|
||||
int hastable(sqlite3*,const char*);
|
||||
int dbupdate(sqlite3*,const char*,const dbfield,const char*,...);
|
||||
dbrecord dbselect(sqlite3*, const char*, const char*,const char*,...);
|
||||
dbrecord dball(sqlite3*,const char*);
|
||||
int dbdelete(sqlite3*,const char*,const char*,...);
|
||||
dbfield __field();
|
||||
dbrecord __record();
|
||||
char* __name_list(const dbfield);
|
||||
char* __value_list(const dbfield);
|
||||
char* __name_value_list(const dbfield);
|
||||
char* value_of(const dbfield,const char*);
|
||||
void add_field(dbfield*,const char*, const char*);
|
||||
void add_record(dbrecord*,dbfield);
|
||||
void dbclose(sqlite3*);
|
||||
void freerecord(dbrecord *);
|
||||
void freefield(dbfield *);
|
||||
#endif
|
@ -1,128 +0,0 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 LE Xuan Sang xsang.le@gmail.com
|
||||
|
||||
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.
|
||||
*/
|
||||
#include "dictionary.h"
|
||||
|
||||
dictionary dict()
|
||||
{
|
||||
dictionary d = (dictionary)malloc(HASHSIZE*sizeof(association));
|
||||
for(int i=0; i< HASHSIZE;i++)
|
||||
d[i] = NULL;
|
||||
return d;
|
||||
}
|
||||
association dlookup(dictionary dic,const char* key)
|
||||
{
|
||||
association np;
|
||||
if(dic == NULL) return NULL;
|
||||
for (np = dic[hash(key,DHASHSIZE)]; np != NULL; np = np->next)
|
||||
{
|
||||
if(!np || !np->key)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (strcmp(key, np->key) == 0)
|
||||
return np; /* found */
|
||||
}
|
||||
return NULL; /* not found */
|
||||
}
|
||||
association __put_el_with_key(dictionary dic, const char* key)
|
||||
{
|
||||
association np;
|
||||
unsigned hashval;
|
||||
if(dic == NULL) return NULL;
|
||||
if ((np = dlookup(dic,key)) == NULL) { /* not found */
|
||||
np = (association) malloc(sizeof(*np));
|
||||
np->value = NULL;
|
||||
if (np == NULL || (np->key = strdup(key)) == NULL)
|
||||
return NULL;
|
||||
hashval = hash(key, DHASHSIZE);
|
||||
np->next = dic[hashval];
|
||||
dic[hashval] = np;
|
||||
}
|
||||
// found
|
||||
return np;
|
||||
}
|
||||
association dput(dictionary dic,const char* key, void* value)
|
||||
{
|
||||
association np = __put_el_with_key(dic,key);
|
||||
if(np == NULL)
|
||||
{
|
||||
if(value) free(value);
|
||||
return NULL;
|
||||
}
|
||||
if(np->value && value) free(np->value);
|
||||
np->value = value;
|
||||
return np;
|
||||
}
|
||||
int dremove(dictionary dic, const char* key)
|
||||
{
|
||||
if(dic == NULL) return 0;
|
||||
int hashval = hash(key, DHASHSIZE);
|
||||
association np = dic[hashval];
|
||||
if(np!=NULL && strcmp(key,np->key)==0)
|
||||
{
|
||||
dic[hashval] = np->next;
|
||||
return 1;
|
||||
}
|
||||
for (np= dic[hashval]; np != NULL; np = np->next)
|
||||
if (np->next!=NULL&&strcmp(key, np->next->key) == 0)
|
||||
{
|
||||
np->next = np->next->next; /* found */
|
||||
return 1;
|
||||
}
|
||||
return 0; /* not found */
|
||||
|
||||
}
|
||||
void* dvalue(dictionary dic, const char* key)
|
||||
{
|
||||
association as = dlookup(dic,key);
|
||||
if(as == NULL) return NULL;
|
||||
return as->value;
|
||||
}
|
||||
|
||||
void free_association(association * asoc)
|
||||
{
|
||||
|
||||
while( (*asoc) != NULL )
|
||||
{
|
||||
association a = *asoc;
|
||||
(* asoc) = (*asoc) ->next;
|
||||
|
||||
if(a->key)
|
||||
{
|
||||
free(a->key);
|
||||
if(a->value) free(a->value);
|
||||
}
|
||||
free(a);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void freedict(dictionary dic){
|
||||
for(int i = 0; i < HASHSIZE; i++)
|
||||
free_association(&(dic[i]));
|
||||
free(dic);
|
||||
|
||||
}
|
||||
|
||||
|
663
libs/handle.c
663
libs/handle.c
@ -1,663 +0,0 @@
|
||||
#include "handle.h"
|
||||
#ifdef USE_OPENSSL
|
||||
int usessl()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void set_status(void* client,int code,const char* msg)
|
||||
{
|
||||
char *s = __s("HTTP/1.1 %d %s", code, msg);
|
||||
response(client, s);
|
||||
free(s);
|
||||
s = __s("Server: %s ", SERVER_NAME);
|
||||
response(client, s);
|
||||
free(s);
|
||||
}
|
||||
void redirect(void* client,const char*path)
|
||||
{
|
||||
__t(client,"<html><head><meta http-equiv=\"refresh\" content=\"0; url=%s\"></head><body></body></html>",path);
|
||||
}
|
||||
|
||||
void html(void* client)
|
||||
{
|
||||
ctype(client,"text/html; charset=utf-8");
|
||||
}
|
||||
void text(void* client)
|
||||
{
|
||||
ctype(client,"text/plain; charset=utf-8");
|
||||
}
|
||||
void json(void* client)
|
||||
{
|
||||
ctype(client,"application/json");
|
||||
}
|
||||
void textstream(void* client)
|
||||
{
|
||||
ctype(client, "text/event-stream");
|
||||
}
|
||||
void octstream(void* client, char* name)
|
||||
{
|
||||
set_status(client,200,"OK");
|
||||
__t(client,"Content-Type: application/octet-stream");
|
||||
__t(client,"Content-Disposition: attachment; filename=\"%s\"", name);
|
||||
response(client,"");
|
||||
//Content-Disposition: attachment; filename="fname.ext"
|
||||
}
|
||||
void jpeg(void* client)
|
||||
{
|
||||
ctype(client,"image/jpeg");
|
||||
}
|
||||
void ctype(void* client, const char* type)
|
||||
{
|
||||
set_status(client,200,"OK");
|
||||
__t(client,"Content-Type: %s",type);
|
||||
response(client,"");
|
||||
}
|
||||
|
||||
int response(void* client, const char* data)
|
||||
{
|
||||
char buf[BUFFLEN+3];
|
||||
strcpy(buf, data);
|
||||
int nbytes;
|
||||
int size = strlen(data);
|
||||
buf[size] = '\r';
|
||||
buf[size+1] = '\n';
|
||||
buf[size+2] = '\0';
|
||||
|
||||
nbytes = antd_send(client, buf, strlen(buf));
|
||||
return (nbytes ==-1?0:1);
|
||||
}
|
||||
int antd_send(void *src, const void* data, int len)
|
||||
{
|
||||
if(!src || !data) return -1;
|
||||
int written;
|
||||
antd_client_t * source = (antd_client_t *) src;
|
||||
char* ptr;
|
||||
int writelen;
|
||||
int count;
|
||||
#ifdef USE_OPENSSL
|
||||
if(usessl())
|
||||
{
|
||||
//LOG("SSL WRITE\n");
|
||||
//ret = SSL_write((SSL*) source->ssl, data, len);
|
||||
ptr = (char* )data;
|
||||
writelen = len > BUFFLEN?BUFFLEN:len;
|
||||
written = 0;
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
while (writelen > 0) //source->attempt < MAX_ATTEMPT
|
||||
{
|
||||
count = SSL_write (source->ssl, ptr+written, writelen);
|
||||
if (count > 0)
|
||||
{
|
||||
written += count;
|
||||
writelen = (len - written) > BUFFLEN?BUFFLEN:(len-written);
|
||||
}
|
||||
else
|
||||
{
|
||||
//printf(" received equal to or less than 0\n")
|
||||
int err = SSL_get_error(source->ssl, count);
|
||||
switch (err)
|
||||
{
|
||||
case SSL_ERROR_NONE:
|
||||
{
|
||||
// no real error, just try again...
|
||||
//LOG("SSL_ERROR_NONE \n");
|
||||
//source->attempt++;
|
||||
continue;
|
||||
}
|
||||
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
{
|
||||
// peer disconnected...
|
||||
//printf("SSL_ERROR_ZERO_RETURN \n");
|
||||
break;
|
||||
}
|
||||
|
||||
case SSL_ERROR_WANT_READ:
|
||||
{
|
||||
// no data available right now, wait a few seconds in case new data arrives...
|
||||
//printf("SSL_ERROR_WANT_READ\n");
|
||||
|
||||
int sock = SSL_get_rfd(source->ssl);
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 500;
|
||||
err = select(sock+1, &fds, NULL, NULL, &timeout);
|
||||
if (err == 0 || (err > 0 && FD_ISSET(sock, &fds)))
|
||||
{
|
||||
//source->attempt++;
|
||||
continue; // more data to read...
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
{
|
||||
// socket not writable right now, wait a few seconds and try again...
|
||||
//printf("SSL_ERROR_WANT_WRITE \n");
|
||||
int sock = SSL_get_wfd(source->ssl);
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 500;
|
||||
|
||||
err = select(sock+1, NULL, &fds, NULL, &timeout);
|
||||
if (err == 0 || (err > 0 && FD_ISSET(sock, &fds)))
|
||||
{
|
||||
//source->attempt++;
|
||||
continue; // can write more data now...
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// other error
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
//source->attempt = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
ptr = (char* )data;
|
||||
writelen = len > BUFFLEN?BUFFLEN:len;
|
||||
written = 0;
|
||||
while (writelen > 0)
|
||||
{
|
||||
count = send(source->sock, ptr+written, writelen, 0);
|
||||
if (count > 0)
|
||||
{
|
||||
written += count;
|
||||
writelen = (len - written) > BUFFLEN?BUFFLEN:(len-written);
|
||||
}
|
||||
else if(count == -1 && errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
{
|
||||
return written;
|
||||
}
|
||||
}
|
||||
#ifdef USE_OPENSSL
|
||||
}
|
||||
#endif
|
||||
/*if(ret <= 0)
|
||||
{
|
||||
antd_close(src);
|
||||
}*/
|
||||
if(written > 0)
|
||||
time(&source->last_io);
|
||||
return written;
|
||||
}
|
||||
int antd_recv(void *src, void* data, int len)
|
||||
{
|
||||
if(!src) return -1;
|
||||
int read=0;
|
||||
char* ptr = NULL;
|
||||
int received=0;
|
||||
int readlen=0;
|
||||
antd_client_t * source = (antd_client_t *) src;
|
||||
#ifdef USE_OPENSSL
|
||||
if(usessl())
|
||||
{
|
||||
ptr = (char* )data;
|
||||
readlen = len > BUFFLEN?BUFFLEN:len;
|
||||
read = 0;
|
||||
fd_set fds;
|
||||
struct timeval timeout;
|
||||
while (readlen > 0 )//&& source->attempt < MAX_ATTEMPT
|
||||
{
|
||||
received = SSL_read (source->ssl, ptr+read, readlen);
|
||||
if (received > 0)
|
||||
{
|
||||
read += received;
|
||||
readlen = (len - read) > BUFFLEN?BUFFLEN:(len-read);
|
||||
}
|
||||
else
|
||||
{
|
||||
//printf(" received equal to or less than 0\n")
|
||||
int err = SSL_get_error(source->ssl, received);
|
||||
switch (err)
|
||||
{
|
||||
case SSL_ERROR_NONE:
|
||||
{
|
||||
// no real error, just try again...
|
||||
//LOG("SSL_ERROR_NONE \n");
|
||||
//source->attempt++;
|
||||
continue;
|
||||
}
|
||||
|
||||
case SSL_ERROR_ZERO_RETURN:
|
||||
{
|
||||
// peer disconnected...
|
||||
//printf("SSL_ERROR_ZERO_RETURN \n");
|
||||
break;
|
||||
}
|
||||
|
||||
case SSL_ERROR_WANT_READ:
|
||||
{
|
||||
// no data available right now, wait a few seconds in case new data arrives...
|
||||
//printf("SSL_ERROR_WANT_READ\n");
|
||||
|
||||
int sock = SSL_get_rfd(source->ssl);
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 500;
|
||||
err = select(sock+1, &fds, NULL, NULL, &timeout);
|
||||
if (err == 0 || (err > 0 && FD_ISSET(sock, &fds)))
|
||||
{
|
||||
//source->attempt++;
|
||||
continue; // more data to read...
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
{
|
||||
// socket not writable right now, wait a few seconds and try again...
|
||||
//printf("SSL_ERROR_WANT_WRITE \n");
|
||||
int sock = SSL_get_wfd(source->ssl);
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 500;
|
||||
|
||||
err = select(sock+1, NULL, &fds, NULL, &timeout);
|
||||
if (err == 0 || (err > 0 && FD_ISSET(sock, &fds)))
|
||||
{
|
||||
//source->attempt++;
|
||||
continue; // can write more data now...
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
// other error
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
//source->attempt = 0;
|
||||
/*
|
||||
int stat, r, st;
|
||||
do{
|
||||
ret = SSL_read((SSL*) source->ssl, data, len);
|
||||
stat = SSL_get_error((SSL*)source->ssl, r);
|
||||
} while(ret == -1 &&
|
||||
(
|
||||
stat == SSL_ERROR_WANT_READ ||
|
||||
stat == SSL_ERROR_WANT_WRITE ||
|
||||
stat == SSL_ERROR_NONE ||
|
||||
(stat == SSL_ERROR_SYSCALL && r== 0 && !ERR_get_error())
|
||||
));
|
||||
if(ret == -1)
|
||||
{
|
||||
LOG("Problem reading %d %d %d\n", ret, stat, r);
|
||||
}
|
||||
//set_nonblock(source->sock);
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif
|
||||
ptr = (char* )data;
|
||||
readlen = len > BUFFLEN?BUFFLEN:len;
|
||||
read = 0;
|
||||
while (readlen > 0 )//&& source->attempt < MAX_ATTEMPT
|
||||
{
|
||||
received = recv(((int) source->sock), ptr+read, readlen, 0);
|
||||
//LOG("Read : %c\n", *ptr);
|
||||
if (received > 0)
|
||||
{
|
||||
read += received;
|
||||
readlen = (len - read) > BUFFLEN?BUFFLEN:(len-read);
|
||||
//LOG("Read len is %d\n", readlen);
|
||||
}
|
||||
else if(errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
//read = recv(((int) source->sock), data, len, 0);
|
||||
#ifdef USE_OPENSSL
|
||||
}
|
||||
#endif
|
||||
//LOG("Received %d bytes\n", read);
|
||||
/*if(ret == 0)
|
||||
{
|
||||
antd_close(src);
|
||||
}*/
|
||||
if(read > 0)
|
||||
time(&source->last_io);
|
||||
return read;
|
||||
}
|
||||
void set_nonblock(int socket) {
|
||||
int flags;
|
||||
flags = fcntl(socket,F_GETFL,0);
|
||||
//assert(flags != -1);
|
||||
fcntl(socket, F_SETFL, flags | O_NONBLOCK);
|
||||
}
|
||||
/*void set_block()
|
||||
{
|
||||
int flags;
|
||||
flags = fcntl(socket,F_GETFL,0);
|
||||
//assert(flags != -1);
|
||||
fcntl(socket, F_SETFL, flags & (~O_NONBLOCK));
|
||||
}*/
|
||||
int antd_close(void* src)
|
||||
{
|
||||
if(!src) return -1;
|
||||
antd_client_t * source = (antd_client_t *) src;
|
||||
#ifdef USE_OPENSSL
|
||||
if(source->ssl && usessl()){
|
||||
//printf("SSL:Shutdown ssl\n");
|
||||
//SSL_shutdown((SSL*) source->ssl);
|
||||
SSL_set_shutdown((SSL*) source->ssl, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
|
||||
//printf("SSL:Free ssl\n");
|
||||
SSL_free((SSL*) source->ssl);
|
||||
|
||||
//EVP_cleanup();
|
||||
//ENGINE_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
ERR_remove_state(0);
|
||||
ERR_free_strings();
|
||||
source->ssl = NULL;
|
||||
//LOG("Freeing SSL\n");
|
||||
}
|
||||
#endif
|
||||
//printf("Close sock %d\n", source->sock);
|
||||
int ret = close(source->sock);
|
||||
if(source->ip) free(source->ip);
|
||||
free(src);
|
||||
src = NULL;
|
||||
return ret;
|
||||
}
|
||||
int __ti(void* client,int data)
|
||||
{
|
||||
char str[15];
|
||||
sprintf(str, "%d", data);
|
||||
return response(client,str);
|
||||
}
|
||||
|
||||
int __t(void* client, const char* fstring,...)
|
||||
{
|
||||
int nbytes;
|
||||
int dlen;
|
||||
int sent = 0;
|
||||
int buflen = 0;
|
||||
va_list arguments;
|
||||
char * data;
|
||||
char* chunk;
|
||||
va_start( arguments, fstring);
|
||||
dlen = vsnprintf(0,0,fstring,arguments) + 1;
|
||||
va_end(arguments);
|
||||
if ((data = (char*)malloc(dlen*sizeof(char))) != 0)
|
||||
{
|
||||
va_start(arguments, fstring);
|
||||
vsnprintf(data, dlen, fstring, arguments);
|
||||
va_end(arguments);
|
||||
|
||||
if(dlen < BUFFLEN)
|
||||
{
|
||||
int ret = response(client,data);
|
||||
free(data);
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
while(sent < dlen - 1)
|
||||
{
|
||||
if(dlen - sent > BUFFLEN)
|
||||
buflen = BUFFLEN;
|
||||
else
|
||||
buflen = dlen - sent - 1;
|
||||
//LOG("BUFFLEN %d\n",buflen);
|
||||
chunk = (char*) malloc((buflen)*sizeof(char));
|
||||
memcpy(chunk,data+sent,buflen);
|
||||
//chunk[buflen-1] = '\0';
|
||||
//response(client,chunk);
|
||||
sent += buflen;
|
||||
nbytes = antd_send(client, chunk, buflen);
|
||||
free(chunk);
|
||||
if(nbytes == -1)
|
||||
{
|
||||
//free(data);
|
||||
//return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chunk = "\r\n";
|
||||
antd_send(client, chunk, strlen(chunk));
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
return 1;
|
||||
//
|
||||
}
|
||||
int __b(void* client, const unsigned char* data, int size)
|
||||
{
|
||||
char buf[BUFFLEN];
|
||||
int sent = 0;
|
||||
int buflen = 0;
|
||||
int nbytes;
|
||||
|
||||
if(size <= BUFFLEN)
|
||||
{
|
||||
nbytes = antd_send(client,data,size);
|
||||
return (nbytes==-1?0:1);
|
||||
}
|
||||
else
|
||||
{
|
||||
while(sent < size)
|
||||
{
|
||||
if(size - sent > BUFFLEN)
|
||||
buflen = BUFFLEN;
|
||||
else
|
||||
buflen = size - sent;
|
||||
memcpy(buf,data+sent,buflen);
|
||||
nbytes = antd_send(client,buf,buflen);
|
||||
sent += buflen;
|
||||
if(nbytes == -1) return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
int __fb(void* client, const char* file)
|
||||
{
|
||||
printf("Open file %s\n",file );
|
||||
unsigned char buffer[BUFFLEN];
|
||||
FILE *ptr;
|
||||
ptr = fopen(file,"rb");
|
||||
if(!ptr)
|
||||
{
|
||||
LOG("Cannot read : %s\n", file);
|
||||
return 0;
|
||||
}
|
||||
size_t size;
|
||||
while(!feof(ptr))
|
||||
{
|
||||
size = fread(buffer,1,BUFFLEN,ptr);
|
||||
if(!__b(client,buffer,size)) return 0;
|
||||
}
|
||||
fclose(ptr);
|
||||
return 1;
|
||||
}
|
||||
int __f(void* client, const char* file)
|
||||
{
|
||||
char buf[BUFFLEN];
|
||||
FILE *ptr;
|
||||
int nbytes;
|
||||
ptr = fopen(file,"r");
|
||||
if(!ptr)
|
||||
{
|
||||
LOG("Cannot read : %s\n", file);
|
||||
return 0;
|
||||
}
|
||||
while(fgets(buf, sizeof(buf), ptr) != NULL)
|
||||
{
|
||||
nbytes = antd_send(client, buf, strlen(buf));
|
||||
if(nbytes == -1) return 0;
|
||||
//LOG("READ : %s\n", buf);
|
||||
//fgets(buf, sizeof(buf), ptr);
|
||||
}
|
||||
fclose(ptr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int upload(const char* tmp, const char* path)
|
||||
{
|
||||
return !rename(tmp, path);
|
||||
}
|
||||
// __plugin__.name
|
||||
void set_cookie(void* client,const char* type, dictionary dic, const char* name)
|
||||
{
|
||||
set_status(client,200,"OK");
|
||||
__t(client,"Content-Type: %s",type);
|
||||
association assoc;
|
||||
for_each_assoc(assoc,dic){
|
||||
__t(client,"Set-Cookie: %s=%s; Path=/%s",assoc->key, (char*)assoc->value, name);
|
||||
}
|
||||
response(client,"");
|
||||
}
|
||||
void clear_cookie(void* client, dictionary dic)
|
||||
{
|
||||
set_status(client,200,"OK");
|
||||
__t(client,"Content-Type: text/html; charset=utf-8");
|
||||
association assoc;
|
||||
for_each_assoc(assoc,dic){
|
||||
__t(client,"Set-Cookie: %s=%s;expires=",assoc->key, (char*)assoc->value, server_time());
|
||||
}
|
||||
response(client,"");
|
||||
}
|
||||
void unknow(void* client)
|
||||
{
|
||||
set_status(client,520,"Unknown Error");
|
||||
__t(client,"Content-Type: text/html; charset=utf-8");
|
||||
response(client,"");
|
||||
__t(client,"520 Unknow request");
|
||||
}
|
||||
void notfound(void* client)
|
||||
{
|
||||
set_status(client,404,"Not found");
|
||||
__t(client,"Content-Type: text/html; charset=utf-8");
|
||||
response(client,"");
|
||||
__t(client,"Resource not found");
|
||||
}
|
||||
void badrequest(void* client)
|
||||
{
|
||||
set_status(client,400,"Bad request");
|
||||
__t(client,"Content-Type: text/html; charset=utf-8");
|
||||
response(client,"");
|
||||
__t(client,"400 Bad request");
|
||||
}
|
||||
void unimplemented(void* client)
|
||||
{
|
||||
set_status(client,501,"Method Not Implemented");
|
||||
__t(client,"Content-Type: text/html");
|
||||
response(client,"");
|
||||
__t(client, "<HTML><HEAD><TITLE>Method Not Implemented");
|
||||
__t(client, "</TITLE></HEAD>");
|
||||
__t(client, "<BODY><P>HTTP request method not supported.");
|
||||
__t(client, "</BODY></HTML>");
|
||||
}
|
||||
void cannot_execute(void* client)
|
||||
{
|
||||
set_status(client,500,"Internal Server Error");
|
||||
__t(client,"Content-Type: text/html");
|
||||
response(client,"");
|
||||
__t(client, "<P>Error prohibited CGI execution.");
|
||||
}
|
||||
int ws_enable(dictionary dic)
|
||||
{
|
||||
if(!dic) return 0;
|
||||
char*v = (char*)dvalue(dic, "__web_socket__");
|
||||
if(!v) return 0;
|
||||
return atoi(v) == 1;
|
||||
}
|
||||
/**
|
||||
* read the request as a string line format
|
||||
* @param sock socket
|
||||
* @return a request string
|
||||
*/
|
||||
char* read_line(void* sock)
|
||||
{
|
||||
char buf[BUFFLEN];
|
||||
read_buf(sock,buf,sizeof(buf));
|
||||
return strdup(buf);
|
||||
}
|
||||
/**
|
||||
* Read the socket request in to a buffer or size
|
||||
* The data is read until the buffer is full or
|
||||
* there are a carrier return character
|
||||
* @param sock socket
|
||||
* @param buf buffer
|
||||
* @param size size of buffer
|
||||
* @return number of bytes read
|
||||
*/
|
||||
int read_buf(void* sock, char*buf,int size)
|
||||
{
|
||||
int i = 0;
|
||||
char c = '\0';
|
||||
int n;
|
||||
while ((i < size - 1) && (c != '\n'))
|
||||
{
|
||||
n = antd_recv(sock, &c, 1);
|
||||
if (n > 0)
|
||||
{
|
||||
//LOG("Data : %c\n", c);
|
||||
buf[i] = c;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
c = '\n';
|
||||
}
|
||||
buf[i] = '\0';
|
||||
return i;
|
||||
}
|
||||
/*
|
||||
We put it here since we want the plugin is able
|
||||
to destroy the request if it want to
|
||||
in this case, the plugin should return an empty
|
||||
with no data
|
||||
*/
|
||||
void destroy_request(void *data)
|
||||
{
|
||||
if (!data)
|
||||
return;
|
||||
LOG("Close request\n");
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
// free all other thing
|
||||
if (rq->request)
|
||||
{
|
||||
dictionary tmp = dvalue(rq->request, "COOKIE");
|
||||
if (tmp)
|
||||
freedict(tmp);
|
||||
tmp = dvalue(rq->request, "REQUEST_HEADER");
|
||||
if (tmp)
|
||||
freedict(tmp);
|
||||
tmp = dvalue(rq->request, "REQUEST_DATA");
|
||||
if (tmp)
|
||||
freedict(tmp);
|
||||
dput(rq->request, "REQUEST_HEADER", NULL);
|
||||
dput(rq->request, "REQUEST_DATA", NULL);
|
||||
dput(rq->request, "COOKIE", NULL);
|
||||
freedict(rq->request);
|
||||
}
|
||||
antd_close(rq->client);
|
||||
free(rq);
|
||||
}
|
115
libs/handle.h
115
libs/handle.h
@ -1,115 +0,0 @@
|
||||
#ifndef HANDLE_H
|
||||
#define HANDLE_H
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
//open ssl
|
||||
#ifdef USE_OPENSSL
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
#ifdef USE_DB
|
||||
#include "dbhelper.h"
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include "dictionary.h"
|
||||
#include "list.h"
|
||||
#include "ini.h"
|
||||
|
||||
#define SERVER_NAME "Antd"
|
||||
#define IS_POST(method) (strcmp(method,"POST")== 0)
|
||||
#define IS_GET(method) (strcmp(method,"GET")== 0)
|
||||
#define R_STR(d,k) ((char*)dvalue(d,k))
|
||||
#define R_INT(d,k) (atoi(dvalue(d,k)))
|
||||
#define R_FLOAT(d,k) ((double)atof(dvalue(d,k)))
|
||||
#define R_PTR(d,k) (dvalue(d,k))
|
||||
#define __RESULT__ "{\"result\":%d,\"msg\":\"%s\"}"
|
||||
#define FORM_URL_ENCODE "application/x-www-form-urlencoded"
|
||||
#define FORM_MULTI_PART "multipart/form-data"
|
||||
#define MAX_WAIT_S 2 // 1/3 minute
|
||||
#ifdef USE_OPENSSL
|
||||
int __attribute__((weak)) usessl();
|
||||
#endif
|
||||
//extern config_t server_config;
|
||||
typedef struct{
|
||||
int sock;
|
||||
void* ssl;
|
||||
char* ip;
|
||||
#ifdef USE_OPENSSL
|
||||
int status;
|
||||
#endif
|
||||
time_t last_io;
|
||||
} antd_client_t;
|
||||
|
||||
typedef struct {
|
||||
antd_client_t* client;
|
||||
dictionary request;
|
||||
} antd_request_t;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int port;
|
||||
char *plugins_dir;
|
||||
char *plugins_ext;
|
||||
char *db_path;
|
||||
char* htdocs;
|
||||
char* tmpdir;
|
||||
list rules;
|
||||
dictionary handlers;
|
||||
int backlog;
|
||||
int maxcon;
|
||||
int connection;
|
||||
int n_workers;
|
||||
#ifdef USE_OPENSSL
|
||||
int usessl;
|
||||
char* sslcert;
|
||||
char* sslkey;
|
||||
#endif
|
||||
}config_t;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
char *dbpath;
|
||||
char * htdocs;
|
||||
char*pdir;
|
||||
int sport;
|
||||
int raw_body;
|
||||
#ifdef USE_OPENSSL
|
||||
int usessl;
|
||||
#endif
|
||||
} plugin_header_t;
|
||||
|
||||
void set_nonblock(int socket);
|
||||
//void set_block(int socket);
|
||||
int response(void*, const char*);
|
||||
void ctype(void*,const char*);
|
||||
void redirect(void*,const char*);
|
||||
void html(void*);
|
||||
void text(void*);
|
||||
void json(void*);
|
||||
void jpeg(void*);
|
||||
void octstream(void*, char*);
|
||||
void textstream(void*);
|
||||
int __ti(void*,int);
|
||||
int __t(void*, const char*,...);
|
||||
int __b(void*, const unsigned char*, int);
|
||||
int __f(void*, const char*);
|
||||
int __fb(void*, const char*);
|
||||
int upload(const char*, const char*);
|
||||
|
||||
void set_cookie(void*, const char*,dictionary,const char*);
|
||||
void set_status(void*,int,const char*);
|
||||
void clear_cookie(void*, dictionary);
|
||||
/*Default function for plugin*/
|
||||
void unknow(void*);
|
||||
void badrequest(void* client);
|
||||
void unimplemented(void* client);
|
||||
void notfound(void* client);
|
||||
int ws_enable(dictionary);
|
||||
char* read_line(void* sock);
|
||||
int read_buf(void* sock,char* buf,int i);
|
||||
int antd_send( void *source, const void* data, int len);
|
||||
int antd_recv( void *source, void* data, int len);
|
||||
int antd_close(void* source);
|
||||
void destroy_request(void *data);
|
||||
#endif
|
248
libs/list.c
248
libs/list.c
@ -1,248 +0,0 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 LE Xuan Sang xsang.le@gmail.com
|
||||
|
||||
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.
|
||||
*/
|
||||
#include "list.h"
|
||||
|
||||
list list_init()
|
||||
{
|
||||
list ret = (list)malloc(sizeof *ret);
|
||||
ret->type = RPC_TYPE_NIL;
|
||||
ret->next = NULL;
|
||||
return ret;
|
||||
}
|
||||
void list_put(list* l, item it)
|
||||
{
|
||||
if(*l == NULL || (*l)->type == RPC_TYPE_NIL)
|
||||
{
|
||||
free(*l);
|
||||
*l = it;
|
||||
return ;
|
||||
}
|
||||
item np = list_last(*l);
|
||||
np->next = it;
|
||||
}
|
||||
void list_put_special(list* l, const char* str)
|
||||
{
|
||||
item v;
|
||||
if(IS_INT(str))
|
||||
{
|
||||
v = list_item(RPC_TYPE_INT);
|
||||
v->value.i = atoi(str);
|
||||
}
|
||||
else if(IS_FLOAT(str))
|
||||
{
|
||||
v = list_item(RPC_TYPE_DOUBLE);
|
||||
v->value.d = (double)atof(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
v = list_item(RPC_TYPE_STRING);
|
||||
v->value.s = strdup(str);
|
||||
}
|
||||
list_put(l,v);
|
||||
}
|
||||
item list_last(list l)
|
||||
{
|
||||
item p = l;
|
||||
while(p && p->next != NULL)
|
||||
p = p->next;
|
||||
return p;
|
||||
}
|
||||
int list_remove(list l,int idx)
|
||||
{
|
||||
if(l==NULL) return 0;
|
||||
if(idx <0 || idx >= list_size(l)) return 0;
|
||||
if(idx == 0)
|
||||
{
|
||||
l=l->next;
|
||||
return 1;
|
||||
}
|
||||
item np = list_at(l,idx-1);
|
||||
if(np == NULL) return 0;
|
||||
if(np->next == NULL) return 1;
|
||||
np->next = np->next->next;
|
||||
return 1;
|
||||
}
|
||||
int list_size(list l)
|
||||
{
|
||||
if(l == NULL || l->type == RPC_TYPE_NIL) return 0;
|
||||
int i=0;
|
||||
item np = l;
|
||||
while(np)
|
||||
{
|
||||
np = np->next;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
char* as_string(list l)
|
||||
{
|
||||
char* str = "";
|
||||
if(l != NULL && l->type != RPC_TYPE_NIL)
|
||||
{
|
||||
switch(l->type)
|
||||
{
|
||||
case RPC_TYPE_BASE64:
|
||||
str = __s("b64:%s", l->value.b64);
|
||||
break;
|
||||
|
||||
case RPC_TYPE_BOOL:
|
||||
str = __s("bool:%d", l->value.b);
|
||||
break;
|
||||
|
||||
case RPC_TYPE_DOUBLE:
|
||||
str = __s("double:%lf", l->value.d);
|
||||
break;
|
||||
|
||||
case RPC_TYPE_DATE:
|
||||
str = __s("date:%s", l->value.date);
|
||||
break;
|
||||
|
||||
case RPC_TYPE_INT:
|
||||
case RPC_TYPE_I4:
|
||||
str = __s("int:%d", l->value.i);
|
||||
break;
|
||||
|
||||
case RPC_TYPE_STRING:
|
||||
str = __s("string:%s", l->value.s);
|
||||
break;
|
||||
|
||||
case RPC_TYPE_ARRAY:
|
||||
str = __s("[%s]", as_string(l->value.array));
|
||||
break;
|
||||
default:
|
||||
str = "<Unknown>";
|
||||
break;
|
||||
}
|
||||
item np = l->next;
|
||||
if(np)
|
||||
{
|
||||
str = __s("%s,\n%s", str, as_string(np));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
return "[empty]";
|
||||
}
|
||||
item list_at(list l,int idx)
|
||||
{
|
||||
if(l == NULL || idx<0 || idx>= list_size(l))
|
||||
return NULL;
|
||||
int i=0;
|
||||
item np = l;
|
||||
while(np)
|
||||
{
|
||||
if(i==idx)
|
||||
return np;
|
||||
np = np->next;
|
||||
i++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
item list_item(int type)
|
||||
{
|
||||
item ret = (item)malloc(sizeof *ret);
|
||||
ret->type = type;
|
||||
ret->next = NULL;
|
||||
return ret;
|
||||
}
|
||||
list split(const char* str, const char* delim)
|
||||
{
|
||||
if(str == NULL || delim == NULL) return NULL;
|
||||
char* str_cpy = strdup(str);
|
||||
char* org_str = str_cpy;
|
||||
char* token;
|
||||
list l = list_init();
|
||||
while((token = strsep(&str_cpy,delim)))
|
||||
{
|
||||
if(strlen(token) > 0)
|
||||
{
|
||||
list_put_special(&l,token);
|
||||
}
|
||||
}
|
||||
free(org_str);
|
||||
if(l->type== RPC_TYPE_NIL)
|
||||
return NULL;
|
||||
return l;
|
||||
}
|
||||
void list_put_i(list* l,int v)
|
||||
{
|
||||
item it = list_item(RPC_TYPE_INT);
|
||||
it->value.i = v;
|
||||
list_put(l,it);
|
||||
}
|
||||
void list_put_d(list* l,double v)
|
||||
{
|
||||
item it = list_item(RPC_TYPE_DOUBLE);
|
||||
it->value.d = v;
|
||||
list_put(l,it);
|
||||
}
|
||||
void list_put_b(list* l,int v)
|
||||
{
|
||||
item it = list_item(RPC_TYPE_BOOL);
|
||||
it->value.b = v;
|
||||
list_put(l,it);
|
||||
}
|
||||
void list_put_b64(list* l,const char* v)
|
||||
{
|
||||
item it = list_item(RPC_TYPE_BASE64);
|
||||
it->value.b64 = strdup(v);
|
||||
list_put(l,it);
|
||||
}
|
||||
void list_put_date(list* l,const char* v)
|
||||
{
|
||||
item it = list_item(RPC_TYPE_DATE);
|
||||
it->value.date = strdup(v);
|
||||
list_put(l,it);
|
||||
}
|
||||
void list_put_s(list* l,const char* v)
|
||||
{
|
||||
item it = list_item(RPC_TYPE_STRING);
|
||||
it->value.s = strdup(v);
|
||||
list_put(l,it);
|
||||
}
|
||||
void list_put_array(list* l,list v)
|
||||
{
|
||||
item it = list_item(RPC_TYPE_ARRAY);
|
||||
it->value.array = v;
|
||||
list_put(l,it);
|
||||
}
|
||||
void list_free(list *l)
|
||||
{
|
||||
item curr;
|
||||
while ((curr = (*l)) != NULL) {
|
||||
(*l) = (*l)->next;
|
||||
if(curr->type == RPC_TYPE_ARRAY)
|
||||
list_free(&curr->value.array);
|
||||
else if(curr->type == RPC_TYPE_STRING)
|
||||
free(curr->value.s);
|
||||
else if(curr->type == RPC_TYPE_DATE)
|
||||
free(curr->value.date);
|
||||
else if(curr->type == RPC_TYPE_BASE64)
|
||||
free(curr->value.b64);
|
||||
free (curr);
|
||||
}
|
||||
}
|
||||
int list_empty(list l)
|
||||
{
|
||||
return l== NULL || l->type == RPC_TYPE_NIL;
|
||||
}
|
198
libs/mypty.c
198
libs/mypty.c
@ -1,198 +0,0 @@
|
||||
//#define _XOPEN_SOURCE 600
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
//#define __USE_BSD #define _BSD_SOURCE
|
||||
#include <termios.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <string.h>
|
||||
//#include <strings.h>
|
||||
int read_buf(int fd, char*buf,int size)
|
||||
{
|
||||
int i = 0;
|
||||
char c = '\0';
|
||||
int n;
|
||||
while ((i < size - 1) && (c != '\n'))
|
||||
{
|
||||
n = read(fd, &c, 1);
|
||||
if (n > 0)
|
||||
{
|
||||
buf[i] = c;
|
||||
i++;
|
||||
}
|
||||
else if(n == -1) return n;
|
||||
else
|
||||
c = '\n';
|
||||
}
|
||||
buf[i] = '\0';
|
||||
return i;
|
||||
}
|
||||
|
||||
int main(int ac, char *av[])
|
||||
{
|
||||
int fdm, fds;
|
||||
int rc;
|
||||
char input[150];
|
||||
// Check arguments
|
||||
fdm = posix_openpt(O_RDWR);
|
||||
if (fdm < 0)
|
||||
{
|
||||
fprintf(stderr, "Error %d on posix_openpt()\n", errno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = grantpt(fdm);
|
||||
if (rc != 0)
|
||||
{
|
||||
fprintf(stderr, "Error %d on grantpt()\n", errno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = unlockpt(fdm);
|
||||
if (rc != 0)
|
||||
{
|
||||
fprintf(stderr, "Error %d on unlockpt()\n", errno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Open the slave side ot the PTY
|
||||
fds = open(ptsname(fdm), O_RDWR);
|
||||
|
||||
// Create the child process
|
||||
if (fork())
|
||||
{
|
||||
fd_set fd_in;
|
||||
|
||||
// FATHER
|
||||
|
||||
// Close the slave side of the PTY
|
||||
close(fds);
|
||||
|
||||
while (1)
|
||||
{
|
||||
// Wait for data from standard input and master side of PTY
|
||||
FD_ZERO(&fd_in);
|
||||
FD_SET(0, &fd_in);
|
||||
FD_SET(fdm, &fd_in);
|
||||
|
||||
rc = select(fdm + 1, &fd_in, NULL, NULL, NULL);
|
||||
switch(rc)
|
||||
{
|
||||
case -1 : fprintf(stderr, "Error %d on select()\n", errno);
|
||||
exit(1);
|
||||
|
||||
default :
|
||||
{
|
||||
// If data on standard input
|
||||
if (FD_ISSET(0, &fd_in))
|
||||
{
|
||||
rc = read(0, input, sizeof(input));
|
||||
if (rc > 0)
|
||||
{
|
||||
// Send data on the master side of PTY
|
||||
//printf("DATA sent\n");
|
||||
write(fdm, input, rc);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rc < 0)
|
||||
{
|
||||
fprintf(stderr, "Error %d on read standard input\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If data on master side of PTY
|
||||
if (FD_ISSET(fdm, &fd_in))
|
||||
{
|
||||
rc = read(fdm, input, sizeof(input));
|
||||
if (rc > 0)
|
||||
{
|
||||
// Send data on standard output
|
||||
write(1, input, rc);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rc <= 0)
|
||||
{
|
||||
fprintf(stderr, "Error %d on read master PTY\n", errno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // End switch
|
||||
} // End while
|
||||
}
|
||||
else
|
||||
{
|
||||
struct termios slave_orig_term_settings; // Saved terminal settings
|
||||
struct termios new_term_settings; // Current terminal settings
|
||||
|
||||
// CHILD
|
||||
|
||||
// Close the master side of the PTY
|
||||
close(fdm);
|
||||
|
||||
// Save the defaults parameters of the slave side of the PTY
|
||||
rc = tcgetattr(fds, &slave_orig_term_settings);
|
||||
|
||||
// Set RAW mode on slave side of PTY
|
||||
new_term_settings = slave_orig_term_settings;
|
||||
cfmakeraw (&new_term_settings);
|
||||
tcsetattr (fds, TCSANOW, &new_term_settings);
|
||||
|
||||
// The slave side of the PTY becomes the standard input and outputs of the child process
|
||||
close(0); // Close standard input (current terminal)
|
||||
close(1); // Close standard output (current terminal)
|
||||
close(2); // Close standard error (current terminal)
|
||||
|
||||
dup(fds); // PTY becomes standard input (0)
|
||||
dup(fds); // PTY becomes standard output (1)
|
||||
dup(fds); // PTY becomes standard error (2)
|
||||
|
||||
// Now the original file descriptor is useless
|
||||
close(fds);
|
||||
|
||||
// Make the current process a new session leader
|
||||
setsid();
|
||||
|
||||
// As the child is a session leader, set the controlling terminal to be the slave side of the PTY
|
||||
// (Mandatory for programs like the shell to make them manage correctly their outputs)
|
||||
ioctl(0, TIOCSCTTY, 1);
|
||||
|
||||
system("/bin/bash");
|
||||
/*fd_set child_fds;
|
||||
char cinput[150];
|
||||
while(1){
|
||||
//printf("Wait for data\n");
|
||||
FD_ZERO(&child_fds);
|
||||
FD_SET(0, &child_fds);
|
||||
rc = select(1, &child_fds,NULL, NULL, NULL);
|
||||
if(rc == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FD_ISSET(0, &child_fds))
|
||||
{
|
||||
rc = read_buf(0, cinput, sizeof(cinput));
|
||||
if (rc > 0)
|
||||
{
|
||||
system(cinput);
|
||||
} else if(rc < 0) break;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// if Error...
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
// network support
|
||||
#include <netinet/in.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <time.h>
|
||||
#include <resolv.h>
|
||||
|
||||
#include "../plugin.h"
|
||||
#define RQ
|
||||
#define REQUEST_PATTERN "POST /node_register HTTP/1.0\r\nHost: antd\r\nUser-Agent: antd\r\nContent-Type: application/json\r\nContent-Length: %d\r\n\r\n%s"
|
||||
#define JSON_MSG "{\"ip\":\"%s\",\"port\":\"%d\"}"
|
||||
void init();
|
||||
struct master_conf_t{
|
||||
int port;
|
||||
char* ip;
|
||||
} ;
|
||||
|
||||
struct master_conf_t mconfig;
|
||||
|
||||
call __init__ = init;
|
||||
|
||||
/*char* get_ip_address()
|
||||
{
|
||||
struct ifaddrs* addrs;
|
||||
getifaddrs(&addrs);
|
||||
struct ifaddrs* tmp = addrs;
|
||||
char* ip;
|
||||
while (tmp)
|
||||
{
|
||||
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET)
|
||||
{
|
||||
struct sockaddr_in *pAddr = (struct sockaddr_in *)tmp->ifa_addr;
|
||||
ip = inet_ntoa(pAddr->sin_addr);
|
||||
if(strcmp(ip,"127.0.0.1") != 0)
|
||||
return ip;
|
||||
}
|
||||
tmp = tmp->ifa_next;
|
||||
}
|
||||
freeifaddrs(addrs);
|
||||
return "127.0.0.1";
|
||||
}*/
|
||||
int inform_master()
|
||||
{
|
||||
int sockfd;
|
||||
//rpc_response_t* rdata = NULL;
|
||||
char* request;
|
||||
char* data = __s(JSON_MSG,get_ip_address(),__plugin__.sport);
|
||||
while((sockfd = request_socket(mconfig.ip, mconfig.port)) == -1)
|
||||
{
|
||||
// wait for 3s and then request to server
|
||||
usleep(3000000);
|
||||
}
|
||||
request = __s(REQUEST_PATTERN, strlen(data), data);
|
||||
send(sockfd,request, strlen(request),0);
|
||||
//rdata = parse_response(sockfd);
|
||||
close(sockfd);
|
||||
LOG("%s","OK, master registered \n");
|
||||
free(request);
|
||||
return 0;
|
||||
}
|
||||
static int config_handler(void* conf, const char* section, const char* name,
|
||||
const char* value)
|
||||
{
|
||||
struct master_conf_t* pconfig = (struct master_conf_t*)conf;
|
||||
char * ppath = NULL;
|
||||
if (strcmp(name, "port") == 0) {
|
||||
pconfig->port = atoi(value);
|
||||
} else if (strcmp(name, "ip") == 0) {
|
||||
pconfig->ip = strdup(value);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
void read_config()
|
||||
{
|
||||
mconfig.ip = "127.0.0.1";
|
||||
mconfig.port = 8080;
|
||||
char* file = __s("%s%s%s.ini",config_dir(), DIR_SEP, __plugin__.name);
|
||||
if (ini_parse(file, config_handler, &mconfig) < 0) {
|
||||
LOG("Can't load '%s'\n. Used defaut configuration", file);
|
||||
}
|
||||
printf("%s %d\n",mconfig.ip, mconfig.port );
|
||||
}
|
||||
void init()
|
||||
{
|
||||
read_config();
|
||||
pthread_t newthread;
|
||||
if (pthread_create(&newthread , NULL,(void(*)()) inform_master, NULL) != 0)
|
||||
perror("pthread_create: cannot create daemon for finding master");
|
||||
else
|
||||
{
|
||||
//reclaim the stack data when thread finish
|
||||
pthread_detach(newthread) ;
|
||||
}
|
||||
}
|
||||
void pexit()
|
||||
{
|
||||
LOG("%s","EXIT daemon");
|
||||
}
|
||||
void handler(void* c, const char* m, const char* rqp, dictionary d)
|
||||
{
|
||||
text(c);
|
||||
__t(c,"This is a system plugin. It cant be acessed from the web");
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
#include "plugin.h"
|
||||
|
||||
plugin_header_t __plugin__;
|
||||
// private function
|
||||
void __init_plugin__(const char* pl,config_t* conf){
|
||||
__plugin__.name = strdup(pl);
|
||||
__plugin__.dbpath= strdup(conf->db_path);
|
||||
__plugin__.htdocs = strdup(conf->htdocs);
|
||||
__plugin__.pdir = strdup(conf->plugins_dir);
|
||||
__plugin__.sport = conf->port;
|
||||
#ifdef USE_OPENSSL
|
||||
__plugin__.usessl = conf->usessl;
|
||||
#endif
|
||||
__plugin__.raw_body = 0;
|
||||
init();
|
||||
};
|
||||
void use_raw_body()
|
||||
{
|
||||
__plugin__.raw_body = 1;
|
||||
}
|
||||
#ifdef USE_DB
|
||||
sqldb __getdb(char *name)
|
||||
{
|
||||
int plen = strlen(name)+strlen(__plugin__.dbpath)+4;
|
||||
char* path = (char*) malloc(plen*sizeof(char));
|
||||
strcpy(path,__plugin__.dbpath);
|
||||
strcat(path,name);
|
||||
strcat(path,".db");
|
||||
//LOG("database: %s\n", path);
|
||||
sqldb ret = (sqldb)database(path);
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
sqldb getdb()
|
||||
{
|
||||
return __getdb(__plugin__.name);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*#ifdef USE_OPENSSL
|
||||
int usessl()
|
||||
{
|
||||
LOG("CALLED from plugin \n");
|
||||
return __plugin__.usessl;
|
||||
}
|
||||
#endif*/
|
||||
plugin_header_t* meta()
|
||||
{
|
||||
return &__plugin__;
|
||||
}
|
||||
char* route(const char* repath)
|
||||
{
|
||||
int len = strlen(__plugin__.name) + 2;
|
||||
if(repath != NULL)
|
||||
len += strlen(repath)+1;
|
||||
char * path = (char*) malloc(len*sizeof(char));
|
||||
strcpy(path,"/");
|
||||
strcat(path,__plugin__.name);
|
||||
if(repath != NULL)
|
||||
{
|
||||
strcat(path,"/");
|
||||
strcat(path,repath);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
char* htdocs(const char* repath)
|
||||
{
|
||||
if(repath != NULL)
|
||||
return __s("%s/%s",__plugin__.htdocs,repath);
|
||||
else
|
||||
return __s("%s",__plugin__.htdocs);
|
||||
}
|
||||
|
||||
char* config_dir()
|
||||
{
|
||||
struct stat st;
|
||||
char* path = __s("%s%s%s", __plugin__.pdir,DIR_SEP, __plugin__.name);
|
||||
if (stat(path, &st) == -1)
|
||||
mkdir(path, 0755);
|
||||
return path;
|
||||
}
|
||||
|
||||
void __release__()
|
||||
{
|
||||
destroy();
|
||||
LOG("Releasing plugin\n");
|
||||
if(__plugin__.name) free(__plugin__.name);
|
||||
if(__plugin__.dbpath) free(__plugin__.dbpath);
|
||||
if(__plugin__.htdocs) free(__plugin__.htdocs);
|
||||
if(__plugin__.pdir) free(__plugin__.pdir);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#ifndef PLUGIN_H
|
||||
#define PLUGIN_H
|
||||
|
||||
#ifdef USE_DB
|
||||
#include "dbhelper.h"
|
||||
#endif
|
||||
#include "ws.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
|
||||
|
||||
//typedef void(*call)();
|
||||
#ifdef USE_DB
|
||||
typedef sqlite3* sqldb;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef USE_DB
|
||||
sqldb getdb();
|
||||
sqldb __getdb(char *name);
|
||||
#endif
|
||||
|
||||
char* route(const char*);
|
||||
char* htdocs(const char*);
|
||||
char* config_dir();
|
||||
/*Default function for plugin*/
|
||||
// init the plugin
|
||||
void init();
|
||||
void destroy();
|
||||
void* handle(void*);
|
||||
plugin_header_t* meta();
|
||||
void use_raw_body();
|
||||
#endif
|
368
libs/scheduler.c
368
libs/scheduler.c
@ -1,368 +0,0 @@
|
||||
#include "scheduler.h"
|
||||
|
||||
static void enqueue(antd_task_queue_t* q, antd_task_t* task)
|
||||
{
|
||||
antd_task_item_t it = *q;
|
||||
while(it && it->next != NULL)
|
||||
it = it->next;
|
||||
antd_task_item_t taski = (antd_task_item_t)malloc(sizeof *taski);
|
||||
taski->task = task;
|
||||
taski->next = NULL;
|
||||
if(!it) // first task
|
||||
{
|
||||
*q = taski;
|
||||
}
|
||||
else
|
||||
{
|
||||
it->next = taski;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void stop(antd_scheduler_t* scheduler)
|
||||
{
|
||||
scheduler->status = 0;
|
||||
// unlock all idle workers if any
|
||||
for (int i = 0; i < scheduler->n_workers; i++)
|
||||
sem_post(scheduler->worker_sem);
|
||||
sem_post(scheduler->scheduler_sem);
|
||||
for (int i = 0; i < scheduler->n_workers; i++)
|
||||
if(scheduler->workers[i].id != -1)
|
||||
pthread_join(scheduler->workers[i].tid, NULL);
|
||||
if(scheduler->workers) free(scheduler->workers);
|
||||
// destroy all the mutex
|
||||
pthread_mutex_destroy(&scheduler->scheduler_lock);
|
||||
pthread_mutex_destroy(&scheduler->worker_lock);
|
||||
pthread_mutex_destroy(&scheduler->pending_lock);
|
||||
sem_unlink("scheduler");
|
||||
sem_unlink("worker");
|
||||
sem_close(scheduler->scheduler_sem);
|
||||
sem_close(scheduler->worker_sem);
|
||||
}
|
||||
|
||||
static antd_task_item_t dequeue(antd_task_queue_t* q)
|
||||
{
|
||||
antd_task_item_t it = *q;
|
||||
if(it)
|
||||
{
|
||||
*q = it->next;
|
||||
it->next = NULL;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
|
||||
static antd_callback_t* callback_of( void* (*callback)(void*) )
|
||||
{
|
||||
antd_callback_t* cb = NULL;
|
||||
if(callback)
|
||||
{
|
||||
cb = (antd_callback_t*)malloc(sizeof *cb);
|
||||
cb->handle = callback;
|
||||
cb->next = NULL;
|
||||
}
|
||||
return cb;
|
||||
}
|
||||
|
||||
static void free_callback(antd_callback_t* cb)
|
||||
{
|
||||
antd_callback_t* it = cb;
|
||||
antd_callback_t* curr;
|
||||
while(it)
|
||||
{
|
||||
curr = it;
|
||||
it = it->next;
|
||||
free(curr);
|
||||
}
|
||||
}
|
||||
|
||||
static void enqueue_callback(antd_callback_t* cb, antd_callback_t* el)
|
||||
{
|
||||
antd_callback_t* it = cb;
|
||||
while(it && it->next != NULL)
|
||||
it = it->next;
|
||||
if(!it) return; // this should not happend
|
||||
it->next = el;
|
||||
}
|
||||
|
||||
static void execute_callback(antd_scheduler_t* scheduler, antd_task_t* task)
|
||||
{
|
||||
antd_callback_t* cb = task->callback;
|
||||
if(cb)
|
||||
{
|
||||
// call the first come call back
|
||||
task->handle = cb->handle;
|
||||
task->callback = task->callback->next;
|
||||
free(cb);
|
||||
antd_add_task(scheduler, task);
|
||||
}
|
||||
else
|
||||
{
|
||||
free(task);
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_queue(antd_task_queue_t q)
|
||||
{
|
||||
antd_task_item_t it, curr;
|
||||
it = q;
|
||||
while(it)
|
||||
{
|
||||
// first free the task
|
||||
if(it->task && it->task->callback) free_callback(it->task->callback);
|
||||
if(it->task) free(it->task);
|
||||
// then free the placeholder
|
||||
curr = it;
|
||||
it = it->next;
|
||||
free(curr);
|
||||
}
|
||||
}
|
||||
static void work(antd_worker_t* worker)
|
||||
{
|
||||
antd_scheduler_t* scheduler = (antd_scheduler_t*) worker->manager;
|
||||
while(scheduler->status)
|
||||
{
|
||||
antd_task_item_t it;
|
||||
pthread_mutex_lock(&scheduler->worker_lock);
|
||||
it = dequeue(&scheduler->workers_queue);
|
||||
pthread_mutex_unlock(&scheduler->worker_lock);
|
||||
// execute the task
|
||||
//LOG("task executed by worker %d\n", worker->pid);
|
||||
// no task to execute, just sleep wait
|
||||
if(!it)
|
||||
{
|
||||
//LOG("Worker %d goes to idle state\n", worker->id);
|
||||
sem_wait(scheduler->worker_sem);
|
||||
}
|
||||
else
|
||||
{
|
||||
//LOG("task executed by worker %d\n", worker->id);
|
||||
antd_execute_task(scheduler, it);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Main API methods
|
||||
init the main scheduler
|
||||
*/
|
||||
|
||||
void antd_scheduler_init(antd_scheduler_t* scheduler, int n)
|
||||
{
|
||||
scheduler->n_workers = n;
|
||||
scheduler->status = 1;
|
||||
scheduler->workers_queue = NULL;
|
||||
scheduler->pending_task = 0 ;
|
||||
scheduler->validate_data = 0;
|
||||
scheduler->destroy_data = NULL;
|
||||
// init semaphore
|
||||
scheduler->scheduler_sem = sem_open("scheduler", O_CREAT, 0600, 0);
|
||||
if (scheduler->scheduler_sem == SEM_FAILED)
|
||||
{
|
||||
LOG("Cannot open semaphore for scheduler\n");
|
||||
exit(-1);
|
||||
}
|
||||
scheduler->worker_sem = sem_open("worker", O_CREAT, 0600, 0);
|
||||
if (!scheduler->worker_sem)
|
||||
{
|
||||
LOG("Cannot open semaphore for workers\n");
|
||||
exit(-1);
|
||||
}
|
||||
// init lock
|
||||
pthread_mutex_init(&scheduler->scheduler_lock,NULL);
|
||||
pthread_mutex_init(&scheduler->worker_lock, NULL);
|
||||
pthread_mutex_init(&scheduler->pending_lock, NULL);
|
||||
for(int i = 0; i < N_PRIORITY; i++) scheduler->task_queue[i] = NULL;
|
||||
// create scheduler.workers
|
||||
if(n > 0)
|
||||
{
|
||||
scheduler->workers = (antd_worker_t*)malloc(n*(sizeof(antd_worker_t)));
|
||||
if(!scheduler->workers)
|
||||
{
|
||||
LOG("Cannot allocate memory for worker\n");
|
||||
exit(-1);
|
||||
}
|
||||
for(int i = 0; i < scheduler->n_workers;i++)
|
||||
{
|
||||
scheduler->workers[i].id = -1;
|
||||
scheduler->workers[i].manager = (void*)scheduler;
|
||||
if (pthread_create(&scheduler->workers[i].tid, NULL,(void *(*)(void *))work, (void*)&scheduler->workers[i]) != 0)
|
||||
{
|
||||
perror("pthread_create: cannot create worker\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
scheduler->workers[i].id = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG("Antd scheduler initialized with %d worker\n", scheduler->n_workers);
|
||||
}
|
||||
/*
|
||||
destroy all pending task
|
||||
pthread_mutex_lock(&scheduler.queue_lock);
|
||||
*/
|
||||
void antd_scheduler_destroy(antd_scheduler_t* scheduler)
|
||||
{
|
||||
// free all the chains
|
||||
stop(scheduler);
|
||||
LOG("Destroy remaining queue\n");
|
||||
for(int i=0; i < N_PRIORITY; i++)
|
||||
{
|
||||
destroy_queue(scheduler->task_queue[i]);
|
||||
}
|
||||
destroy_queue(scheduler->workers_queue);
|
||||
}
|
||||
|
||||
/*
|
||||
create a task
|
||||
*/
|
||||
antd_task_t* antd_create_task(void* (*handle)(void*), void *data, void* (*callback)(void*), time_t atime)
|
||||
{
|
||||
antd_task_t* task = (antd_task_t*)malloc(sizeof *task);
|
||||
task->stamp = (unsigned long)time(NULL);
|
||||
task->data = data;
|
||||
task->handle = handle;
|
||||
task->callback = callback_of(callback);
|
||||
task->priority = NORMAL_PRIORITY;
|
||||
task->type = LIGHT;
|
||||
task->access_time = atime;
|
||||
return task;
|
||||
}
|
||||
|
||||
/*
|
||||
scheduling a task
|
||||
*/
|
||||
void antd_add_task(antd_scheduler_t* scheduler, antd_task_t* task)
|
||||
{
|
||||
// check if task is exist
|
||||
int prio = task->priority>N_PRIORITY-1?N_PRIORITY-1:task->priority;
|
||||
//LOG("Prio is %d\n", prio);
|
||||
pthread_mutex_lock(&scheduler->scheduler_lock);
|
||||
enqueue(&scheduler->task_queue[prio], task);
|
||||
pthread_mutex_unlock(&scheduler->scheduler_lock);
|
||||
pthread_mutex_lock(&scheduler->pending_lock);
|
||||
scheduler->pending_task++;
|
||||
pthread_mutex_unlock(&scheduler->pending_lock);
|
||||
// wake up the scheduler if idle
|
||||
sem_post(scheduler->scheduler_sem);
|
||||
}
|
||||
|
||||
|
||||
void antd_execute_task(antd_scheduler_t* scheduler, antd_task_item_t taski)
|
||||
{
|
||||
if(!taski)
|
||||
return;
|
||||
// execute the task
|
||||
void *ret = (*(taski->task->handle))(taski->task->data);
|
||||
// check the return data if it is a new task
|
||||
if(!ret)
|
||||
{
|
||||
// call the first callback
|
||||
execute_callback(scheduler, taski->task);
|
||||
free(taski);
|
||||
}
|
||||
else
|
||||
{
|
||||
antd_task_t* rtask = (antd_task_t*) ret;
|
||||
if(taski->task->callback)
|
||||
{
|
||||
if(rtask->callback)
|
||||
{
|
||||
enqueue_callback(rtask->callback, taski->task->callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
rtask->callback = taski->task->callback;
|
||||
}
|
||||
}
|
||||
if(!rtask->handle)
|
||||
{
|
||||
// call the first callback
|
||||
execute_callback(scheduler, rtask);
|
||||
free(taski->task);
|
||||
free(taski);
|
||||
}
|
||||
else
|
||||
{
|
||||
antd_add_task(scheduler, rtask);
|
||||
free(taski->task);
|
||||
free(taski);
|
||||
}
|
||||
}
|
||||
pthread_mutex_lock(&scheduler->pending_lock);
|
||||
scheduler->pending_task--;
|
||||
pthread_mutex_unlock(&scheduler->pending_lock);
|
||||
}
|
||||
|
||||
int antd_scheduler_busy(antd_scheduler_t* scheduler)
|
||||
{
|
||||
return scheduler->pending_task != 0;
|
||||
}
|
||||
|
||||
int antd_task_schedule(antd_scheduler_t* scheduler)
|
||||
{
|
||||
// fetch next task from the task_queue
|
||||
antd_task_item_t it = NULL;
|
||||
pthread_mutex_lock(&scheduler->scheduler_lock);
|
||||
for(int i = 0; i< N_PRIORITY; i++)
|
||||
{
|
||||
|
||||
it = dequeue(&scheduler->task_queue[i]);
|
||||
if(it)
|
||||
break;
|
||||
}
|
||||
pthread_mutex_unlock(&scheduler->scheduler_lock);
|
||||
// no task
|
||||
if(!it)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// has the task now
|
||||
// validate the task
|
||||
if(scheduler->validate_data && difftime( time(NULL), it->task->access_time) > MAX_VALIDITY_INTERVAL)
|
||||
{
|
||||
// data task is not valid
|
||||
LOG("Task data is not valid \n");
|
||||
if(scheduler->destroy_data)
|
||||
scheduler->destroy_data(it->task->data);
|
||||
if(it->task->callback)
|
||||
free_callback(it->task->callback);
|
||||
free(it->task);
|
||||
free(it);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check the type of task
|
||||
if(it->task->type == LIGHT || scheduler->n_workers <= 0)
|
||||
{
|
||||
// do it by myself
|
||||
antd_execute_task( scheduler, it);
|
||||
}
|
||||
else
|
||||
{
|
||||
// delegate to other workers by
|
||||
//pushing to the worker queue
|
||||
pthread_mutex_lock(&scheduler->worker_lock);
|
||||
enqueue(&scheduler->workers_queue, it->task);
|
||||
pthread_mutex_unlock(&scheduler->worker_lock);
|
||||
// wake up idle worker
|
||||
sem_post(scheduler->worker_sem);
|
||||
free(it);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
void antd_wait(antd_scheduler_t* scheduler)
|
||||
{
|
||||
int stat;
|
||||
while(scheduler->status)
|
||||
{
|
||||
stat = antd_task_schedule(scheduler);
|
||||
if(!stat)
|
||||
{
|
||||
// no task found, go to idle state
|
||||
sem_wait(scheduler->scheduler_sem);
|
||||
}
|
||||
}
|
||||
}
|
138
libs/scheduler.h
138
libs/scheduler.h
@ -1,138 +0,0 @@
|
||||
#ifndef ANT_SCHEDULER
|
||||
#define ANT_SCHEDULER
|
||||
|
||||
#include "utils.h"
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <fcntl.h>
|
||||
#define N_PRIORITY 10
|
||||
#define NORMAL_PRIORITY ((int)((N_PRIORITY - 1) / 2))
|
||||
#define LOW_PRIORITY (N_PRIORITY - 1)
|
||||
#define HIGH_PRIORITY 0
|
||||
#define MAX_VALIDITY_INTERVAL 20 // 10 s for task validity
|
||||
typedef enum
|
||||
{
|
||||
LIGHT,
|
||||
HEAVY
|
||||
} antd_task_type_t;
|
||||
// callback definition
|
||||
typedef struct __callback_t
|
||||
{
|
||||
void *(*handle)(void *);
|
||||
struct __callback_t *next;
|
||||
} antd_callback_t;
|
||||
// task definition
|
||||
typedef struct
|
||||
{
|
||||
/*
|
||||
creation time of a task
|
||||
*/
|
||||
unsigned long stamp;
|
||||
/*
|
||||
Last access time of
|
||||
task data
|
||||
*/
|
||||
time_t access_time;
|
||||
/*
|
||||
priority from 0 to N_PRIORITY - 1
|
||||
higher value is lower priority
|
||||
*/
|
||||
uint8_t priority;
|
||||
/*
|
||||
the callback
|
||||
*/
|
||||
void *(*handle)(void *);
|
||||
antd_callback_t *callback;
|
||||
/*
|
||||
user data if any
|
||||
*/
|
||||
void *data;
|
||||
/*
|
||||
type of a task
|
||||
light tasks are executed directly
|
||||
heavy tasks are delegated to workers
|
||||
*/
|
||||
antd_task_type_t type;
|
||||
} antd_task_t;
|
||||
|
||||
typedef struct __task_item_t
|
||||
{
|
||||
antd_task_t *task;
|
||||
struct __task_item_t *next;
|
||||
} * antd_task_item_t;
|
||||
|
||||
typedef antd_task_item_t antd_task_queue_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int id;
|
||||
pthread_t tid;
|
||||
void *manager;
|
||||
} antd_worker_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// data lock
|
||||
pthread_mutex_t scheduler_lock;
|
||||
pthread_mutex_t worker_lock;
|
||||
pthread_mutex_t pending_lock;
|
||||
// event handle
|
||||
sem_t *scheduler_sem;
|
||||
sem_t *worker_sem;
|
||||
// worker and data
|
||||
antd_task_queue_t task_queue[N_PRIORITY];
|
||||
antd_task_queue_t workers_queue;
|
||||
uint8_t status; // 0 stop, 1 working
|
||||
antd_worker_t *workers;
|
||||
int n_workers;
|
||||
int pending_task;
|
||||
/*
|
||||
function pointer that free data in a task if
|
||||
the task is not valid
|
||||
default to NULL
|
||||
*/
|
||||
void* (*destroy_data)(void*);
|
||||
int validate_data;
|
||||
} antd_scheduler_t;
|
||||
|
||||
/*
|
||||
init the main scheduler
|
||||
*/
|
||||
void antd_scheduler_init(antd_scheduler_t *, int);
|
||||
/*
|
||||
destroy all pending task
|
||||
*/
|
||||
void antd_scheduler_destroy(antd_scheduler_t *);
|
||||
|
||||
/*
|
||||
create a task
|
||||
parameter:
|
||||
- handle
|
||||
- data
|
||||
- callback
|
||||
- last data access time
|
||||
*/
|
||||
antd_task_t *antd_create_task(void *(*handle)(void *), void *data, void *(*callback)(void *), time_t);
|
||||
|
||||
/*
|
||||
add a task
|
||||
*/
|
||||
void antd_add_task(antd_scheduler_t *, antd_task_t *);
|
||||
/*
|
||||
execute and free a task a task
|
||||
*/
|
||||
void antd_execute_task(antd_scheduler_t *, antd_task_item_t);
|
||||
/*
|
||||
scheduler status
|
||||
*/
|
||||
int antd_scheduler_busy(antd_scheduler_t *);
|
||||
/*
|
||||
schedule a task
|
||||
*/
|
||||
int antd_task_schedule(antd_scheduler_t *);
|
||||
/*
|
||||
wait for event
|
||||
*/
|
||||
void antd_wait(antd_scheduler_t *);
|
||||
|
||||
#endif
|
@ -1,10 +0,0 @@
|
||||
#include "utils.h"
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
char* v = url_decode("code=3%2B4");
|
||||
if(match_float(argv[1]))
|
||||
printf("It is a float\n");
|
||||
printf("Result is %s\n",v);
|
||||
return 0;
|
||||
}
|
471
libs/utils.c
471
libs/utils.c
@ -1,471 +0,0 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 LE Xuan Sang xsang.le@gmail.com
|
||||
|
||||
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.
|
||||
*/
|
||||
#include "utils.h"
|
||||
|
||||
//define all basic mime here
|
||||
|
||||
static mime_t _mimes[] = {
|
||||
{"image/bmp",(const char *[]){"bmp",NULL},1},
|
||||
{"image/jpeg",(const char *[]){"jpg","jpeg",NULL},1},
|
||||
{"text/css",(const char *[]){"css",NULL},0},
|
||||
{"text/markdown",(const char *[]){"md",NULL},0},
|
||||
{"text/csv",(const char *[]){"csv",NULL},0},
|
||||
{"application/pdf",(const char *[]){"pdf",NULL},1},
|
||||
{"image/gif",(const char *[]){"gif",NULL},1},
|
||||
{"text/html",(const char *[]){"html","htm",NULL},0},
|
||||
{"application/json",(const char *[]){"json",NULL},0},
|
||||
{"application/javascript",(const char *[]){"js",NULL},0},
|
||||
{"image/png",(const char *[]){"png",NULL},1},
|
||||
{"image/x-portable-pixmap",(const char *[]){"ppm",NULL},1},
|
||||
{"application/x-rar-compressed",(const char *[]){"rar",NULL},1},
|
||||
{"image/tiff",(const char *[]){"tiff",NULL},1},
|
||||
{"application/x-tar",(const char *[]){"tar",NULL},1},
|
||||
{"text/plain",(const char *[]){"txt",NULL},0},
|
||||
{"application/x-font-ttf",(const char *[]){"ttf",NULL},1},
|
||||
{"application/xhtml+xml",(const char *[]){"xhtml",NULL},0},
|
||||
{"application/xml",(const char *[]){"xml",NULL},0},
|
||||
{"application/zip",(const char *[]){"zip",NULL},1},
|
||||
{"image/svg+xml",(const char *[]){"svg",NULL},0},
|
||||
{"application/vnd.ms-fontobject",(const char *[]){"eot",NULL},1},
|
||||
{"application/x-font-woff",(const char *[]){"woff","woff2",NULL},1},
|
||||
{"application/x-font-otf",(const char *[]){"otf",NULL},1},
|
||||
{"audio/mpeg",(const char *[]){"mp3","mpeg",NULL},1},
|
||||
{NULL,NULL,0}
|
||||
};
|
||||
|
||||
|
||||
char* __s(const char* fstring,...)
|
||||
{
|
||||
char* data;
|
||||
va_list arguments;
|
||||
int dlen;
|
||||
va_start( arguments, fstring);
|
||||
dlen = vsnprintf(0,0,fstring,arguments) + 1;
|
||||
va_end(arguments);
|
||||
va_end(arguments);
|
||||
if ((data = (char*)malloc(dlen*sizeof(char))) != 0)
|
||||
{
|
||||
va_start(arguments, fstring);
|
||||
vsnprintf(data, dlen, fstring, arguments);
|
||||
va_end(arguments);
|
||||
return data;
|
||||
} else
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Trim a string by a character on both ends
|
||||
* @param str The target string
|
||||
* @param delim the delim
|
||||
*/
|
||||
void trim(char* str, const char delim)
|
||||
{
|
||||
if(!str || strlen(str) == 0) return;
|
||||
char * p = str;
|
||||
int l = strlen(p);
|
||||
while(l > 0 && p[l - 1] == delim)
|
||||
p[--l] = 0;
|
||||
while(* p && (* p) == delim ) ++p, --l;
|
||||
memmove(str, p, l + 1);
|
||||
}
|
||||
|
||||
void removeAll(const char* path,int mode)
|
||||
{
|
||||
DIR *d;
|
||||
struct dirent *dir;
|
||||
char* file;
|
||||
struct stat st;
|
||||
if( stat(path, &st) == 0)
|
||||
{
|
||||
if(S_ISDIR(st.st_mode))
|
||||
{
|
||||
d = opendir(path);
|
||||
if(d)
|
||||
{
|
||||
while ((dir = readdir(d)) != NULL)
|
||||
{
|
||||
if(strcmp(dir->d_name,".") == 0 || strcmp(dir->d_name,"..")==0) continue;
|
||||
file = __s("%s/%s",path,dir->d_name);
|
||||
|
||||
removeAll(file,1);
|
||||
free(file);
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
}
|
||||
if(mode)
|
||||
remove(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
char* __time(time_t time)
|
||||
{
|
||||
struct tm *t = localtime(&time);
|
||||
char * buf = asctime(t);
|
||||
char* pos = strchr(buf,'\n');
|
||||
if(pos) *pos = '\0';
|
||||
return buf;
|
||||
}
|
||||
char* server_time()
|
||||
{
|
||||
return __time(time(NULL));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extension of a file name
|
||||
* @param file The file name
|
||||
* @return the extension
|
||||
*/
|
||||
char* ext(const char* file)
|
||||
{
|
||||
char* token,*ltoken = "";
|
||||
if(file == NULL) return NULL;
|
||||
char* str_cpy = strdup(file);
|
||||
char* str_org = str_cpy;
|
||||
if(!strstr(str_cpy,"."))
|
||||
{
|
||||
free(str_org);
|
||||
return NULL;
|
||||
}
|
||||
if(*file == '.')
|
||||
trim(str_cpy,'.');
|
||||
|
||||
while((token = strsep(&str_cpy,".")) && strlen(token)>0) {ltoken = token;}
|
||||
char* ext = strdup(ltoken);
|
||||
free(str_org);
|
||||
return ext;
|
||||
|
||||
}
|
||||
/*get mime file info from extension*/
|
||||
mime_t mime_from_ext(const char* ex)
|
||||
{
|
||||
for(int i = 0; _mimes[i].type != NULL; i++)
|
||||
for(int j = 0; _mimes[i].ext[j] != NULL; j++)
|
||||
{
|
||||
if(IEQU(ex,_mimes[i].ext[j])) return _mimes[i];
|
||||
}
|
||||
|
||||
return (mime_t){"application/octet-stream",(const char *[]){(char*)ext,NULL},1};
|
||||
}
|
||||
/*get mime file info from type*/
|
||||
mime_t mime_from_type(const char* type)
|
||||
{
|
||||
for(int i = 0; _mimes[i].type != NULL; i++)
|
||||
if(strstr(type, _mimes[i].type) != NULL)
|
||||
return _mimes[i];
|
||||
return (mime_t){NULL,NULL,0};
|
||||
}
|
||||
/**
|
||||
* Get correct HTTP mime type of a file
|
||||
* This is a minimalistic mime type list supported
|
||||
* by the server
|
||||
* @param file File name
|
||||
* @return The HTTP Mime Type
|
||||
*/
|
||||
char* mime(const char* file)
|
||||
{
|
||||
char * ex = ext(file);
|
||||
if(!ex) return "application/octet-stream";
|
||||
mime_t m = mime_from_ext(ex);
|
||||
if(ex)
|
||||
free(ex);
|
||||
return (char*)m.type;
|
||||
}
|
||||
|
||||
int is_bin(const char* file)
|
||||
{
|
||||
char * ex = ext(file);
|
||||
mime_t m = mime_from_ext(ex);
|
||||
if(ex)
|
||||
free(ex);
|
||||
return m.bin;
|
||||
}
|
||||
|
||||
int match_int(const char* search)
|
||||
{
|
||||
return regex_match("^[-+]?[0-9]+$",search,0, NULL);
|
||||
}
|
||||
int match_float(const char* search)
|
||||
{
|
||||
return regex_match("^[+-]?[0-9]*\\.[0-9]+$",search,0,NULL);
|
||||
}
|
||||
/*
|
||||
regmatch_t matches[MAX_MATCHES];
|
||||
if (regexec(&exp, sz, MAX_MATCHES, matches, 0) == 0) {
|
||||
memcpy(buff, sz + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
|
||||
printf("group1: %s\n", buff);
|
||||
}
|
||||
*/
|
||||
int regex_match(const char* expr,const char* search, int msize, regmatch_t* matches)
|
||||
{
|
||||
regex_t regex;
|
||||
int reti;
|
||||
char msgbuf[100];
|
||||
int ret;
|
||||
/* Compile regular expression */
|
||||
reti = regcomp(®ex, expr, REG_ICASE | REG_EXTENDED);
|
||||
if( reti ){
|
||||
LOG("Could not compile regex: %s\n",expr);
|
||||
regerror(reti, ®ex, msgbuf, sizeof(msgbuf));
|
||||
LOG("Regex match failed: %s\n", msgbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Execute regular expression */
|
||||
reti = regexec(®ex, search, msize, matches, 0);
|
||||
if( !reti ){
|
||||
//LOG("Match");
|
||||
ret = 1;
|
||||
}
|
||||
else if( reti == REG_NOMATCH ){
|
||||
//LOG("No match");
|
||||
ret = 0;
|
||||
}
|
||||
else{
|
||||
regerror(reti, ®ex, msgbuf, sizeof(msgbuf));
|
||||
LOG("Regex match failed: %s\n", msgbuf);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
|
||||
regfree(®ex);
|
||||
return ret;
|
||||
}
|
||||
char *url_decode(const char *str) {
|
||||
if(!str)
|
||||
return NULL;
|
||||
char *pstr = (char*)str, *buf = malloc(strlen(str) + 1), *pbuf = buf;
|
||||
|
||||
while (*pstr) {
|
||||
if (*pstr == '%') {
|
||||
if (pstr[1] && pstr[2]) {
|
||||
*pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
|
||||
pstr += 2;
|
||||
}
|
||||
} else if (*pstr == '+') {
|
||||
*pbuf++ = ' ';
|
||||
} else {
|
||||
*pbuf++ = *pstr;
|
||||
}
|
||||
pstr++;
|
||||
}
|
||||
*pbuf = '\0';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
char *url_encode(const char *str) {
|
||||
char *pstr = (char*)str, *buf = malloc(strlen(str) * 3 + 1), *pbuf = buf;
|
||||
while (*pstr) {
|
||||
if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~')
|
||||
*pbuf++ = *pstr;
|
||||
else if (*pstr == ' ')
|
||||
*pbuf++ = '+';
|
||||
else
|
||||
*pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
|
||||
pstr++;
|
||||
}
|
||||
*pbuf = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
char from_hex(char ch) {
|
||||
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
|
||||
}
|
||||
|
||||
char to_hex(char code) {
|
||||
static char hex[] = "0123456789abcdef";
|
||||
return hex[code & 15];
|
||||
}
|
||||
unsigned hash(const char* key, int hash_size)
|
||||
{
|
||||
unsigned hashval = simple_hash(key);
|
||||
return hashval % hash_size;
|
||||
}
|
||||
unsigned simple_hash(const char* key)
|
||||
{
|
||||
unsigned hashval;
|
||||
for (hashval = 0; *key != '\0'; key++)
|
||||
hashval = *key + 31 * hashval;
|
||||
return hashval;
|
||||
}
|
||||
int _exist(const char* f)
|
||||
{
|
||||
struct stat st;
|
||||
return !(stat(f, &st) == -1);
|
||||
}
|
||||
int is_file(const char* f)
|
||||
{
|
||||
int st = is_dir(f);
|
||||
if(st == -1) return -1;
|
||||
else return st==0;
|
||||
}
|
||||
int is_dir(const char* f)
|
||||
{
|
||||
struct stat st;
|
||||
if(stat(f, &st) == -1)
|
||||
return -1; // unknow
|
||||
else if(st.st_mode & S_IFDIR)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
// These vars will contain the hash
|
||||
|
||||
void md5(uint8_t *initial_msg, size_t initial_len, char* buff) {
|
||||
uint32_t h0, h1, h2, h3;
|
||||
char tmp[80];
|
||||
uint8_t *msg = NULL;
|
||||
uint32_t r[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
|
||||
// Use binary integer part of the sines of integers (in radians) as constants// Initialize variables:
|
||||
uint32_t k[] = {
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
||||
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
||||
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
||||
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
|
||||
|
||||
h0 = 0x67452301;
|
||||
h1 = 0xefcdab89;
|
||||
h2 = 0x98badcfe;
|
||||
h3 = 0x10325476;
|
||||
|
||||
// Pre-processing: adding a single 1 bit
|
||||
//append "1" bit to message
|
||||
/* Notice: the input bytes are considered as bits strings,
|
||||
where the first bit is the most significant bit of the byte.[37] */
|
||||
|
||||
// Pre-processing: padding with zeros
|
||||
//append "0" bit until message length in bit ≡ 448 (mod 512)
|
||||
//append length mod (2 pow 64) to message
|
||||
|
||||
int new_len;
|
||||
for(new_len = initial_len*8 + 1; new_len%512!=448; new_len++);
|
||||
new_len /= 8;
|
||||
|
||||
msg = calloc(new_len + 64, 1); // also appends "0" bits
|
||||
// (we alloc also 64 extra bytes...)
|
||||
memcpy(msg, initial_msg, initial_len);
|
||||
msg[initial_len] = 128; // write the "1" bit
|
||||
|
||||
uint32_t bits_len = 8*initial_len; // note, we append the len
|
||||
memcpy(msg + new_len, &bits_len, 4); // in bits at the end of the buffer
|
||||
|
||||
// Process the message in successive 512-bit chunks:
|
||||
//for each 512-bit chunk of message:
|
||||
int offset;
|
||||
for(offset=0; offset<new_len; offset += (512/8)) {
|
||||
|
||||
// break chunk into sixteen 32-bit words w[j], 0 ≤ j ≤ 15
|
||||
uint32_t *w = (uint32_t *) (msg + offset);
|
||||
|
||||
// Initialize hash value for this chunk:
|
||||
uint32_t a = h0;
|
||||
uint32_t b = h1;
|
||||
uint32_t c = h2;
|
||||
uint32_t d = h3;
|
||||
|
||||
// Main loop:
|
||||
uint32_t i;
|
||||
for(i = 0; i<64; i++) {
|
||||
|
||||
uint32_t f, g;
|
||||
|
||||
if (i < 16) {
|
||||
f = (b & c) | ((~b) & d);
|
||||
g = i;
|
||||
} else if (i < 32) {
|
||||
f = (d & b) | ((~d) & c);
|
||||
g = (5*i + 1) % 16;
|
||||
} else if (i < 48) {
|
||||
f = b ^ c ^ d;
|
||||
g = (3*i + 5) % 16;
|
||||
} else {
|
||||
f = c ^ (b | (~d));
|
||||
g = (7*i) % 16;
|
||||
}
|
||||
|
||||
uint32_t temp = d;
|
||||
d = c;
|
||||
c = b;
|
||||
//printf("rotateLeft(%x + %x + %x + %x, %d)\n", a, f, k[i], w[g], r[i]);
|
||||
b = b + LEFTROTATE((a + f + k[i] + w[g]), r[i]);
|
||||
a = temp;
|
||||
|
||||
}
|
||||
|
||||
// Add this chunk's hash to result so far:
|
||||
|
||||
h0 += a;
|
||||
h1 += b;
|
||||
h2 += c;
|
||||
h3 += d;
|
||||
|
||||
}
|
||||
uint8_t *p;
|
||||
p=(uint8_t *)&h0;
|
||||
sprintf(tmp,"%02x%02x%02x%02x", p[0], p[1], p[2], p[3]);//, h0
|
||||
strcpy(buff, tmp);
|
||||
p=(uint8_t *)&h1;
|
||||
sprintf(tmp,"%02x%02x%02x%02x", p[0], p[1], p[2], p[3]); //, h1)
|
||||
strcat(buff,tmp);
|
||||
p=(uint8_t *)&h2;
|
||||
sprintf(tmp,"%02x%02x%02x%02x", p[0], p[1], p[2], p[3]); // , h2
|
||||
strcat(buff,tmp);
|
||||
p=(uint8_t *)&h3;
|
||||
sprintf(tmp,"%02x%02x%02x%02x", p[0], p[1], p[2], p[3]); // , h3
|
||||
strcat(buff,tmp);
|
||||
// cleanup
|
||||
free(msg);
|
||||
|
||||
}
|
||||
void sha1(const char* text, char* out)
|
||||
{
|
||||
uint8_t d [20];
|
||||
#ifdef USE_OPENSSL
|
||||
SHA_CTX context;
|
||||
#else
|
||||
SHA1_CTX context;
|
||||
#endif
|
||||
SHA1_Init(&context);
|
||||
SHA1_Update(&context, text, strlen(text));
|
||||
SHA1_Final(d, &context);
|
||||
digest_to_hex(d,out);
|
||||
}
|
427
libs/ws.c
427
libs/ws.c
@ -1,427 +0,0 @@
|
||||
#include "ws.h"
|
||||
static void ws_gen_mask_key(ws_msg_header_t * header)
|
||||
{
|
||||
int r = rand();
|
||||
header->mask_key[0] = (r >> 24) & 0xFF;
|
||||
header->mask_key[1] = (r >> 16) & 0xFF;
|
||||
header->mask_key[2] = (r >> 8) & 0xFF;
|
||||
header->mask_key[3] = r & 0xFF;
|
||||
}
|
||||
/**
|
||||
* Read a frame header
|
||||
* based on this header, we'll decide
|
||||
* the appropriate handle for frame data
|
||||
*/
|
||||
ws_msg_header_t * ws_read_header(void* client)
|
||||
{
|
||||
|
||||
uint8_t byte = 0;
|
||||
uint8_t bytes[8];
|
||||
ws_msg_header_t* header = (ws_msg_header_t*) malloc(sizeof(*header));
|
||||
|
||||
// get first byte
|
||||
if(antd_recv(client, &byte, sizeof(byte)) <0) goto fail;
|
||||
if(BITV(byte,6) || BITV(byte,5) || BITV(byte,4)) goto fail;// all RSV bit must be 0
|
||||
|
||||
//printf("FIN: %d, RSV1: %d, RSV2: %d, RSV3:%d, opcode:%d\n", BITV(byte,7), BITV(byte,6), BITV(byte,5), BITV(byte,4),(byte & 0x0F) );
|
||||
// find and opcode
|
||||
header->fin = BITV(byte,7);
|
||||
header->opcode = (byte & 0x0F);
|
||||
|
||||
// get next byte
|
||||
if(antd_recv(client, &byte, sizeof(byte)) <0) goto fail;
|
||||
|
||||
//printf("MASK: %d paylen:%d\n", BITV(byte,7), (byte & 0x7F));
|
||||
// check mask bit, should be 1
|
||||
header->mask = BITV(byte,7);
|
||||
/*if(!BITV(byte,7))
|
||||
{
|
||||
// close the connection with protocol error
|
||||
ws_close(client, 1002);
|
||||
goto fail;
|
||||
}*/
|
||||
// get the data length of the frame
|
||||
int len = (byte & 0x7F);
|
||||
if(len <= 125)
|
||||
{
|
||||
header->plen = len;
|
||||
} else if(len == 126)
|
||||
{
|
||||
if(antd_recv(client,bytes, 2*sizeof(uint8_t)) <0) goto fail;
|
||||
header->plen = (bytes[0]<<8) + bytes[1];
|
||||
|
||||
} else
|
||||
{
|
||||
//read only last 4 byte
|
||||
if(antd_recv(client,bytes, 8*sizeof(uint8_t)) <0) goto fail;
|
||||
header->plen = (bytes[4]<<24) + (bytes[5]<<16) + (bytes[6] << 8) + bytes[7] ;
|
||||
}
|
||||
//printf("len: %d\n", header->plen);
|
||||
// last step is to get the maskey
|
||||
if(header->mask)
|
||||
if(antd_recv(client,header->mask_key, 4*sizeof(uint8_t)) <0) goto fail;
|
||||
//printf("key 0: %d key 1: %d key2:%d, key3: %d\n",header->mask_key[0],header->mask_key[1],header->mask_key[2], header->mask_key[3] );
|
||||
|
||||
// check wheather it is a ping or a close message
|
||||
// process it and return NULL
|
||||
//otherwise return the header
|
||||
//return the header
|
||||
switch(header->opcode){
|
||||
case WS_CLOSE: // client requests to close the connection
|
||||
// send back a close message
|
||||
ws_close(client,1000);
|
||||
//goto fail;
|
||||
break;
|
||||
|
||||
case WS_PING: // client send a ping
|
||||
// send back a pong message
|
||||
pong(client,header->plen);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return header;
|
||||
|
||||
fail:
|
||||
free(header);
|
||||
return NULL;
|
||||
|
||||
}
|
||||
/**
|
||||
* Read data from client
|
||||
* and unmask data using the key
|
||||
*/
|
||||
int ws_read_data(void* client, ws_msg_header_t* header, int len, uint8_t* data)
|
||||
{
|
||||
// if len == -1 ==> read all remaining data to 'data';
|
||||
if(header->plen == 0) return 0;
|
||||
int dlen = (len==-1 || len > (int)header->plen)?(int)header->plen:len;
|
||||
if((dlen = antd_recv(client,data, dlen)) <0) return -1;
|
||||
header->plen = header->plen - dlen;
|
||||
// unmask received data
|
||||
if(header->mask)
|
||||
for(int i = 0; i < dlen; ++i)
|
||||
data[i] = data[i]^ header->mask_key[i%4];
|
||||
data[dlen] = '\0';
|
||||
return dlen;
|
||||
}
|
||||
void _send_header(void* client, ws_msg_header_t header)
|
||||
{
|
||||
uint8_t byte = 0;
|
||||
uint8_t bytes[8];
|
||||
for(int i=0; i< 8; i++) bytes[i] = 0;
|
||||
//first byte |FIN|000|opcode|
|
||||
byte = (header.fin << 7) + header.opcode;
|
||||
//printf("BYTE: %d\n", byte);
|
||||
antd_send(client, &byte, 1);
|
||||
// second byte, payload length
|
||||
// mask may be 0 or 1
|
||||
//if(header.mask == 1)
|
||||
// printf("Data is masked\n");
|
||||
if(header.plen <= 125)
|
||||
{
|
||||
byte = (header.mask << 7) + header.plen;
|
||||
antd_send(client, &byte, 1);
|
||||
}
|
||||
else if(header.plen < 65536) // 16 bits
|
||||
{
|
||||
byte = (header.mask << 7) + 126;
|
||||
bytes[0] = (header.plen) >> 8;
|
||||
bytes[1] = (header.plen) & 0x00FF;
|
||||
antd_send(client, &byte, 1);
|
||||
antd_send(client, &bytes, 2);
|
||||
}
|
||||
else // > 16 bits
|
||||
{
|
||||
byte = (header.mask << 7) + 127;
|
||||
bytes[4] = (header.plen) >> 24;
|
||||
bytes[5] = ((header.plen)>>16) & 0x00FF;
|
||||
bytes[6] = ((header.plen)>>8) & 0x00FF;
|
||||
bytes[7] = (header.plen) & 0x00FF;
|
||||
antd_send(client, &byte, 1);
|
||||
antd_send(client, &bytes, 8);
|
||||
}
|
||||
// send mask key
|
||||
if(header.mask)
|
||||
{
|
||||
antd_send(client, header.mask_key,4);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Send a frame to client
|
||||
*/
|
||||
void ws_send_frame(void* client, uint8_t* data, ws_msg_header_t header)
|
||||
{
|
||||
uint8_t * masked;
|
||||
masked = data;
|
||||
if(header.mask)
|
||||
{
|
||||
ws_gen_mask_key(&header);
|
||||
masked = (uint8_t*) malloc(header.plen);
|
||||
for(int i = 0; i < (int)header.plen; ++i)
|
||||
masked[i] = data[i]^ header.mask_key[i%4];
|
||||
}
|
||||
_send_header(client, header);
|
||||
if(header.opcode == WS_TEXT)
|
||||
antd_send(client,(char*)masked,header.plen);
|
||||
else
|
||||
antd_send(client,(uint8_t*)masked,header.plen);
|
||||
if(masked && header.mask)
|
||||
free(masked);
|
||||
}
|
||||
/**
|
||||
* send a text data frame to client
|
||||
*/
|
||||
void ws_send_text(void* client, const char* data,int mask)
|
||||
{
|
||||
ws_msg_header_t header;
|
||||
header.fin = 1;
|
||||
header.opcode = WS_TEXT;
|
||||
header.mask = mask;
|
||||
header.plen = strlen(data);
|
||||
//_send_header(client,header);
|
||||
//send(client, data, header.plen,0);
|
||||
ws_send_frame(client,(uint8_t*)data,header);
|
||||
}
|
||||
/**
|
||||
* send a single binary data fram to client
|
||||
* not tested yet, but should work
|
||||
*/
|
||||
void ws_send_binary(void* client, uint8_t* data, int l, int mask)
|
||||
{
|
||||
ws_msg_header_t header;
|
||||
header.fin = 1;
|
||||
header.opcode = WS_BIN;
|
||||
header.plen = l;
|
||||
header.mask = mask;
|
||||
ws_send_frame(client,data, header);
|
||||
//_send_header(client,header);
|
||||
//send(client, data, header.plen,0);
|
||||
}
|
||||
/*
|
||||
* send a file as binary data
|
||||
*/
|
||||
void ws_send_file(void* client, const char* file, int mask)
|
||||
{
|
||||
uint8_t buff[1024];
|
||||
FILE *ptr;
|
||||
ptr = fopen(file,"rb");
|
||||
if(!ptr)
|
||||
{
|
||||
ws_close(client,1011);
|
||||
return;
|
||||
}
|
||||
|
||||
ws_msg_header_t header;
|
||||
size_t size;
|
||||
int first_frame = 1;
|
||||
//ws_send_frame(client,buff,header);
|
||||
header.mask = mask;
|
||||
while(!feof(ptr))
|
||||
{
|
||||
size = fread(buff,1,1024,ptr);
|
||||
if(feof(ptr))
|
||||
header.fin = 1;
|
||||
else
|
||||
header.fin = 0;
|
||||
// clear opcode
|
||||
if(first_frame)
|
||||
{
|
||||
header.opcode = WS_BIN;
|
||||
first_frame = 0;
|
||||
}
|
||||
else
|
||||
header.opcode = 0;
|
||||
header.plen = size;
|
||||
//printf("FIN: %d OC:%d\n", header.fin, header.opcode);
|
||||
ws_send_frame(client,buff,header);
|
||||
}
|
||||
fclose(ptr);
|
||||
}
|
||||
/**
|
||||
* Not tested yet
|
||||
* but should work
|
||||
*/
|
||||
void pong(void* client, int len)
|
||||
{
|
||||
//printf("PONG\n");
|
||||
ws_msg_header_t pheader;
|
||||
pheader.fin = 1;
|
||||
pheader.opcode = WS_PONG;
|
||||
pheader.plen = len;
|
||||
pheader.mask = 0;
|
||||
uint8_t data[len];
|
||||
if(antd_recv(client,data, len) < 0) return;
|
||||
ws_send_frame(client,data,pheader);
|
||||
//_send_header(client, pheader);
|
||||
//send(client, data, len, 0);
|
||||
}
|
||||
/*
|
||||
* Not tested yet, but should work
|
||||
*/
|
||||
void ws_send_close(void* client, unsigned int status, int mask)
|
||||
{
|
||||
//printf("CLOSED\n");
|
||||
ws_msg_header_t header;
|
||||
header.fin = 1;
|
||||
header.opcode = WS_CLOSE;
|
||||
header.plen = 2;
|
||||
header.mask=mask;
|
||||
uint8_t bytes[2];
|
||||
bytes[0] = status >> 8;
|
||||
bytes[1] = status & 0xFF;
|
||||
/*if(mask)
|
||||
{
|
||||
// XOR itself
|
||||
header.mask_key[0] = bytes[0];
|
||||
header.mask_key[1] = bytes[1];
|
||||
bytes[0] = bytes[1] ^ bytes[1];
|
||||
}*/
|
||||
ws_send_frame(client,bytes,header);
|
||||
//_send_header(client, header);
|
||||
//send(client,bytes,2,0);
|
||||
}
|
||||
int ip_from_hostname(const char * hostname , char* ip)
|
||||
{
|
||||
struct hostent *he;
|
||||
struct in_addr **addr_list;
|
||||
int i;
|
||||
if ( (he = gethostbyname( hostname ) ) == NULL)
|
||||
{
|
||||
// get the host info
|
||||
herror("gethostbyname");
|
||||
return -1;
|
||||
}
|
||||
addr_list = (struct in_addr **) he->h_addr_list;
|
||||
|
||||
for(i = 0; addr_list[i] != NULL; i++)
|
||||
{
|
||||
//Return the first one;
|
||||
strcpy(ip , inet_ntoa(*addr_list[i]) );
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
send a request
|
||||
*/
|
||||
int request_socket(const char* ip, int port)
|
||||
{
|
||||
int sockfd;
|
||||
struct sockaddr_in dest;
|
||||
|
||||
// time out setting
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = CONN_TIME_OUT_S;
|
||||
timeout.tv_usec = 0;//3 s
|
||||
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
|
||||
{
|
||||
perror("Socket");
|
||||
return -1;
|
||||
}
|
||||
if (setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout)) < 0)
|
||||
perror("setsockopt failed\n");
|
||||
|
||||
if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,sizeof(timeout)) < 0)
|
||||
perror("setsockopt failed\n");
|
||||
/*struct linger lingerStruct;
|
||||
lingerStruct.l_onoff = 0; // turn lingering off for sockets
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &lingerStruct, sizeof(lingerStruct));*/
|
||||
|
||||
bzero(&dest, sizeof(dest));
|
||||
dest.sin_family = AF_INET;
|
||||
dest.sin_port = htons(port);
|
||||
if ( inet_aton(ip, &dest.sin_addr) == 0 )
|
||||
{
|
||||
perror(ip);
|
||||
close(sockfd);
|
||||
return -1;
|
||||
}
|
||||
if ( connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0 )
|
||||
{
|
||||
close(sockfd);
|
||||
perror("Connect");
|
||||
return -1;
|
||||
}
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
//TODO: The ping request
|
||||
/*
|
||||
this is for the client side, not use for now
|
||||
int ws_open_hand_shake(const char* host, int port, const char* resource)
|
||||
{
|
||||
char ip[100];
|
||||
char buff[MAX_BUFF];
|
||||
char* rq = NULL;
|
||||
int size;
|
||||
// request socket
|
||||
ip_from_hostname(host ,ip);
|
||||
int sock = request_socket(ip, port);
|
||||
if(sock <= 0) return -1;
|
||||
// now send ws request handshake
|
||||
rq = __s(CLIENT_RQ,resource,host);
|
||||
// printf("%s\n",rq);
|
||||
size = send(sock, rq, strlen(rq),0);
|
||||
if(size != strlen(rq))
|
||||
{
|
||||
printf("Cannot send request \n");
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
// now verify if server accept the socket
|
||||
size = read_buf(sock,buff,MAX_BUFF);
|
||||
char* token;
|
||||
int done = 0;
|
||||
while (size > 0 && strcmp("\r\n",buff))
|
||||
{
|
||||
char* line = strdup(buff);
|
||||
//printf("LINE %s\n", line);
|
||||
token = strsep(&line,":");
|
||||
trim(token,' ');
|
||||
if(token != NULL &&strcasecmp(token,"Sec-WebSocket-Accept") == 0)
|
||||
{
|
||||
token = strsep(&line,":");
|
||||
trim(token,' ');
|
||||
trim(token,'\n');
|
||||
trim(token,'\r');
|
||||
//printf("Key found %s \n", token);
|
||||
if(strcasecmp(token, SERVER_WS_KEY) == 0)
|
||||
{
|
||||
// printf("Handshake sucessfull\n");
|
||||
done = 1;
|
||||
} else
|
||||
{
|
||||
printf("Wrong key %s vs %s\n",token,SERVER_WS_KEY);
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
//if(line) free(line);
|
||||
size = read_buf(sock,buff,MAX_BUFF);
|
||||
}
|
||||
if(done) return sock;
|
||||
//printf("No server key found \n");
|
||||
return -1;
|
||||
}*/
|
||||
char* get_ip_address()
|
||||
{
|
||||
struct ifaddrs* addrs;
|
||||
getifaddrs(&addrs);
|
||||
struct ifaddrs* tmp = addrs;
|
||||
char* ip;
|
||||
while (tmp)
|
||||
{
|
||||
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET)
|
||||
{
|
||||
struct sockaddr_in *pAddr = (struct sockaddr_in *)tmp->ifa_addr;
|
||||
ip = inet_ntoa(pAddr->sin_addr);
|
||||
if(strcmp(ip,"127.0.0.1") != 0)
|
||||
return ip;
|
||||
}
|
||||
tmp = tmp->ifa_next;
|
||||
}
|
||||
freeifaddrs(addrs);
|
||||
return "127.0.0.1";
|
||||
}
|
55
libs/ws.h
55
libs/ws.h
@ -1,55 +0,0 @@
|
||||
#ifndef WS_H
|
||||
#define WS_H
|
||||
#include <resolv.h>
|
||||
#include <errno.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include<netdb.h> //hostent
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <libgen.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "handle.h"
|
||||
#define CONN_TIME_OUT_S 3
|
||||
#define BITV(v,i) ((v & (1 << i)) >> i)
|
||||
#define WS_TEXT 0x1
|
||||
#define WS_BIN 0x2
|
||||
#define WS_CLOSE 0x8
|
||||
#define WS_PING 0x9
|
||||
#define WS_PONG 0xA
|
||||
#define ws_t(c ,d) (ws_send_text(c,d,0))
|
||||
#define ws_b(c , d,z) (ws_send_binary(c,d,z,0))
|
||||
#define ws_f(c,f) (ws_send_file(c,f,0))
|
||||
#define ws_close(c,r) (ws_send_close(c,r,0))
|
||||
#define MAX_BUFF 1024
|
||||
#define CLIENT_RQ "GET /%s HTTP/1.1\r\nHost: %s\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n"
|
||||
#define SERVER_WS_KEY "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
|
||||
|
||||
typedef struct{
|
||||
uint8_t fin;
|
||||
uint8_t opcode;
|
||||
unsigned int plen;
|
||||
uint8_t mask;
|
||||
uint8_t mask_key[4];
|
||||
} ws_msg_header_t;
|
||||
|
||||
ws_msg_header_t * ws_read_header(void*);
|
||||
void ws_send_frame(void* , uint8_t* , ws_msg_header_t );
|
||||
void pong(void* client, int len);
|
||||
|
||||
void ws_send_text(void* client, const char* data,int mask);
|
||||
void ws_send_close(void* client, unsigned int status, int mask);
|
||||
void ws_send_file(void* client, const char* file, int mask);
|
||||
void ws_send_binary(void* client, uint8_t* data, int l, int mask);
|
||||
|
||||
int ws_read_data(void* , ws_msg_header_t*, int, uint8_t*);
|
||||
int request_socket(const char* ip, int port);
|
||||
int ip_from_hostname(const char * hostname , char* ip);
|
||||
int sock_read_buf(void* sock, char*buf,int size);
|
||||
//int ws_open_hand_shake(const char* host, int port, const char* resource);
|
||||
char* get_ip_address();
|
||||
#endif
|
368
plugin_manager.c
368
plugin_manager.c
@ -1,146 +1,304 @@
|
||||
#include <dlfcn.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include "lib/utils.h"
|
||||
#include "lib/handle.h"
|
||||
#include "config.h"
|
||||
#include "lib/plugin_ctx.h"
|
||||
#include "plugin_manager.h"
|
||||
|
||||
struct plugin_entry {
|
||||
struct plugin_entry *next;
|
||||
char *name;
|
||||
void *handle;
|
||||
dictionary_t instances;
|
||||
};
|
||||
|
||||
extern config_t g_server_config;
|
||||
|
||||
/**
|
||||
* Plugin table to store the loaded plugin
|
||||
*/
|
||||
static struct plugin_entry *plugin_table[HASHSIZE];
|
||||
static struct plugin_entry *plugin_table[HASHSIZE];
|
||||
|
||||
/**
|
||||
* Locate a plugin in the plugin table
|
||||
* @param s plugin name
|
||||
* @return a plugin entry in the plugin table
|
||||
*/
|
||||
struct plugin_entry *plugin_lookup(char *s)
|
||||
static struct plugin_entry *plugin_lookup(const char *s)
|
||||
{
|
||||
if(!s)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
struct plugin_entry *np;
|
||||
for (np = plugin_table[hash(s, HASHSIZE)]; np != NULL; np = np->next)
|
||||
if (strcmp(s, np->pname) == 0)
|
||||
return np; /* found */
|
||||
return NULL; /* not found */
|
||||
if (strcmp(s, np->name) == 0)
|
||||
return np; /* found */
|
||||
return NULL; /* not found */
|
||||
}
|
||||
|
||||
static void antd_plugin_ctx_drop(struct plugin_entry* np, antd_plugin_ctx_t* ctx)
|
||||
{
|
||||
if(!ctx)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(ctx->drop)
|
||||
{
|
||||
if(ctx->name)
|
||||
LOG("Release user resource for context: %s", ctx->name);
|
||||
ctx->drop((void*)ctx);
|
||||
}
|
||||
if(ctx->name)
|
||||
{
|
||||
LOG("Dropping plugin context: %s", ctx->name);
|
||||
if(np->instances)
|
||||
{
|
||||
dput(np->instances, ctx->name, NULL);
|
||||
}
|
||||
free(ctx->name);
|
||||
}
|
||||
if(ctx->confdir)
|
||||
{
|
||||
free(ctx->confdir);
|
||||
}
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
|
||||
static antd_plugin_ctx_t* antd_plugin_ctx_lookup(struct plugin_entry* np, const char* name)
|
||||
{
|
||||
if(!np || !np->instances)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
LOG("Looking for plugin context: %s", name);
|
||||
antd_plugin_ctx_t* ctx = dvalue(np->instances, name);
|
||||
if(ctx == NULL)
|
||||
{
|
||||
char *error;
|
||||
LOG("Create new plugin context: %s", name);
|
||||
ctx = (antd_plugin_ctx_t *)malloc(sizeof(antd_plugin_ctx_t));
|
||||
if(!ctx)
|
||||
{
|
||||
ERROR("Unable to allocate memory for plugin context `%s`: %s", name, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
// init the context
|
||||
ctx->basedir = g_server_config.plugins_dir;
|
||||
ctx->tmpdir = g_server_config.tmpdir;
|
||||
ctx->name = strdup(name);
|
||||
ctx->confdir = NULL;
|
||||
ctx->raw_body = 0;
|
||||
ctx->status = ANTD_PLUGIN_INIT;
|
||||
ctx->config=dvalue(g_server_config.plugins, name);
|
||||
ctx->data = NULL;
|
||||
ctx->handle = NULL;
|
||||
ctx->create = NULL;
|
||||
ctx->drop = NULL;
|
||||
// look for handle function
|
||||
ctx->handle = (void* (*)(void *))dlsym(np->handle, PLUGIN_HANDLE);
|
||||
if ((error = dlerror()) != NULL)
|
||||
{
|
||||
ERROR("Problem when finding plugin handle function for %s : %s", name, error);
|
||||
ctx->handle = NULL;
|
||||
antd_plugin_ctx_drop(np, ctx);
|
||||
return NULL;
|
||||
}
|
||||
// look for drop function
|
||||
ctx->drop = (void (*)(antd_plugin_ctx_t *))dlsym(np->handle, PLUGIN_DROP);
|
||||
if ((error = dlerror()) != NULL)
|
||||
{
|
||||
ERROR("Problem when finding plugin drop function for %s : %s", name, error);
|
||||
ctx->drop = NULL;
|
||||
antd_plugin_ctx_drop(np, ctx);
|
||||
return NULL;
|
||||
}
|
||||
// look for init function
|
||||
ctx->create = (void* (*)(antd_plugin_ctx_t *))dlsym(np->handle, PLUGIN_INIT);
|
||||
if ((error = dlerror()) != NULL)
|
||||
{
|
||||
ERROR("Problem when finding plugin init function for %s : %s.", name, error);
|
||||
ctx->create = NULL;
|
||||
antd_plugin_ctx_drop(np, ctx);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// run the init function
|
||||
ctx->data = ctx->create(ctx);
|
||||
if(ctx->status == ANTD_PLUGIN_PANNIC)
|
||||
{
|
||||
ERROR("PANIC happens when init plugin context %s. drop it", name);
|
||||
antd_plugin_ctx_drop(np, ctx);
|
||||
return NULL;
|
||||
}
|
||||
ctx->status = ANTD_PLUGIN_READY;
|
||||
}
|
||||
dput(np->instances, name, (void*)ctx);
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void antd_plugin_entry_drop(struct plugin_entry* np)
|
||||
{
|
||||
if(!np)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(np->name)
|
||||
{
|
||||
LOG("Unloaded %s", np->name);
|
||||
free(np->name);
|
||||
}
|
||||
if(np->instances)
|
||||
{
|
||||
chain_t it;
|
||||
for_each_assoc(it, np->instances)
|
||||
{
|
||||
antd_plugin_ctx_drop(np,(antd_plugin_ctx_t*)it->value);
|
||||
}
|
||||
freedict(np->instances);
|
||||
}
|
||||
if(np->handle)
|
||||
{
|
||||
dlclose(np->handle);
|
||||
}
|
||||
free(np);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a plugin to the plugin table
|
||||
* Only load when not available in the plugin table
|
||||
* @param name plugin name
|
||||
* @param config: plugin configuration
|
||||
* @return pointer to the loaded plugin
|
||||
*/
|
||||
struct plugin_entry *plugin_load(char *name)
|
||||
antd_plugin_ctx_t* antd_plugin_load(const char *name)
|
||||
{
|
||||
const char *plugin_file_name = NULL;
|
||||
char path[BUFFLEN];
|
||||
struct plugin_entry *np;
|
||||
unsigned hashval;
|
||||
if ((np = plugin_lookup(name)) == NULL) { /* not found */
|
||||
np = (struct plugin_entry *) malloc(sizeof(*np));
|
||||
if (np == NULL || (np->pname = strdup(name)) == NULL)
|
||||
antd_plugin_ctx_t *ctx;
|
||||
dictionary_t pconf = dvalue(g_server_config.plugins, name);
|
||||
if (pconf)
|
||||
{
|
||||
plugin_file_name = dvalue(pconf, "name");
|
||||
}
|
||||
if(plugin_file_name == NULL)
|
||||
{
|
||||
plugin_file_name = name;
|
||||
}
|
||||
if(plugin_file_name == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if ((np = plugin_lookup(plugin_file_name)) == NULL)
|
||||
{ /* not found */
|
||||
LOG("Loading plugin: %s...", plugin_file_name);
|
||||
np = (struct plugin_entry *)malloc(sizeof(struct plugin_entry));
|
||||
np->instances = NULL;
|
||||
if (np == NULL)
|
||||
{
|
||||
if(np) free(np);
|
||||
return NULL;
|
||||
}
|
||||
if ((np->handle = plugin_from_file(name)) == NULL)
|
||||
{
|
||||
if(np) free(np);
|
||||
return NULL;
|
||||
}
|
||||
hashval = hash(name,HASHSIZE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(void)snprintf(path, sizeof(path), "%s/%s%s", g_server_config.plugins_dir, plugin_file_name, g_server_config.plugins_ext);
|
||||
np->name = strdup(plugin_file_name);
|
||||
// now load it from file
|
||||
np->handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL /*| RTLD_NODELETE*/);
|
||||
if (!np->handle)
|
||||
{
|
||||
ERROR("Cannot load plugin '%s' : '%s'", plugin_file_name, dlerror());
|
||||
antd_plugin_entry_drop(np);
|
||||
return NULL;
|
||||
}
|
||||
np->instances = dict();
|
||||
hashval = hash(name, HASHSIZE);
|
||||
np->next = plugin_table[hashval];
|
||||
plugin_table[hashval] = np;
|
||||
} else /* already there */
|
||||
{
|
||||
LOG("The plugin %s id already loaded\n", name);
|
||||
}
|
||||
|
||||
return np;
|
||||
}
|
||||
/**
|
||||
* Find a plugin in a file, and load it in to the plugin table
|
||||
* @param name Name of the plugin
|
||||
* @return
|
||||
*/
|
||||
void * plugin_from_file(char* name)
|
||||
{
|
||||
void *lib_handle;
|
||||
char* error;
|
||||
char* path = __s("%s%s%s",config()->plugins_dir,name,config()->plugins_ext);
|
||||
void (*fn)(const char*, config_t*);
|
||||
lib_handle = dlopen(path, RTLD_LAZY);
|
||||
if (!lib_handle)
|
||||
{
|
||||
LOG("Cannot load plugin '%s' : '%s'\n",name,dlerror());
|
||||
if(path)
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
// set database path
|
||||
fn = (void (*)(const char *, config_t*))dlsym(lib_handle, "__init_plugin__");
|
||||
if ((error = dlerror()) != NULL)
|
||||
LOG("Problem when setting data path for %s : %s \n", name,error);
|
||||
else
|
||||
(*fn)(name,config());
|
||||
if(path)
|
||||
free(path);
|
||||
return lib_handle;
|
||||
else /* already there */
|
||||
{
|
||||
LOG("The plugin %s id already loaded", plugin_file_name);
|
||||
}
|
||||
// now look for the plugin context
|
||||
ctx = antd_plugin_ctx_lookup(np, name);
|
||||
// check if plugin is ready
|
||||
if (ctx == NULL)
|
||||
{
|
||||
ERROR("Unable to fetch plugin context for: [%s] %s", plugin_file_name, name);
|
||||
return NULL;
|
||||
}
|
||||
LOG("PLugin instance status: [%s] %d", name, ctx->status);
|
||||
if (ctx->status != ANTD_PLUGIN_READY)
|
||||
{
|
||||
ERROR("Plugin instance is not ready or error: [%s].", name);
|
||||
antd_plugin_ctx_drop(np, ctx);
|
||||
return NULL;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void unload_plugin(struct plugin_entry* np)
|
||||
{
|
||||
char* error;
|
||||
void (*fn)() = NULL;
|
||||
// find and execute the exit function
|
||||
fn = (void(*)()) dlsym(np->handle, "__release__");
|
||||
if ((error = dlerror()) != NULL)
|
||||
{
|
||||
LOG("Cant not release plugin %s : %s \n", np->pname,error);
|
||||
}
|
||||
if(fn)
|
||||
{
|
||||
(*fn)();
|
||||
}
|
||||
dlclose(np->handle);
|
||||
//free((void *) np->handle);
|
||||
free((void *) np->pname);
|
||||
}
|
||||
/*
|
||||
Unload a plugin by its name
|
||||
*/
|
||||
void unload_plugin_by_name(const char* name)
|
||||
Unload a plugin by its name
|
||||
|
||||
void unload_plugin_by_name(const char *name)
|
||||
{
|
||||
LOG("%s\n","Unloading thing");
|
||||
struct plugin_entry *np;
|
||||
int hasval = hash(name, HASHSIZE);
|
||||
np = plugin_table[hasval];
|
||||
if(strcmp(np->pname,name) == 0)
|
||||
{
|
||||
unload_plugin(np);
|
||||
plugin_table[hasval] = np->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (; np != NULL; np = np->next)
|
||||
if (np->next != NULL && strcmp(name, np->next->pname) == 0)
|
||||
break;
|
||||
if(np == NULL) return; // the plugin is is not loaded
|
||||
unload_plugin(np->next);
|
||||
np->next = np->next->next;
|
||||
}
|
||||
struct plugin_entry *np;
|
||||
int hasval = hash(name, HASHSIZE);
|
||||
np = plugin_table[hasval];
|
||||
if (strcmp(np->name, name) == 0)
|
||||
{
|
||||
antd_plugin_entry_drop(np);
|
||||
plugin_table[hasval] = np->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (np = plugin_table[hasval]; np != NULL; np = np->next)
|
||||
{
|
||||
if (np->next != NULL && strcmp(name, np->next->name) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (np == NULL)
|
||||
return; // the plugin is is not loaded
|
||||
antd_plugin_entry_drop(np->next);
|
||||
np->next = np->next->next;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Unload all the plugin loaded on the plugin table
|
||||
*/
|
||||
void unload_all_plugin()
|
||||
void antd_unload_all_plugin()
|
||||
{
|
||||
LOG("Unload all plugins\n");
|
||||
for(int i=0;i<HASHSIZE;i++)
|
||||
{
|
||||
struct plugin_entry **np, *curr;
|
||||
np = &plugin_table[i];
|
||||
LOG("Unload all plugins");
|
||||
for (int i = 0; i < HASHSIZE; i++)
|
||||
{
|
||||
struct plugin_entry **np, *curr;
|
||||
np = &plugin_table[i];
|
||||
|
||||
while((curr = *np) != NULL)
|
||||
{
|
||||
(*np) = (*np)->next;
|
||||
unload_plugin(curr);
|
||||
free(curr);
|
||||
}
|
||||
while ((curr = *np) != NULL)
|
||||
{
|
||||
(*np) = (*np)->next;
|
||||
antd_plugin_entry_drop(curr);
|
||||
//free(curr);
|
||||
}
|
||||
plugin_table[i] = NULL;
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
antd_plugin_handle_t antd_get_ctx_handle(antd_plugin_ctx_t *ctx)
|
||||
{
|
||||
return ctx->handle;
|
||||
}
|
||||
|
@ -1,21 +1,13 @@
|
||||
#ifndef PLUGIN_MANAGER_H
|
||||
#define PLUGIN_MANAGER_H
|
||||
#include <dlfcn.h>
|
||||
#include "libs/utils.h"
|
||||
#include "libs/handle.h"
|
||||
#include "http_server.h"
|
||||
struct plugin_entry {
|
||||
struct plugin_entry *next;
|
||||
char *pname;
|
||||
void *handle;
|
||||
};
|
||||
/* lookup: look for s in hashtab */
|
||||
struct plugin_entry *plugin_lookup(char *s);
|
||||
/* install: put (name, defn) in hashtab */
|
||||
struct plugin_entry *plugin_load(char *name);
|
||||
void unload_all_plugin();
|
||||
void unload_plugin(struct plugin_entry*);
|
||||
void unload_plugin_by_name(const char*);
|
||||
void * plugin_from_file(char* name);
|
||||
|
||||
#include "lib/dictionary.h"
|
||||
#include "lib/plugin.h"
|
||||
#include "lib/scheduler.h"
|
||||
|
||||
typedef void*(*antd_plugin_handle_t)(void *);
|
||||
|
||||
antd_plugin_ctx_t *antd_plugin_load(const char *name);
|
||||
void antd_unload_all_plugin();
|
||||
antd_plugin_handle_t antd_get_ctx_handle(antd_plugin_ctx_t *);
|
||||
#endif
|
141
relay.c
141
relay.c
@ -1,141 +0,0 @@
|
||||
#include "http_server.h"
|
||||
#include "libs/scheduler.h"
|
||||
#include <fcntl.h>
|
||||
static antd_scheduler_t scheduler;
|
||||
/*
|
||||
this node is a relay from the http
|
||||
to https
|
||||
*/
|
||||
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
|
||||
int server_sock = -1;
|
||||
void stop_serve(int dummy) {
|
||||
UNUSED(dummy);
|
||||
antd_scheduler_destroy(&scheduler);
|
||||
close(server_sock);
|
||||
}
|
||||
/*
|
||||
HTTP/1.1 301 Moved Permanently
|
||||
Location: http://www.example.org/
|
||||
Content-Type: text/html
|
||||
Content-Length: 174
|
||||
*/
|
||||
void* antd_redirect(void* user_data)
|
||||
{
|
||||
void** data = (void**)user_data;
|
||||
void* client = data[0];
|
||||
char* host = (char*)data[1];
|
||||
set_status(client, 301,"Moved Permanently");
|
||||
__t(client, "Location: https://%s", host);
|
||||
__t(client, "%s", "Content-Type: text/html");
|
||||
response(client, "");
|
||||
__t(client, "This page has been moved to https://%s", host);
|
||||
free(host);
|
||||
free(user_data);
|
||||
return antd_create_task(NULL,client, NULL, ((antd_client_t*)client)->last_io);
|
||||
}
|
||||
|
||||
void* antd_free_client(void* client)
|
||||
{
|
||||
antd_client_t * source = (antd_client_t *) client;
|
||||
if(source->ip) free(source->ip);
|
||||
close(source->sock);
|
||||
free(client);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* antd_get_host(void * client)
|
||||
{
|
||||
char buff[1024];
|
||||
char* line, *token;
|
||||
char* host = NULL;
|
||||
while((read_buf(client,buff,sizeof(buff))) && strcmp("\r\n",buff))
|
||||
{
|
||||
line = buff;
|
||||
trim(line, '\n');
|
||||
trim(line, '\r');
|
||||
token = strsep(&line, ":");
|
||||
trim(token,' ');
|
||||
trim(line,' ');
|
||||
if(token && strcasecmp(token,"Host")==0)
|
||||
if(line)
|
||||
{
|
||||
host = strdup(line);
|
||||
//break;
|
||||
}
|
||||
}
|
||||
if(!host) host = strdup("lxsang.me");
|
||||
void** data = (void**)malloc(2*(sizeof *data));
|
||||
data[0] = client;
|
||||
data[1] = (void*)host;
|
||||
LOG("[%s] Request for: %s --> https://%s\n", ((antd_client_t*)client)->ip, host, host);
|
||||
return antd_create_task(antd_redirect,data, NULL, time(NULL));
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
// load the config first
|
||||
unsigned port = 80;
|
||||
int client_sock = -1;
|
||||
struct sockaddr_in client_name;
|
||||
socklen_t client_name_len = sizeof(client_name);
|
||||
// ignore the broken PIPE error when writing
|
||||
//or reading to/from a closed socked connection
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
signal(SIGABRT, SIG_IGN);
|
||||
signal(SIGINT, stop_serve);
|
||||
server_sock = startup(&port);
|
||||
//struct timeval timeout;
|
||||
//timeout.tv_sec = 0;
|
||||
//timeout.tv_usec = 500;
|
||||
// 0 worker
|
||||
antd_scheduler_init(&scheduler, 0);
|
||||
scheduler.validate_data = 1;
|
||||
scheduler.destroy_data = antd_free_client;
|
||||
// set server socket to non blocking
|
||||
set_nonblock(server_sock);
|
||||
LOG("relayd running on port %d\n", port);
|
||||
struct timespec ts_sleep;
|
||||
while (scheduler.status)
|
||||
{
|
||||
// execute task
|
||||
int stat = antd_task_schedule(&scheduler);
|
||||
client_sock = accept(server_sock,(struct sockaddr *)&client_name,&client_name_len);
|
||||
if (client_sock == -1)
|
||||
{
|
||||
// sleep for 500usec if
|
||||
// there is nothing todo
|
||||
if(!stat)
|
||||
{
|
||||
ts_sleep.tv_sec = 0;
|
||||
ts_sleep.tv_nsec = 5000000;
|
||||
nanosleep(&ts_sleep, NULL);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
set_nonblock(client_sock);
|
||||
antd_client_t* client = (antd_client_t*)malloc(sizeof(antd_client_t));
|
||||
// set timeout to socket
|
||||
|
||||
//if (setsockopt (client_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,sizeof(timeout)) < 0)
|
||||
// perror("setsockopt failed\n");
|
||||
|
||||
//if (setsockopt (client_sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,sizeof(timeout)) < 0)
|
||||
// perror("setsockopt failed\n");
|
||||
|
||||
/*
|
||||
get the remote IP
|
||||
*/
|
||||
client->ip = NULL;
|
||||
if (client_name.sin_family == AF_INET)
|
||||
client->ip = strdup(inet_ntoa(client_name.sin_addr));
|
||||
client->sock = client_sock;
|
||||
time(&client->last_io);
|
||||
//accept_request(&client);
|
||||
antd_add_task(&scheduler, antd_create_task(antd_get_host,(void*)client, antd_free_client, client->last_io));
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
577
server.c
Normal file
577
server.c
Normal file
@ -0,0 +1,577 @@
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <sys/socket.h>
|
||||
#include <poll.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
//#include <limits.h>
|
||||
//#include <stdlib.h>
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#endif
|
||||
|
||||
#include "server.h"
|
||||
#include "lib/handle.h"
|
||||
#include "plugin_manager.h"
|
||||
#include "lib/scheduler.h"
|
||||
#include "lib/utils.h"
|
||||
#include "decode.h"
|
||||
#include "config.h"
|
||||
|
||||
|
||||
static pthread_mutex_t server_mux = PTHREAD_MUTEX_INITIALIZER;
|
||||
config_t g_server_config;
|
||||
|
||||
void *execute_plugin(void *data, const char *pname);
|
||||
void *serve_file(void *data);
|
||||
|
||||
void *accept_request(void *data)
|
||||
{
|
||||
char buf[BUFFLEN];
|
||||
char *token = NULL;
|
||||
char *line = NULL;
|
||||
antd_task_t *task;
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
|
||||
task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE);
|
||||
// first verify if the socket is ready
|
||||
antd_client_t *client = (antd_client_t *)rq->client;
|
||||
|
||||
struct pollfd pfd[1];
|
||||
|
||||
pfd[0].fd = client->sock;
|
||||
pfd[0].events = POLLIN | POLLOUT;
|
||||
|
||||
int sel = poll(pfd, 1, POLL_EVENT_TO);
|
||||
if (sel == -1)
|
||||
{
|
||||
antd_error(rq->client, 400, "Bad request");
|
||||
return task;
|
||||
}
|
||||
if (pfd[0].revents & POLLERR || pfd[0].revents & POLLHUP)
|
||||
{
|
||||
antd_error(rq->client, 400, "Bad request");
|
||||
return task;
|
||||
}
|
||||
if (sel == 0 || (!(pfd[0].revents & POLLIN) && !(pfd[0].revents & POLLOUT)))
|
||||
{
|
||||
task->handle = accept_request;
|
||||
return task;
|
||||
}
|
||||
// perform the ssl handshake if enabled
|
||||
#ifdef USE_OPENSSL
|
||||
int ret = -1, stat;
|
||||
if (client->ssl && client->state == ANTD_CLIENT_ACCEPT)
|
||||
{
|
||||
// LOG("Atttempt %d\n", client->attempt);
|
||||
if (SSL_accept((SSL *)client->ssl) == -1)
|
||||
{
|
||||
stat = SSL_get_error((SSL *)client->ssl, ret);
|
||||
switch (stat)
|
||||
{
|
||||
case SSL_ERROR_WANT_READ:
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
case SSL_ERROR_NONE:
|
||||
task->handle = accept_request;
|
||||
return task;
|
||||
default:
|
||||
ERROR("Error performing SSL handshake %d %d %s", stat, ret, ERR_error_string(ERR_get_error(), NULL));
|
||||
antd_error(rq->client, 400, "Invalid SSL request");
|
||||
// server_config.connection++;
|
||||
ERR_print_errors_fp(stderr);
|
||||
return task;
|
||||
}
|
||||
}
|
||||
client->state = ANTD_CLIENT_HANDSHAKE;
|
||||
task->handle = accept_request;
|
||||
// LOG("Handshake finish for %d\n", client->sock);
|
||||
return task;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!((pfd[0].revents & POLLIN)))
|
||||
{
|
||||
task->handle = accept_request;
|
||||
return task;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// LOG("Ready for reading %d\n", client->sock);
|
||||
// server_config.connection++;
|
||||
client->state = ANTD_CLIENT_PROTO_CHECK;
|
||||
read_buf(rq->client, buf, sizeof(buf));
|
||||
line = buf;
|
||||
LOG("Request (%d): %s", rq->client->sock, line);
|
||||
// get the method string
|
||||
token = strsep(&line, " ");
|
||||
if (!line)
|
||||
{
|
||||
// LOG("No method found");
|
||||
antd_error(rq->client, 405, "No method found");
|
||||
return task;
|
||||
}
|
||||
trim(token, ' ');
|
||||
trim(line, ' ');
|
||||
dput(rq->request, "METHOD", strdup(token));
|
||||
// get the request
|
||||
token = strsep(&line, " ");
|
||||
if (!line)
|
||||
{
|
||||
// LOG("No request found");
|
||||
antd_error(rq->client, 400, "Bad request");
|
||||
return task;
|
||||
}
|
||||
trim(token, ' ');
|
||||
trim(line, ' ');
|
||||
trim(line, '\n');
|
||||
trim(line, '\r');
|
||||
dput(rq->request, "PROTOCOL", strdup(line));
|
||||
dput(rq->request, "REQUEST_QUERY", strdup(token));
|
||||
line = token;
|
||||
token = strsep(&line, "?");
|
||||
dput(rq->request, "REQUEST_PATH", url_decode(token));
|
||||
// decode request
|
||||
// now return the task
|
||||
task->handle = decode_request_header;
|
||||
return task;
|
||||
}
|
||||
|
||||
void *resolve_request(void *data)
|
||||
{
|
||||
struct stat st;
|
||||
char path[2 * BUFFLEN];
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
char *url = (char *)dvalue(rq->request, "REQUEST_URI");
|
||||
char *newurl = NULL;
|
||||
char *rqp = NULL;
|
||||
char *oldrqp = NULL;
|
||||
rq->client->state = ANTD_CLIENT_RESOLVE_REQUEST;
|
||||
char * root = (char *)dvalue(rq->request, "SERVER_WWW_ROOT");
|
||||
snprintf(path, sizeof(path), "%s/%s", root, url);
|
||||
LOG("URL is : %s", url);
|
||||
LOG("Resource Path is : %s", path);
|
||||
// if (path[strlen(path) - 1] == '/')
|
||||
// strcat(path, "index.html");
|
||||
if (stat(path, &st) == -1)
|
||||
{
|
||||
free(task);
|
||||
rqp = strdup(url);
|
||||
oldrqp = rqp;
|
||||
trim(rqp, '/');
|
||||
newurl = strsep(&rqp, "/");
|
||||
if (!rqp)
|
||||
rqp = strdup("/");
|
||||
else
|
||||
rqp = strdup(rqp);
|
||||
dput(rq->request, "REQUEST_URI", rqp);
|
||||
dput(rq->request, "RESOURCE_PATH", __s("%s/%s", root,rqp));
|
||||
LOG("Execute plugin %s", newurl);
|
||||
task = execute_plugin(rq, newurl);
|
||||
free(oldrqp);
|
||||
return task;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (S_ISDIR(st.st_mode))
|
||||
{
|
||||
strcat(path, "/index.html");
|
||||
if (stat(path, &st) == -1)
|
||||
{
|
||||
chain_t it;
|
||||
newurl = NULL;
|
||||
for_each_assoc(it, g_server_config.handlers)
|
||||
{
|
||||
memset(path, 0, sizeof(path));
|
||||
snprintf(path, sizeof(path), "%s/%s/index.%s", root, url, it->key);
|
||||
if (stat(path, &st) == 0)
|
||||
{
|
||||
i = g_server_config.handlers->cap;
|
||||
newurl = path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!newurl)
|
||||
{
|
||||
antd_error(rq->client, 404, "Resource Not Found");
|
||||
return task;
|
||||
}
|
||||
}
|
||||
}
|
||||
dput(rq->request, "RESOURCE_PATH", strdup(path));
|
||||
// check if the mime is supported
|
||||
// if the mime is not supported
|
||||
// find an handler plugin to process it
|
||||
// if the plugin is not found, forbidden access to the file should be sent
|
||||
char *mime_type = mime(path);
|
||||
dput(rq->request, "RESOURCE_MIME", strdup(mime_type));
|
||||
if (strcmp(mime_type, "application/octet-stream") == 0)
|
||||
{
|
||||
char *ex = ext(path);
|
||||
char *h = NULL;
|
||||
if (ex)
|
||||
{
|
||||
h = dvalue(g_server_config.handlers, ex);
|
||||
free(ex);
|
||||
}
|
||||
if (h)
|
||||
{
|
||||
// sprintf(path,"/%s%s",h,url);
|
||||
// LOG("WARNING::::Access octetstream via handle %s", h);
|
||||
// if(execute_plugin(client,buf,method,rq) < 0)
|
||||
// cannot_execute(client);
|
||||
free(task);
|
||||
return execute_plugin(rq, h);
|
||||
}
|
||||
else
|
||||
antd_error(rq->client, 403, "Access forbidden");
|
||||
}
|
||||
else
|
||||
{
|
||||
// discard all request data
|
||||
dictionary_t headers = (dictionary_t)dvalue(rq->request, "REQUEST_HEADER");
|
||||
if (headers)
|
||||
{
|
||||
char *sclen = (char *)dvalue(headers, "Content-Length");
|
||||
unsigned clen = 0;
|
||||
unsigned read = 0;
|
||||
int count;
|
||||
if (sclen)
|
||||
{
|
||||
clen = atoi(sclen);
|
||||
while (read < clen)
|
||||
{
|
||||
count = antd_recv(rq->client, path, sizeof(path) < clen ? sizeof(path) : clen);
|
||||
if (count <= 0)
|
||||
break;
|
||||
read += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE);
|
||||
task->handle = serve_file;
|
||||
}
|
||||
return task;
|
||||
}
|
||||
}
|
||||
|
||||
void *finish_request(void *data)
|
||||
{
|
||||
if (!data)
|
||||
return NULL;
|
||||
destroy_request(data);
|
||||
g_server_config.connection--;
|
||||
LOG("Remaining connection %d", g_server_config.connection);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void *serve_file(void *data)
|
||||
{
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
char *path = (char *)dvalue(rq->request, "RESOURCE_PATH");
|
||||
char *mime_type = (char *)dvalue(rq->request, "RESOURCE_MIME");
|
||||
rq->client->state = ANTD_CLIENT_SERVE_FILE;
|
||||
struct stat st;
|
||||
int s = stat(path, &st);
|
||||
|
||||
if (s == -1)
|
||||
{
|
||||
antd_error(rq->client, 404, "File not found");
|
||||
}
|
||||
else
|
||||
{
|
||||
// check if it is modified
|
||||
dictionary_t header = (dictionary_t)dvalue(rq->request, "REQUEST_HEADER");
|
||||
char *last_modif_since = (char *)dvalue(header, "If-Modified-Since");
|
||||
time_t t = st.st_ctime;
|
||||
struct tm tm;
|
||||
if (last_modif_since)
|
||||
{
|
||||
strptime(last_modif_since, "%a, %d %b %Y %H:%M:%S GMT", &tm);
|
||||
t = timegm(&tm);
|
||||
// t = mktime(localtime(&t));
|
||||
}
|
||||
|
||||
if (last_modif_since && st.st_ctime == t)
|
||||
{
|
||||
// return the not changed
|
||||
antd_error(rq->client, 304, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = (int)st.st_size;
|
||||
char ibuf[64];
|
||||
snprintf(ibuf, sizeof(ibuf), "%d", size);
|
||||
antd_response_header_t rhd;
|
||||
rhd.cookie = NULL;
|
||||
rhd.status = 200;
|
||||
rhd.header = dict();
|
||||
dput(rhd.header, "Content-Type", strdup(mime_type));
|
||||
#ifdef USE_ZLIB
|
||||
if (!compressable(mime_type) || rq->client->z_level == ANTD_CNONE)
|
||||
#endif
|
||||
dput(rhd.header, "Content-Length", strdup(ibuf));
|
||||
gmtime_r(&st.st_ctime, &tm);
|
||||
strftime(ibuf, 64, "%a, %d %b %Y %H:%M:%S GMT", &tm);
|
||||
dput(rhd.header, "Last-Modified", strdup(ibuf));
|
||||
dput(rhd.header, "Cache-Control", strdup("no-cache"));
|
||||
antd_send_header(rq->client, &rhd);
|
||||
|
||||
__f(rq->client, path);
|
||||
}
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
static void *proxy_monitor(void *data)
|
||||
{
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
rq->client->state = ANTD_CLIENT_PROXY_MONITOR;
|
||||
antd_client_t *proxy = (antd_client_t *)dvalue(rq->request, "PROXY_HANDLE");
|
||||
antd_task_t *task = antd_create_task(NULL, data, NULL, rq->client->last_io);
|
||||
int pret, ret, sz1 = 0, sz2 = 0;
|
||||
char *buf = NULL;
|
||||
buf = (char *)malloc(BUFFLEN);
|
||||
struct pollfd pfd[1];
|
||||
memset(pfd, 0, sizeof(pfd));
|
||||
pfd[0].fd = proxy->sock;
|
||||
pfd[0].events = POLLIN;
|
||||
ret = 1;
|
||||
|
||||
do
|
||||
{
|
||||
sz1 = antd_recv_upto(rq->client, buf, BUFFLEN);
|
||||
|
||||
if ((sz1 < 0) || (sz1 > 0 && antd_send(proxy, buf, sz1) != sz1))
|
||||
{
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
pret = poll(pfd, 1, 0);
|
||||
if (pret < 0)
|
||||
{
|
||||
(void)close(proxy->sock);
|
||||
return task;
|
||||
}
|
||||
sz2 = 0;
|
||||
if (pret > 0 && (pfd[0].revents & POLLIN))
|
||||
{
|
||||
sz2 = antd_recv_upto(proxy, buf, BUFFLEN);
|
||||
if (sz2 <= 0 || (sz2 > 0 && antd_send(rq->client, buf, sz2) != sz2))
|
||||
{
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((pret > 0) && (pfd[0].revents & POLLERR ||
|
||||
pfd[0].revents & POLLRDHUP ||
|
||||
pfd[0].revents & POLLHUP ||
|
||||
pfd[0].revents & POLLNVAL))
|
||||
{
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
} while (sz1 > 0 || sz2 > 0);
|
||||
free(buf);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
(void)close(proxy->sock);
|
||||
return task;
|
||||
}
|
||||
|
||||
if (pfd[0].revents & POLLIN)
|
||||
{
|
||||
antd_task_bind_event(task, proxy->sock, 0, TASK_EVT_ON_READABLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
antd_task_bind_event(task, proxy->sock, 50u, TASK_EVT_ON_TIMEOUT);
|
||||
}
|
||||
task->handle = proxy_monitor;
|
||||
task->access_time = rq->client->last_io;
|
||||
|
||||
antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_READABLE);
|
||||
return task;
|
||||
}
|
||||
|
||||
void *proxify(void *data)
|
||||
{
|
||||
int sock_fd, size, ret;
|
||||
char *str = NULL;
|
||||
chain_t it;
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
antd_client_t *proxy = NULL;
|
||||
rq->client->state = ANTD_CLIENT_RESOLVE_REQUEST;
|
||||
char *host = dvalue(rq->request, "PROXY_HOST");
|
||||
int port = atoi(dvalue(rq->request, "PROXY_PORT"));
|
||||
char *path = dvalue(rq->request, "PROXY_PATH");
|
||||
char *query = dvalue(rq->request, "PROXY_QUERY");
|
||||
char *ptr, *ip;
|
||||
dictionary_t xheader = dvalue(rq->request, "REQUEST_HEADER");
|
||||
antd_task_t *task = antd_create_task(NULL, data, NULL, rq->client->last_io);
|
||||
if (!xheader)
|
||||
{
|
||||
antd_error(rq->client, 400, "Badd Request");
|
||||
return task;
|
||||
}
|
||||
pthread_mutex_lock(&server_mux);
|
||||
ip = NULL;
|
||||
// ip_from_host is not threadsafe, need to lock it
|
||||
ptr = ip_from_hostname(host);
|
||||
if (ptr)
|
||||
{
|
||||
ip = strdup(ptr);
|
||||
}
|
||||
pthread_mutex_unlock(&server_mux);
|
||||
|
||||
if (!ip)
|
||||
{
|
||||
antd_error(rq->client, 502, "Badd address");
|
||||
return task;
|
||||
}
|
||||
// TODO support ipv6
|
||||
sock_fd = antd_request_socket(ip, port);
|
||||
free(ip);
|
||||
if (sock_fd == -1)
|
||||
{
|
||||
antd_error(rq->client, 503, "Service Unavailable");
|
||||
return task;
|
||||
}
|
||||
set_nonblock(sock_fd);
|
||||
/*struct timeval timeout;
|
||||
timeout.tv_sec = 2;
|
||||
timeout.tv_usec = 0; //POLL_EVENT_TO*1000;
|
||||
if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0)
|
||||
{
|
||||
ERROR("setsockopt failed:%s", strerror(errno));
|
||||
antd_error(rq->client, 500, "Internal proxy error");
|
||||
(void)close(sock_fd);
|
||||
return task;
|
||||
}
|
||||
|
||||
if (setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0)
|
||||
{
|
||||
ERROR("setsockopt failed:%s", strerror(errno));
|
||||
antd_error(rq->client, 500, "Internal proxy error");
|
||||
(void)close(sock_fd);
|
||||
return task;
|
||||
}*/
|
||||
|
||||
proxy = (antd_client_t *)malloc(sizeof(antd_client_t));
|
||||
proxy->sock = sock_fd;
|
||||
proxy->ssl = NULL;
|
||||
proxy->zstream = NULL;
|
||||
proxy->z_level = ANTD_CNONE;
|
||||
time(&proxy->last_io);
|
||||
|
||||
// store content length here
|
||||
dput(rq->request, "PROXY_HANDLE", proxy);
|
||||
|
||||
str = __s("%s %s?%s HTTP/1.1\r\n", (char *)dvalue(rq->request, "METHOD"), path, query);
|
||||
size = strlen(str);
|
||||
ret = antd_send(proxy, str, size);
|
||||
free(str);
|
||||
if (ret != size)
|
||||
{
|
||||
antd_error(rq->client, 500, "");
|
||||
(void)close(sock_fd);
|
||||
return task;
|
||||
}
|
||||
for_each_assoc(it, xheader)
|
||||
{
|
||||
str = __s("%s: %s\r\n", it->key, (char *)it->value);
|
||||
size = strlen(str);
|
||||
ret = antd_send(proxy, str, size);
|
||||
free(str);
|
||||
if (ret != size)
|
||||
{
|
||||
antd_error(rq->client, 500, "");
|
||||
(void)close(sock_fd);
|
||||
return task;
|
||||
}
|
||||
}
|
||||
(void)antd_send(proxy, "\r\n", 2);
|
||||
// now monitor the proxy
|
||||
task->handle = proxy_monitor;
|
||||
task->access_time = rq->client->last_io;
|
||||
// register event
|
||||
antd_task_bind_event(task, proxy->sock, 0, TASK_EVT_ON_READABLE | TASK_EVT_ON_WRITABLE);
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Execute a plugin based on the http requeset
|
||||
* First decode the http request header to find the correct plugin
|
||||
* and the correct function on the plugin
|
||||
* Second, decode all parameters necessary of the request and pass it
|
||||
* to the callback function.
|
||||
* Execute the callback function if sucess
|
||||
* @param client soket client
|
||||
* @param path request path
|
||||
* @param method request method
|
||||
* @param query_string GET query string
|
||||
* @return -1 if failure
|
||||
* 1 if sucess
|
||||
*/
|
||||
void *execute_plugin(void *data, const char *pname)
|
||||
{
|
||||
char pattern[256];
|
||||
antd_plugin_ctx_t* ctx;
|
||||
antd_request_t *rq = (antd_request_t *)data;
|
||||
antd_task_t *task = antd_create_task(NULL, (void *)rq, NULL, rq->client->last_io);
|
||||
antd_task_bind_event(task, rq->client->sock, 0, TASK_EVT_ON_WRITABLE | TASK_EVT_ON_READABLE);
|
||||
|
||||
snprintf(pattern, sizeof(pattern), "\\b%s\\b", pname);
|
||||
char *port_s = (char *)dvalue(rq->request, "SERVER_PORT");
|
||||
port_config_t *pcnf = (port_config_t *)dvalue(g_server_config.ports, port_s);
|
||||
|
||||
// check if plugin is enabled on this port
|
||||
if (!pcnf->plugins || !regex_match(pattern, pcnf->plugins, 0, NULL))
|
||||
{
|
||||
LOG("No plugin matched in [%s] using pattern [%s]", pcnf->plugins, pattern);
|
||||
antd_error(rq->client, 403, "Access forbidden");
|
||||
return task;
|
||||
}
|
||||
|
||||
// LOG("Plugin name '%s'", pname);
|
||||
rq->client->state = ANTD_CLIENT_PLUGIN_EXEC;
|
||||
// load the plugin
|
||||
pthread_mutex_lock(&server_mux);
|
||||
ctx = antd_plugin_load(pname);
|
||||
pthread_mutex_unlock(&server_mux);
|
||||
if (ctx == NULL)
|
||||
{
|
||||
antd_error(rq->client, 503, "Requested service not found");
|
||||
return task;
|
||||
}
|
||||
rq->context = ctx;
|
||||
// check if we need the raw data or not
|
||||
if (antd_plugin_is_raw_body(ctx) == 1)
|
||||
{
|
||||
task->handle = antd_get_ctx_handle(ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
free(task);
|
||||
task = antd_create_task(decode_post_request, (void *)rq, antd_get_ctx_handle(ctx), rq->client->last_io);
|
||||
}
|
||||
return task;
|
||||
}
|
8
server.h
Normal file
8
server.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef HTTP_SERVER
|
||||
#define HTTP_SERVER
|
||||
|
||||
void * accept_request(void *);
|
||||
void * proxify(void *data);
|
||||
void * resolve_request(void *data);
|
||||
void * finish_request(void *data);
|
||||
#endif
|
BIN
ssl_score.png
BIN
ssl_score.png
Binary file not shown.
Before Width: | Height: | Size: 18 KiB |
51
var.mk
51
var.mk
@ -1,51 +0,0 @@
|
||||
USE_DB=TRUE
|
||||
USE_SSL = TRUE
|
||||
CC=gcc
|
||||
EXT=dylib
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
BUILDIRD=/opt/www
|
||||
PF_FLAG=-D_GNU_SOURCE -DLINUX
|
||||
PPF_FLAG=-D_GNU_SOURCE -DLINUX -Wl,--no-as-needed
|
||||
endif
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
BUILDIRD=/Users/mrsang/Documents/build/www
|
||||
PF_FLAG= -DMACOS
|
||||
PPF_FLAG=-D_GNU_SOURCE -DMACOS -Wl,-undefined,dynamic_lookup
|
||||
SSL_HEADER_PATH = -I/usr/local/opt/openssl/include
|
||||
SSL_LIB_PATH = -L/usr/local/opt/openssl/lib
|
||||
endif
|
||||
|
||||
ifeq ($(USE_DB),TRUE)
|
||||
DB_OBJ=libs/dbhelper.o
|
||||
DB_LIB=-lsqlite3
|
||||
DB_FLAG=-D USE_DB
|
||||
endif
|
||||
|
||||
ifeq ($(USE_DB),FALSE)
|
||||
DB_OBJ=
|
||||
DB_LIB=
|
||||
DB_FLAG=
|
||||
endif
|
||||
|
||||
ifeq ($(USE_SSL),TRUE)
|
||||
SSL_LIB= $(SSL_LIB_PATH) -lssl -lcrypto
|
||||
SSL_FLAG=-D USE_OPENSSL
|
||||
endif
|
||||
|
||||
ifeq ($(USE_SSL),FALSE)
|
||||
SSL_LIB=
|
||||
SSL_FLAG=
|
||||
SSL_HEADER_PATH =
|
||||
SSL_LIB_PATH =
|
||||
endif
|
||||
|
||||
|
||||
CFLAGS= -W -Wall -g -std=c99 -D DEBUG $(DB_FLAG) $(PF_FLAG) $(SSL_FLAG) $(SSL_HEADER_PATH)
|
||||
|
||||
# xplugin variables
|
||||
PLUGINS_BASE=../../libs
|
||||
PBUILDIRD=$(BUILDIRD)/plugins
|
||||
LIB_CFLAGS= -W -Wall -g -std=c99 -W $(PPF_FLAG)
|
||||
APP_DIR=$(BUILDIRD)/htdocs/
|
||||
INCFLAG= -I$(PLUGINS_BASE)
|
Loading…
x
Reference in New Issue
Block a user