Compare commits

...

207 Commits

Author SHA1 Message Date
DanyLE
cc5bb1284c update REAME
All checks were successful
Autotools pipeline / build-amd64 (push) Successful in 22s
Autotools pipeline / build-arm64 (push) Successful in 25s
Autotools pipeline / build-arm (push) Successful in 25s
2024-07-27 00:55:03 +02:00
DanyLE
d3e8a05732 feat(ci): use gitea action instead of Jenkins 2024-07-27 00:52:28 +02:00
DanyLE
2863cf1710 fix(CI): use cross toolchain
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2024-03-13 23:30:42 +01:00
DanyLE
4f91f99ca0 feat: add SIGTERM handling
Some checks reported errors
gitea-sync/ant-http/pipeline/head Something is wrong with the build of this commit
2024-03-13 22:48:10 +01:00
DanyLE
58a7738afe feat: rework on plugin support
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
- New plugin interface that supports multiple instances
- Fix and improve memory bugs
- Refactory and cleanup lib
- Improve scheduler
2024-03-13 18:07:07 +01:00
DanyLE
3bedc3ffb5 refactor: refactoring base code 2024-03-12 21:59:50 +01:00
DanyLE
9c25e62c0a feat(v2.0.0): Improvement + add IPv6 support
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
- fix: scheduler causes segmentfault when exiting the server
  - Add support for IPv6 protocol
2024-03-11 22:35:35 +01:00
DanyLE
32552ee0ed fix: remove openssl deprecated code
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2024-03-09 16:52:28 +01:00
DanyLE
375989acbe fix: correct path to support fastCGI
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2023-01-25 22:35:15 +01:00
DanyLE
eed2e9c1af Add more log
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2023-01-16 23:23:52 +01:00
DanyLE
a1acea2441 Allow sandboxing plugin instances
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2023-01-15 18:21:42 +01:00
DanyLE
bd0663ddf7 Unload plugin if pannic happen
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2023-01-11 22:18:45 +01:00
DanyLE
6a483e188d Allow to break rules applying when <break> command found in the current matched rule
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2023-01-09 20:54:06 +01:00
DanyLE
a78815c3b6 Allow enable/disable plugin on each port
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2023-01-09 19:51:21 +01:00
DanyLE
4e1c220b39 Allow specific configuration for plugins 2023-01-09 16:31:59 +01:00
Dany LE
5d7a0f8b49
Fix incorrect upload file path
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2023-01-04 21:39:46 +01:00
Dany LE
cab479a0fb
fix: plugin path bug
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2022-10-04 14:20:05 +02:00
Dany LE
a8201947ef
Update http_server.c
Some checks reported errors
gitea-sync/ant-http/pipeline/head Something is wrong with the build of this commit
2022-10-04 14:16:42 +02:00
Dany LE
91461f0b21
Infer plugin realpath from configuration
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2022-10-04 13:02:20 +02:00
Dany LE
0d9d6b5497
query realpathes from configurations
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2022-10-04 12:30:35 +02:00
Dany LE
a96523c218
database path shall only accessed by root
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2022-10-03 17:47:37 +02:00
Dany LE
6c39e969a4
Use older toolchain
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2022-09-30 11:57:30 +02:00
DanyLE
cd40ec7214 ws header read: return null if data is not available
All checks were successful
gitea-sync/ant-http/pipeline/head This commit looks good
2022-08-31 20:37:00 +02:00
DanyLE
bf9cd3f1ca use select in scheduler 2022-08-30 17:36:19 +02:00
DanyLE
e35c8b7a6f use poll instead of select 2022-08-30 17:27:01 +02:00
DanyLE
c7f6cf42cb refactor code, let plugin handle application data in post request 2022-08-27 21:42:56 +02:00
DanyLE
088baa1ef5 use open instead of fopen for fire readwarite 2022-08-27 16:57:44 +02:00
DanyLE
923c6b2625 fix bug on reading socket when ssl is disable 2022-08-23 01:16:25 +02:00
Dany LE
7315ece4dc
Update http_server.c 2022-08-21 18:26:34 +02:00
Dany LE
e6f0588a84
Update http_server.c 2022-08-21 18:25:26 +02:00
Dany LE
99c5df1c48
Update http_server.c 2022-08-21 18:14:08 +02:00
Dany LE
8cab868003
Update http_server.c 2022-08-21 17:01:34 +02:00
lxsang
7bf58c1d01 save the entire post request body to request 2022-08-19 17:57:45 +02:00
Dany LE
5d79f912c1
Update Jenkinsfile 2022-08-02 21:28:51 +02:00
Dany LE
4c62fae037
Update Jenkinsfile 2022-08-02 21:14:51 +02:00
Dany LE
9b8b4b7b63
Update Jenkinsfile 2022-08-02 21:02:26 +02:00
Dany LE
8f982694c6
Update Jenkinsfile 2022-08-02 20:59:48 +02:00
Dany LE
e1872f8b6b
Update Jenkinsfile 2022-08-02 20:58:57 +02:00
Dany LE
6780f94fcf
Update Jenkinsfile 2022-08-02 17:33:15 +02:00
Dany LE
f9f5de2809
Update Jenkinsfile 2022-08-02 17:32:42 +02:00
Dany LE
4d6c7f9326
Update Jenkinsfile 2022-08-02 17:24:35 +02:00
Dany LE
0c5385fd3f
Update Jenkinsfile 2022-08-02 17:22:58 +02:00
Dany LE
5851f3cba3
Update Jenkinsfile 2022-08-02 17:21:14 +02:00
Dany LE
d4fa350a3a
Update Jenkinsfile 2022-08-02 17:20:29 +02:00
Dany LE
b864827f2c
Update Jenkinsfile 2022-08-02 17:18:15 +02:00
Dany LE
11876c6e29
Update Jenkinsfile 2022-08-02 17:15:23 +02:00
Dany LE
a6bb0b16db
Update Jenkinsfile 2022-08-02 17:08:01 +02:00
Dany LE
100f9d74b6
Update Jenkinsfile 2022-08-02 17:01:10 +02:00
Dany LE
19ea0256a4
Update Jenkinsfile 2022-08-02 16:54:19 +02:00
Dany LE
b766884997
Update Jenkinsfile 2022-08-02 16:39:40 +02:00
Dany LE
81e73669e6
Update Jenkinsfile 2022-08-02 11:47:15 +02:00
Dany LE
82bede7e72
Update Jenkinsfile 2022-08-02 11:43:55 +02:00
Dany LE
6758a4bb52
Update Jenkinsfile 2022-08-02 09:22:51 +02:00
Dany LE
8d55e6efd4
Update Jenkinsfile 2022-08-02 09:21:16 +02:00
Dany LE
d3f68b8153
Update Jenkinsfile 2022-08-02 09:17:01 +02:00
Dany LE
24de800ee8
Update Jenkinsfile 2022-08-02 09:15:38 +02:00
Dany LE
eb5af36a05
Update Makefile.am 2022-08-02 09:15:08 +02:00
Dany LE
6fcc16fe23
Update Jenkinsfile 2022-08-02 08:42:25 +02:00
Dany LE
65a6813af5
Update README.md 2022-08-01 18:23:04 +02:00
Dany LE
ba6694ab8b
Update README.md 2022-08-01 18:21:19 +02:00
Dany LE
9f1f814ac4
Update Jenkinsfile 2022-08-01 12:14:01 +02:00
Dany LE
e009e6dc20
Update Jenkinsfile 2022-08-01 12:12:14 +02:00
Dany LE
b435495850
Update Jenkinsfile 2022-08-01 12:10:24 +02:00
Dany LE
d2ebccecf0
Update Jenkinsfile 2022-08-01 12:05:02 +02:00
Dany LE
7efaa32245
Update Jenkinsfile 2022-08-01 12:03:14 +02:00
Dany LE
307ff008f9
Update Jenkinsfile 2022-08-01 12:01:13 +02:00
Dany LE
5efad64e9c
Update Jenkinsfile 2022-07-29 15:28:50 +02:00
Dany LE
5f9d8dcf8f
Create Jenkinsfile 2022-07-29 15:27:34 +02:00
Dany LE
9b37f45e5f
Update README.md 2022-07-29 02:00:00 +02:00
lxsang
2d2ac4f1da cleanup unuse functions 2021-10-08 23:35:02 +02:00
lxsang
818142612a regen archive 2021-10-08 23:25:30 +02:00
lxsang
3d4b602884 fix bug on access_time is not update after long body 2021-10-08 23:16:56 +02:00
lxsang
c7e9d2a579 remove debug compiling option 2021-10-08 22:53:08 +02:00
lxsang
0daef95c25 scheduler timeout should be configurable 2021-10-08 22:47:27 +02:00
lxsang
e9cd1addf7 log level should be configured in config file 2021-10-08 22:33:16 +02:00
Xuan Sang LE
e7ec929709
Update README.md 2021-08-29 00:15:20 +02:00
Xuan Sang LE
a31625951b
Update .drone.yml 2021-05-24 17:46:43 +02:00
lxsang
2b457988e1 regen archive 2021-05-07 10:59:10 +02:00
lxsang
cab458b84e increase proxy wait time 2021-03-28 19:24:16 +02:00
lxsang
2d06355b91 reduce proxy data wait time 2021-03-26 09:53:27 +01:00
lxsang
9c1e925e85 change build option 2021-03-25 21:50:23 +01:00
lxsang
786f395139 improve proxy handling 2021-03-25 21:38:09 +01:00
Xuan Sang LE
4a7a5bf632
Update README.md 2021-03-23 18:31:14 +01:00
Xuan Sang LE
6d75aed2ce
Update README.md 2021-03-23 16:53:31 +01:00
Xuan Sang LE
242da4386d
Update README.md 2021-03-23 16:49:17 +01:00
Xuan Sang LE
ce6fd04a16
Update .drone.yml 2021-03-19 10:27:46 +01:00
Xuan Sang LE
5e2f34e75b
Update .drone.yml 2021-03-19 10:21:45 +01:00
Xuan Sang LE
46c1102537
Update .drone.yml 2021-03-19 10:01:31 +01:00
Xuan Sang LE
d32df84f2e
Update README.md 2021-03-10 16:26:28 +01:00
Xuan Sang LE
b28f1a3fa0
Update README.md 2021-03-04 13:48:52 +01:00
Xuan Sang LE
dd7ff9b434
Xle perf improvement (#1)
* alpha version of proxy
* alpha version of proxy
* fix method bug
* fix post deode bug
* regen archive
* Reduce CPU usage on idle in proxy mode:
  - Scheduler timeout event now support millisecond granularity
  - Use only READABLE event on proxy socket which reduce tasks CPU time
* use both readable event and timeout event
* reduce scheduler poll time
* Fix segmentfault error
* Log worker task in statistic
* fix high cpu usage when handling bad POST request
* fix event-stream handling bug on proxy mode
* fix missing data events when uploading
* Race condition when get IP address from host in proxy mode
  - ip_from_hostname() is not thread safe
  - use global lock mechanism
2021-02-24 18:12:36 +01:00
Xuan Sang LE
ce5549d394
Update README.md 2021-02-05 03:44:30 +01:00
Xuan Sang LE
31accd9060
Update README.md 2021-02-05 03:31:06 +01:00
Xuan Sang LE
386146fd9c
Delete ssl_score.png 2021-02-05 03:26:35 +01:00
Xuan Sang LE
e2e4dc0603
Update README.md 2021-02-05 03:25:46 +01:00
Xuan Sang LE
608c2680ed
Update README.md 2021-01-31 13:56:50 +01:00
Xuan Sang LE
8814364690
Update README.md 2021-01-31 13:40:22 +01:00
Xuan Sang LE
23bd7d67fc
Delete forward 2021-01-31 13:30:40 +01:00
Xuan Sang LE
491f288ea5
Update README.md 2021-01-30 22:43:17 +01:00
Xuan Sang LE
50a2d40213
Update .drone.yml 2021-01-27 19:58:14 +01:00
Xuan Sang LE
49ac8cb18b
Update .drone.yml 2021-01-27 08:52:47 +01:00
Xuan Sang LE
a9cf5f0123
Update .drone.yml 2021-01-27 00:44:48 +01:00
Xuan Sang LE
eec7fefb3e
Create .drone.yml 2021-01-27 00:40:51 +01:00
lxsang
be71a6d146 regen release 2021-01-23 16:21:06 +01:00
lxsang
6c4713ac62 remove reverse proxy, it should be handled by the plugin 2021-01-23 16:20:16 +01:00
lxsang
1f143327e4 proxy task is not closed imediately, result in hight CPU usage 2021-01-23 09:54:02 +01:00
lxsang
8b483f0579 fix proxy protocol bug 2021-01-22 13:12:23 +01:00
lxsang
520b99f94b add support to reverse proxy (alpha) 2021-01-22 01:12:26 +01:00
lxsang
5d1998ef1f inactivity timeout set to 60 2021-01-05 18:00:51 +00:00
lxsang
44a33e5413 regen archive 2021-01-03 21:32:17 +01:00
lxsang
08877f84ad use the new scheduler 2021-01-03 21:31:35 +01:00
lxsang
cbbc48d216 improvement scheduler 2021-01-03 11:24:55 +01:00
lxsang
9d19a81a8e fix sha1 include path 2021-01-01 13:48:00 +01:00
lxsang
77f4165fd4 fix sha1 include path 2021-01-01 13:30:46 +01:00
lxsang
7de4bd6c1c fix sha1 include path 2021-01-01 13:30:04 +01:00
lxsang
6408e646e8 add missing header 2021-01-01 13:26:51 +01:00
lxsang
f1a188ca3d add missing header file 2021-01-01 13:21:26 +01:00
lxsang
7e0232f299 fix setsockopt 2020-12-28 13:49:44 +01:00
lxsang
02d48fb659 fix 2020-12-28 12:26:08 +01:00
lxsang
4b6de31e66 improve websocket lib 2020-12-22 16:02:36 +01:00
lxsang
708e492c49 Use blocking select to monitor ports, no need to use a thread for each port 2020-09-23 13:10:54 +02:00
lxsang
9da33124bc allow ordered dictionary 2020-09-22 16:49:43 +02:00
lxsang
1e4856b08a support options method 2020-09-21 20:10:07 +02:00
lxsang
8c11f06ff0 fix scheduler bug: Scheduler dont validate client data as expected 2020-09-17 13:42:28 +02:00
lxsang
69ad08b1a1 fix scheduler statistic take too much cpu at idle 2020-09-13 02:46:24 +02:00
lxsang
b35cd61da4 add statistic log to scheduler, good for debug 2020-09-13 01:29:55 +02:00
lxsang
e38cd9de1b scheduler cause high CPU usage in syslog 2020-08-31 08:24:21 +02:00
lxsang
883ef9c3a3 limit header size 2020-08-27 13:43:27 +02:00
lxsang
5e60eeac02 limit header size 2020-08-27 13:42:58 +02:00
lxsang
806a7ccc6a limit header size 2020-08-27 13:31:40 +02:00
lxsang
56806fb25b add more verbose to log 2020-08-27 12:46:05 +02:00
lxsang
280ad920e9 fix plugin include error 2020-08-25 17:04:17 +02:00
lxsang
776bd017e2 Enhance scheduler, cleanup lib 2020-08-25 16:40:24 +02:00
lxsang
2041ee2ba0 cleanup code 2020-08-19 12:26:17 +02:00
lxsang
5765df3697 optimise cpu usage 2020-08-19 11:08:30 +02:00
lxsang
397145662c add service file 2020-08-08 20:35:21 +02:00
lxsang
adb0811a08 add BST to lib 2020-08-06 18:45:12 +02:00
lxsang
d2a5f3220a Minor fix on configuration read 2020-08-03 18:56:51 +02:00
lxsang
a0445adcc5 user log daemon 2020-02-12 12:56:30 +01:00
lxsang
0f44d62849 add identifier for log filter & regen distrib 2020-02-12 12:27:12 +01:00
lxsang
775b025087 regen distrib. 2020-02-12 12:10:06 +01:00
lxsang
d5682fe186 user syslog for logging 2020-02-12 12:08:36 +01:00
lxsang
357b45aee9 gen distrib 2020-01-11 23:39:37 +01:00
lxsang
7fc12a72e6 add protocol selection to ssl, used for http2 support in the future 2020-01-11 22:30:01 +01:00
lxsang
7b274de0db Merge branch 'master' of https://github.com/lxsang/ant-http 2020-01-10 17:32:09 +01:00
lxsang
6f7d028c86 add cache control to static file, remove unthread safe code 2020-01-10 17:31:49 +01:00
Xuan Sang LE
4c6eeef0ac
Update README.md 2020-01-09 14:55:02 +01:00
lxsang
1200d9d990 gen distrib 2020-01-09 14:26:55 +01:00
lxsang
75df72e10a add header verify 2020-01-09 12:18:59 +01:00
lxsang
1af2945af0 cleanup 2020-01-08 22:35:58 +01:00
lxsang
189120bd88 allow copress body with zlib 2020-01-08 22:04:10 +01:00
lxsang
4608ae874f fix 2020-01-08 19:17:51 +01:00
lxsang
cb98562585 alow a plugin to require other plugin 2020-01-03 10:14:04 +01:00
lxsang
876f60c9a2 fix serve_file problem 2020-01-02 11:54:09 +01:00
lxsang
909d1f10e3 fix 2020-01-01 22:10:05 +01:00
lxsang
d5c4461d34 fix ld bug 2020-01-01 21:21:05 +01:00
lxsang
b3609ea014 remove compile warning 2020-01-01 13:14:50 +01:00
lxsang
91d84baf01 fix list 2019-12-22 17:33:35 +01:00
lxsang
c429a7dd66 remove strdup calles 2019-12-22 14:33:42 +01:00
lxsang
ea07981160 add websocket client to the library 2019-12-22 00:13:24 +01:00
lxsang
4fb4674045 add websocket client to the library 2019-12-22 00:11:26 +01:00
lxsang
4c9808da78 fix websocket plen 2019-12-21 17:38:20 +01:00
lxsang
d391b78109 add timeout to io read write 2019-12-21 15:02:33 +01:00
lxsang
50b248c9e2 fix r/w wo ssl 2019-12-21 00:22:45 +01:00
lxsang
d01e5e13aa increte wait time 2019-12-21 00:00:53 +01:00
lxsang
f0ecc62162 rules now work on port 2019-12-20 19:26:45 +01:00
lxsang
85cfce134b fix typo 2019-12-20 16:51:10 +01:00
lxsang
3435733ac2 add multiport support 2019-12-20 16:49:41 +01:00
lxsang
d04d29fe6b stable version 2019-12-19 11:01:48 +01:00
lxsang
2562c06f31 fix write 2019-12-19 09:58:19 +01:00
lxsang
348dd45385 fix write 2019-12-19 09:52:56 +01:00
lxsang
457eefee25 zzzz 2019-12-15 18:32:43 +01:00
lxsang
b093bd0194 zzzz 2019-12-15 18:23:01 +01:00
lxsang
63c65e0d46 fix 2019-12-15 17:13:25 +01:00
lxsang
4e75706e22 fix looping rw 2019-12-15 16:38:58 +01:00
lxsang
e98970b378 remove err 2019-12-15 16:24:15 +01:00
lxsang
98feff09d6 fix 2019-12-15 15:40:23 +01:00
lxsang
9b68d1d327 use static const constant string 2019-12-15 15:04:37 +01:00
lxsang
8d2449bd69 new api 2019-12-15 12:10:06 +01:00
lxsang
1e2e600bfc fix missing symbol 2019-12-13 13:42:39 +01:00
lxsang
079f96de66 add ssl debug info 2019-12-13 09:52:28 +01:00
lxsang
3f2af063aa fix dict size 2019-12-12 18:35:00 +01:00
lxsang
fabe2e6e2a fix logging bug 2019-12-12 10:37:26 +01:00
lxsang
e05515a537 change log mechanism 2019-12-11 22:17:42 +00:00
lxsang
2dc6d09413 add clen 2019-12-04 18:18:48 +00:00
lxsang
5d795ce4c3 update 2019-11-30 19:40:40 +00:00
lxsang
f48321e1a1 modify dist 2019-11-24 14:32:08 +01:00
lxsang
afc0b31184 fix length bug 2019-11-24 14:30:03 +01:00
Xuan Sang LE
903fdfa483
Update .travis.yml 2019-11-20 09:00:41 +01:00
Xuan Sang LE
bac36497e2
Update .travis.yml 2019-11-20 08:58:45 +01:00
Xuan Sang LE
1a8423b120
Update .travis.yml 2019-11-20 08:56:33 +01:00
Xuan Sang LE
6bd5c473b9
Update README.md 2019-11-19 17:48:59 +01:00
lxsang
cfcadffa1f fix lib linking error 2019-11-19 17:07:04 +01:00
lxsang
b07cffd28e fix -O flag 2019-11-18 14:07:43 +01:00
lxsang
94ab53febf minor fix 2019-11-18 11:13:30 +01:00
lxsang
adc898d5d8 fix dlopen 2019-11-14 18:48:44 +01:00
lxsang
97228be9e6 fix readme 2019-11-13 17:00:11 +01:00
lxsang
b82e992ef2 fix config 2019-11-13 15:39:42 +01:00
lxsang
01722ae39c add config files 2019-11-13 14:22:25 +01:00
lxsang
296fae73c3 add dist 2019-11-13 13:40:52 +01:00
lxsang
91a45fab24 add distribution 2019-11-13 13:31:10 +01:00
lxsang
0d9e536e55 missing header 2019-11-13 13:25:35 +01:00
lxsang
1305c6268e add Makefile.am 2019-11-13 13:21:04 +01:00
lxsang
985597cb3c remove generated files 2019-11-13 13:17:25 +01:00
lxsang
476db8ecee minor fix 2019-11-13 13:05:04 +01:00
lxsang
3513b562f5 remove generated files 2019-11-13 11:54:09 +01:00
lxsang
424db8dd6e switch to autotools 2019-11-13 11:48:54 +01:00
70 changed files with 7174 additions and 5278 deletions

17
.gitea/workflows/ci.yml Normal file
View 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
View File

@ -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~

View File

@ -1,7 +0,0 @@
language: c
before_install:
- sudo apt-get -qq update
- sudo apt-get install libssl-dev
script:
- make initd
- make

View File

@ -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
View 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

View File

@ -1,18 +1,20 @@
![Logo](https://github.com/lxsang/ant-http/raw/master/ant-logo.png)
# ant-http
[![Build Status](https://travis-ci.org/lxsang/ant-http.svg?branch=master)](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
![SSL score](https://github.com/lxsang/ant-http/raw/master/ssl_score.png)
- 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
```

2
ant-d Executable file
View File

@ -0,0 +1,2 @@
#! /bin/bash
/usr/bin/antd /opt/www/config.ini

8
antd
View File

@ -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
View 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
View 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
View 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
View 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
View 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

View File

@ -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
View File

@ -0,0 +1,103 @@
# initialise autoconf and set up some basic information about the program were packaging
AC_INIT([antd], [2.0.0], [xsang.le@gmail.com])
# Were 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
View 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
View 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

Binary file not shown.

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

Binary file not shown.

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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
View File

@ -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
View 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
View 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
View 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);
}

View File

@ -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

File diff suppressed because it is too large Load Diff

99
lib/handle.h Normal file
View 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

View File

@ -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
View 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;
}

View File

@ -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
View 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
View 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
View 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
View 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
View 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

View File

@ -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]);

View File

@ -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
View 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(&regex, expr, REG_ICASE | REG_EXTENDED);
if (reti)
{
// ERROR("Could not compile regex: %s",expr);
regerror(reti, &regex, msgbuf, sizeof(msgbuf));
ERROR("Regex match failed: %s", msgbuf);
// return 0;
}
/* Execute regular expression */
reti = regexec(&regex, search, msize, matches, 0);
if (!reti)
{
// LOG("Match");
ret = 1;
}
else if (reti == REG_NOMATCH)
{
// LOG("No match");
ret = 0;
}
else
{
regerror(reti, &regex, msgbuf, sizeof(msgbuf));
// ERROR("Regex match failed: %s\n", msgbuf);
ret = 0;
}
regfree(&regex);
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);
}

View File

@ -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
View 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
View 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

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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");
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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(&regex, expr, REG_ICASE | REG_EXTENDED);
if( reti ){
LOG("Could not compile regex: %s\n",expr);
regerror(reti, &regex, msgbuf, sizeof(msgbuf));
LOG("Regex match failed: %s\n", msgbuf);
return 0;
}
/* Execute regular expression */
reti = regexec(&regex, search, msize, matches, 0);
if( !reti ){
//LOG("Match");
ret = 1;
}
else if( reti == REG_NOMATCH ){
//LOG("No match");
ret = 0;
}
else{
regerror(reti, &regex, msgbuf, sizeof(msgbuf));
LOG("Regex match failed: %s\n", msgbuf);
ret = 0;
}
regfree(&regex);
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
View File

@ -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";
}

View File

@ -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

View File

@ -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;
}

View File

@ -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
View File

@ -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
View 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
View 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

51
var.mk
View File

@ -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)