Merge tag 'upstream/2.0.0'
Upstream version 2.0.0
Shih-Yuan Lee (FourDollars)
7 years ago
0 | language: c | |
1 | sudo: false | |
2 | compiler: | |
3 | - clang | |
4 | - gcc | |
5 | addons: | |
6 | apt: | |
7 | packages: | |
8 | - libssl-dev | |
9 | script: | |
10 | - make |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
1 | 1 | |
2 | 2 | CC?=gcc |
3 | 3 | PREFIX?=/usr/local |
4 | OBJDIR?=obj | |
4 | 5 | KORE=kore |
5 | 6 | INSTALL_DIR=$(PREFIX)/bin |
6 | 7 | INCLUDE_DIR=$(PREFIX)/include/kore |
7 | 8 | |
8 | S_SRC= src/kore.c src/accesslog.c src/auth.c src/buf.c src/cli.c src/config.c \ | |
9 | src/connection.c src/domain.c src/http.c src/mem.c src/module.c \ | |
10 | src/net.c src/pool.c src/spdy.c src/timer.c src/validator.c \ | |
11 | src/utils.c src/websocket.c src/worker.c src/zlib_dict.c | |
12 | S_OBJS= $(S_SRC:.c=.o) | |
9 | S_SRC= src/kore.c src/buf.c src/cli.c src/config.c src/connection.c \ | |
10 | src/domain.c src/mem.c src/msg.c src/module.c src/net.c \ | |
11 | src/pool.c src/timer.c src/utils.c src/worker.c src/keymgr.c | |
13 | 12 | |
14 | CFLAGS+=-Wall -Wstrict-prototypes -Wmissing-prototypes | |
13 | CFLAGS+=-Wall -Werror -Wstrict-prototypes -Wmissing-prototypes | |
15 | 14 | CFLAGS+=-Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-qual |
16 | CFLAGS+=-Wsign-compare -Iincludes -g | |
15 | CFLAGS+=-Wsign-compare -Iincludes -std=c99 -pedantic | |
17 | 16 | CFLAGS+=-DPREFIX='"$(PREFIX)"' |
18 | LDFLAGS+=-rdynamic -lssl -lcrypto -lz | |
17 | LDFLAGS=-rdynamic -lssl -lcrypto | |
19 | 18 | |
20 | 19 | ifneq ("$(DEBUG)", "") |
21 | CFLAGS+=-DKORE_DEBUG | |
20 | CFLAGS+=-DKORE_DEBUG -g | |
21 | NOOPT=1 | |
22 | 22 | endif |
23 | 23 | |
24 | ifneq ("$(KORE_PEDANTIC_MALLOC)", "") | |
25 | CFLAGS+=-DKORE_PEDANTIC_MALLOC | |
24 | ifneq ("$(NOOPT)", "") | |
25 | CFLAGS+=-O0 | |
26 | else | |
27 | CFLAGS+=-O2 | |
26 | 28 | endif |
27 | 29 | |
28 | ifneq ("$(BENCHMARK)", "") | |
29 | CFLAGS+=-DKORE_BENCHMARK | |
30 | LDFLAGS=-rdynamic -lz -lcrypto | |
30 | ifneq ("$(NOHTTP)", "") | |
31 | CFLAGS+=-DKORE_NO_HTTP | |
32 | else | |
33 | S_SRC+= src/auth.c src/accesslog.c src/http.c \ | |
34 | src/validator.c src/websocket.c | |
35 | endif | |
36 | ||
37 | ifneq ("$(NOTLS)", "") | |
38 | CFLAGS+=-DKORE_NO_TLS | |
39 | ifneq ("$(NOHTTP)", "") | |
40 | LDFLAGS=-rdynamic | |
41 | else | |
42 | LDFLAGS=-rdynamic -lcrypto | |
43 | endif | |
31 | 44 | endif |
32 | 45 | |
33 | 46 | ifneq ("$(PGSQL)", "") |
43 | 56 | CFLAGS+=-DKORE_USE_TASKS |
44 | 57 | endif |
45 | 58 | |
59 | ifneq ("$(JSONRPC)", "") | |
60 | S_SRC+=src/jsonrpc.c | |
61 | LDFLAGS+=-lyajl | |
62 | CFLAGS+=-DKORE_USE_JSONRPC | |
63 | endif | |
64 | ||
46 | 65 | OSNAME=$(shell uname -s | sed -e 's/[-_].*//g' | tr A-Z a-z) |
47 | 66 | ifeq ("$(OSNAME)", "darwin") |
48 | 67 | CFLAGS+=-I/opt/local/include/ -I/usr/local/opt/openssl/include |
54 | 73 | S_SRC+=src/linux.c |
55 | 74 | else |
56 | 75 | S_SRC+=src/bsd.c |
76 | ifneq ("$(JSONRPC)", "") | |
77 | CFLAGS+=-I/usr/local/include | |
78 | LDFLAGS+=-L/usr/local/lib | |
79 | endif | |
57 | 80 | endif |
58 | 81 | |
59 | all: $(S_OBJS) | |
82 | S_OBJS= $(S_SRC:src/%.c=$(OBJDIR)/%.o) | |
83 | ||
84 | $(KORE): $(OBJDIR) $(S_OBJS) | |
60 | 85 | $(CC) $(S_OBJS) $(LDFLAGS) -o $(KORE) |
86 | ||
87 | objects: $(OBJDIR) $(S_OBJS) | |
88 | ||
89 | all: $(KORE) | |
90 | ||
91 | $(OBJDIR): | |
92 | @mkdir -p $(OBJDIR) | |
61 | 93 | |
62 | 94 | install: |
63 | 95 | mkdir -p $(INCLUDE_DIR) |
69 | 101 | rm -f $(INSTALL_DIR)/$(KORE) |
70 | 102 | rm -rf $(INCLUDE_DIR) |
71 | 103 | |
72 | .c.o: | |
104 | $(OBJDIR)/%.o: src/%.c | |
73 | 105 | $(CC) $(CFLAGS) -c $< -o $@ |
74 | 106 | |
75 | 107 | clean: |
76 | 108 | find . -type f -name \*.o -exec rm {} \; |
77 | rm -f $(KORE) | |
109 | rm -rf $(KORE) $(OBJDIR) | |
78 | 110 | |
79 | .PHONY: clean | |
111 | .PHONY: all clean |
10 | 10 | Features |
11 | 11 | -------- |
12 | 12 | * Supports SNI |
13 | * Supports SPDY/3.1 | |
14 | 13 | * Supports HTTP/1.1 |
15 | 14 | * Websocket support |
15 | * Privseps by default | |
16 | 16 | * Lightweight background tasks |
17 | 17 | * Built-in parameter validation |
18 | 18 | * Only HTTPS connections allowed |
19 | * Multiple modules can be loaded at once | |
20 | 19 | * Built-in asynchronous PostgreSQL support |
20 | * Private keys isolated in separate process (RSA and ECDSA) | |
21 | 21 | * Default sane TLS ciphersuites (PFS in all major browsers) |
22 | * Load your web application as a precompiled dynamic library | |
23 | 22 | * Modules can be reloaded on-the-fly, even while serving content |
24 | 23 | * Event driven (epoll/kqueue) architecture with per CPU core workers |
24 | * Build your web application as a precompiled dynamic library or single binary | |
25 | 25 | |
26 | 26 | License |
27 | 27 | ------- |
36 | 36 | |
37 | 37 | See https://kore.io/doc/#requirements for more information. |
38 | 38 | |
39 | Latest release | |
40 | -------------- | |
41 | * [2015-05-21] version 1.2.3 - https://kore.io/release/kore-1.2.3-release.tgz | |
42 | ||
43 | Old releases | |
44 | ------------ | |
45 | * [2015-04-09] version 1.2.2 - https://kore.io/release/kore-1.2.2-release.tgz | |
46 | * [2014-12-12] version 1.2.1 - https://kore.io/release/kore-1.2.1-release.tgz | |
47 | * [2014-08-25] version 1.2 - https://kore.io/release/kore-1.2-stable.tgz | |
48 | * [2014-03-01] version 1.1 - https://kore.io/release/kore-1.1-stable.tgz | |
49 | ||
50 | 39 | Building Kore |
51 | 40 | ------------- |
52 | 41 | |
53 | 42 | Requirements |
54 | * libz | |
55 | * openssl >= 1.0.1i | |
43 | * openssl (latest) | |
44 | (note: this requirement drops away when building with NOTLS=1 NOHTTP=1) | |
56 | 45 | |
57 | 46 | Requirements for background tasks (optional) |
58 | 47 | * pthreads |
63 | 52 | Normal compilation and installation: |
64 | 53 | |
65 | 54 | ``` |
66 | # git clone https://github.com/jorisvink/kore.git | |
67 | 55 | # cd kore |
68 | 56 | # make |
69 | 57 | # make install |
75 | 63 | * TASKS=1 (compiles in task support) |
76 | 64 | * PGSQL=1 (compiles in pgsql support) |
77 | 65 | * DEBUG=1 (enables use of -d for debug) |
78 | * BENCHMARK=1 (compiles Kore without OpenSSL) | |
79 | * KORE_PEDANTIC_MALLOC=1 (zero all allocated memory) | |
66 | * NOTLS=1 (compiles Kore without TLS) | |
67 | * NOHTTP=1 (compiles Kore without HTTP support) | |
68 | * NOOPT=1 (disable compiler optimizations) | |
69 | * JSONRPC=1 (compiles in JSONRPC support) | |
80 | 70 | |
81 | Example libraries | |
71 | Example applications | |
82 | 72 | ----------------- |
83 | 73 | |
84 | You can find example libraries under **_examples/_**. | |
74 | You can find example applications under **_examples/_**. | |
85 | 75 | |
86 | 76 | The examples contain a README file with instructions on how |
87 | 77 | to build or use them. |
88 | ||
89 | I apologize for unclear examples or documentation, I am working on | |
90 | improving those. | |
91 | 78 | |
92 | 79 | Bugs, contributions and more |
93 | 80 | ---------------------------- |
95 | 82 | If you run into any bugs, have suggestions or patches please |
96 | 83 | contact me at joris@coders.se. |
97 | 84 | |
85 | If you feel like hanging out or just chatting there is an [IRC chatroom (#kore-dev@irc.freenode.org)](https://webchat.freenode.net?channels=kore-dev). | |
86 | ||
98 | 87 | More information can be found on https://kore.io/ |
0 | This is the official release for Kore 1.2.3. | |
0 | This is the official release for Kore 2.0.0. | |
1 | 1 | |
2 | 2 | If you want the current version, use git to clone the following: |
3 | https://github.com/jorisvink/kore/ | |
3 | https://github.com/jorisvink/kore/ | |
4 | 4 | |
5 | 5 | See the README file for more information. |
17 | 17 | # Worker processes will run as the specified user. |
18 | 18 | runas joris |
19 | 19 | |
20 | # Set workers to the amount of CPU's available in your system, | |
21 | # kore will automatically distribute all workers on them. | |
20 | # How many worker processes Kore will spawn. If the directive | |
21 | # worker_set_affinity is set to 1 (the default) Kore will automatically | |
22 | # pin these worker processes to different CPU cores in your system. | |
23 | # NOTE: If you set this to the maximum number of cores you have | |
24 | # in your system (or more) you might consider turning off affinity | |
25 | # if you are running CPU heavy services on the same machine. | |
22 | 26 | workers 4 |
23 | 27 | |
24 | 28 | # The number of active connections each worker can handle. |
29 | 33 | #worker_rlimit_nofiles 1024 |
30 | 34 | |
31 | 35 | # Limit the number of new connections a worker can accept |
32 | # in a single event loop. | |
33 | # NOTE: This can have a *MASSIVE* impact as this controls | |
34 | # how new connections are spread across worker processes. | |
36 | # in a single event loop. By default Kore will accept as | |
37 | # many new connections it can up to worker_max_connections. | |
38 | # | |
39 | # NOTE: If you are running benchmark tools that throw all | |
40 | # connections at Kore at the same time (when they are less | |
41 | # then worker_max_connections) or you have an actual reason | |
42 | # to not spend too much time in the accept loop this setting | |
43 | # will make a HUGE positive difference. | |
35 | 44 | # |
36 | 45 | # This is disabled by default. If you wish to enable this |
37 | 46 | # specify the number of connections a worker will accept |
49 | 58 | # http_header_max Maximum size of HTTP headers (in bytes). |
50 | 59 | # |
51 | 60 | # http_body_max Maximum size of an HTTP body (in bytes). |
61 | # If set to 0 disallows requests with a body | |
62 | # all together. | |
63 | # | |
64 | # http_body_disk_offload Number of bytes after which Kore will use | |
65 | # a temporary file to hold the HTTP body | |
66 | # instead of holding it in memory. If set to | |
67 | # 0 no disk offloading will be done. This is | |
68 | # turned off by default. | |
69 | # | |
70 | # http_body_disk_path Path where Kore will store any temporary | |
71 | # HTTP body files. | |
52 | 72 | # |
53 | 73 | # http_keepalive_time Maximum seconds an HTTP connection can be |
54 | 74 | # kept alive by the browser. |
61 | 81 | # http_request_limit Limit the number of requests Kore processes |
62 | 82 | # in a single event loop. |
63 | 83 | #http_header_max 4096 |
64 | #http_body_max 10240000 | |
84 | #http_body_max 1024000 | |
65 | 85 | #http_keepalive_time 0 |
66 | 86 | #http_hsts_enable 31536000 |
67 | 87 | #http_request_limit 1000 |
88 | #http_body_disk_offload 0 | |
89 | #http_body_disk_path tmp_files | |
68 | 90 | |
69 | 91 | # Websocket specific settings. |
70 | 92 | # websocket_maxframe Specifies the maximum frame size we can receive |
73 | 95 | #websocket_maxframe 16384 |
74 | 96 | #websocket_timeout 120 |
75 | 97 | |
98 | # Configure the number of available threads for background tasks. | |
99 | #task_threads 2 | |
100 | ||
76 | 101 | # Load modules (you can load multiple at the same time). |
77 | 102 | # An additional parameter can be specified as the "onload" function |
78 | 103 | # which Kore will call when the module is loaded/reloaded. |
96 | 121 | # Specify the TLS ciphers that will be used. |
97 | 122 | #tls_cipher ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!kRSA:!kDSA |
98 | 123 | |
99 | # If you wish to use EDH / ECDH specify a file containing | |
100 | # a generated DH key (See OpenSSL dhparam). | |
124 | # Required DH parameters for TLS. | |
101 | 125 | #tls_dhparam dh2048.pem |
102 | ||
103 | # Specify the amount of seconds a SPDY connection is kept open. | |
104 | # You can keep it open indefinitely by setting this to 0. | |
105 | #spdy_idle_time 120 | |
106 | 126 | |
107 | 127 | # Authentication configuration |
108 | 128 | # |
183 | 203 | static / serve_index |
184 | 204 | static /intro.jpg serve_intro |
185 | 205 | static /b64test serve_b64test |
186 | static /spdy-reset serve_spdyreset | |
187 | 206 | static /upload serve_file_upload |
188 | 207 | static /lock-test serve_lock_test |
189 | 208 | static /validator serve_validator |
0 | all: build app | |
1 | ||
2 | build: | |
3 | docker build -t kore-build -f build/Dockerfile build/ | |
4 | docker tag kore-build kore/build | |
5 | ||
6 | app: | |
7 | docker build -t kore-app -f app/Dockerfile app/ | |
8 | docker tag kore-app kore/app | |
9 | ||
10 | clean: | |
11 | docker rmi -f kore-build kore-app | |
12 | ||
13 | .PHONY: all build app clean |
0 | FROM kore/build | |
1 | MAINTAINER Thordur I. Bjornsson <thorduri@secnorth.net> | |
2 | ||
3 | ENV KORE_VERSION 1.2.3 | |
4 | ||
5 | WORKDIR /kore | |
6 | ||
7 | ADD https://github.com/jorisvink/kore/archive/$KORE_VERSION-release.tar.gz /kore/ | |
8 | RUN tar -zxf $KORE_VERSION-release.tar.gz && cd kore-$KORE_VERSION-release/ && \ | |
9 | make clean && make && make install && \ | |
10 | rm -rf /kore/$KORE_VERSION-release.tar.gz /kore/kore-$KORE_VERSION-release | |
11 | ||
12 | EXPOSE 443 8888 | |
13 | ENTRYPOINT ["kore", "run"] |
0 | FROM debian:jessie | |
1 | MAINTAINER Thordur I. Bjornsson <thorduri@secnorth.net> | |
2 | ||
3 | RUN apt-get update -qq && \ | |
4 | apt-get -qqy install build-essential clang git libpq-dev libssl-dev libz-dev |
0 | # cpp build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | cxxflags=-Wall -Wmissing-declarations -Wshadow | |
9 | cxxflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
10 | ||
11 | dev { | |
12 | # These cflags are added to the shared ones when | |
13 | # you build the "dev" flavor. | |
14 | cflags=-g | |
15 | cxxflags=-g | |
16 | } | |
17 | ||
18 | #prod { | |
19 | # You can specify additional CFLAGS here which are only | |
20 | # included if you build with the "prod" flavor. | |
21 | #} |
0 | # generic build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
3 | 3 | load ./generic.so example_load |
4 | 4 | |
5 | 5 | tls_dhparam dh2048.pem |
6 | ||
7 | http_body_max 1024000000 | |
8 | http_body_disk_offload 1024000 | |
6 | 9 | |
7 | 10 | validator v_example function v_example_func |
8 | 11 | validator v_regex regex ^/test/[a-z]*$ |
25 | 28 | static / serve_index |
26 | 29 | static /intro.jpg serve_intro |
27 | 30 | static /b64test serve_b64test |
28 | static /spdy-reset serve_spdyreset | |
29 | 31 | static /upload serve_file_upload |
30 | 32 | static /validator serve_validator |
31 | 33 | static /params-test serve_params_test |
16 | 16 | #include <kore/kore.h> |
17 | 17 | #include <kore/http.h> |
18 | 18 | |
19 | #include <openssl/sha.h> | |
20 | ||
21 | #include <fcntl.h> | |
22 | #include <stdlib.h> | |
23 | #include <unistd.h> | |
24 | ||
19 | 25 | #include "assets.h" |
20 | 26 | |
21 | 27 | int example_load(int); |
24 | 30 | int serve_index(struct http_request *); |
25 | 31 | int serve_intro(struct http_request *); |
26 | 32 | int serve_b64test(struct http_request *); |
27 | int serve_spdyreset(struct http_request *); | |
28 | 33 | int serve_file_upload(struct http_request *); |
29 | 34 | int serve_validator(struct http_request *); |
30 | 35 | int serve_params_test(struct http_request *); |
73 | 78 | tstamp = 0; |
74 | 79 | if (http_request_header(req, "if-modified-since", &date)) { |
75 | 80 | tstamp = kore_date_to_time(date); |
76 | kore_mem_free(date); | |
77 | ||
78 | 81 | kore_debug("header was present with %ld", tstamp); |
79 | 82 | } |
80 | 83 | |
114 | 117 | serve_b64test(struct http_request *req) |
115 | 118 | { |
116 | 119 | int i; |
117 | u_int32_t len; | |
120 | size_t len; | |
118 | 121 | struct kore_buf *res; |
119 | 122 | u_int8_t *data; |
120 | 123 | |
121 | res = kore_buf_create(1024); | |
124 | res = kore_buf_alloc(1024); | |
122 | 125 | for (i = 0; b64tests[i] != NULL; i++) |
123 | 126 | test_base64((u_int8_t *)b64tests[i], strlen(b64tests[i]), res); |
124 | 127 | |
126 | 129 | |
127 | 130 | http_response_header(req, "content-type", "text/plain"); |
128 | 131 | http_response(req, 200, data, len); |
129 | kore_mem_free(data); | |
130 | ||
131 | return (KORE_RESULT_OK); | |
132 | } | |
133 | ||
134 | int | |
135 | serve_spdyreset(struct http_request *req) | |
136 | { | |
137 | spdy_session_teardown(req->owner, SPDY_SESSION_ERROR_OK); | |
132 | kore_free(data); | |
133 | ||
138 | 134 | return (KORE_RESULT_OK); |
139 | 135 | } |
140 | 136 | |
141 | 137 | int |
142 | 138 | serve_file_upload(struct http_request *req) |
143 | 139 | { |
144 | int r; | |
145 | 140 | u_int8_t *d; |
146 | 141 | struct kore_buf *b; |
147 | u_int32_t len; | |
142 | struct http_file *f; | |
143 | size_t len; | |
148 | 144 | char *name, buf[BUFSIZ]; |
149 | 145 | |
150 | b = kore_buf_create(asset_len_upload_html); | |
146 | b = kore_buf_alloc(asset_len_upload_html); | |
151 | 147 | kore_buf_append(b, asset_upload_html, asset_len_upload_html); |
152 | 148 | |
153 | 149 | if (req->method == HTTP_METHOD_POST) { |
154 | http_populate_multipart_form(req, &r); | |
155 | if (http_argument_get_string("firstname", &name, &len)) { | |
156 | kore_buf_replace_string(b, "$firstname$", name, len); | |
150 | if (req->http_body_fd != -1) | |
151 | kore_log(LOG_NOTICE, "file is on disk"); | |
152 | ||
153 | http_populate_multipart_form(req); | |
154 | if (http_argument_get_string(req, "firstname", &name)) { | |
155 | kore_buf_replace_string(b, "$firstname$", | |
156 | name, strlen(name)); | |
157 | 157 | } else { |
158 | 158 | kore_buf_replace_string(b, "$firstname$", NULL, 0); |
159 | 159 | } |
160 | 160 | |
161 | if (http_file_lookup(req, "file", &name, &d, &len)) { | |
161 | if ((f = http_file_lookup(req, "file")) != NULL) { | |
162 | 162 | (void)snprintf(buf, sizeof(buf), |
163 | "%s is %d bytes", name, len); | |
163 | "%s is %ld bytes", f->filename, f->length); | |
164 | 164 | kore_buf_replace_string(b, |
165 | 165 | "$upload$", buf, strlen(buf)); |
166 | 166 | } else { |
175 | 175 | |
176 | 176 | http_response_header(req, "content-type", "text/html"); |
177 | 177 | http_response(req, 200, d, len); |
178 | kore_mem_free(d); | |
178 | kore_free(d); | |
179 | 179 | |
180 | 180 | return (KORE_RESULT_OK); |
181 | 181 | } |
184 | 184 | test_base64(u_int8_t *src, u_int32_t slen, struct kore_buf *res) |
185 | 185 | { |
186 | 186 | char *in; |
187 | u_int32_t len; | |
187 | size_t len; | |
188 | 188 | u_int8_t *out; |
189 | 189 | |
190 | 190 | kore_buf_appendf(res, "test '%s'\n", src); |
200 | 200 | kore_buf_appendf(res, "decoded: "); |
201 | 201 | kore_buf_append(res, out, len); |
202 | 202 | kore_buf_appendf(res, "\n"); |
203 | kore_mem_free(out); | |
203 | kore_free(out); | |
204 | 204 | } |
205 | 205 | |
206 | kore_mem_free(in); | |
206 | kore_free(in); | |
207 | 207 | } |
208 | 208 | |
209 | 209 | kore_buf_appendf(res, "\n"); |
237 | 237 | { |
238 | 238 | struct kore_buf *b; |
239 | 239 | u_int8_t *d; |
240 | u_int32_t len; | |
240 | size_t len; | |
241 | 241 | int r, i; |
242 | 242 | char *test, name[10]; |
243 | 243 | |
244 | http_populate_arguments(req); | |
245 | ||
246 | b = kore_buf_create(asset_len_params_html); | |
244 | if (req->method == HTTP_METHOD_GET) | |
245 | http_populate_get(req); | |
246 | else if (req->method == HTTP_METHOD_POST) | |
247 | http_populate_post(req); | |
248 | ||
249 | b = kore_buf_alloc(asset_len_params_html); | |
247 | 250 | kore_buf_append(b, asset_params_html, asset_len_params_html); |
248 | 251 | |
249 | 252 | /* |
250 | 253 | * The GET parameters will be filtered out on POST. |
251 | 254 | */ |
252 | if (http_argument_get_string("arg1", &test, &len)) { | |
253 | kore_buf_replace_string(b, "$arg1$", test, len); | |
255 | if (http_argument_get_string(req, "arg1", &test)) { | |
256 | kore_buf_replace_string(b, "$arg1$", test, strlen(test)); | |
254 | 257 | } else { |
255 | 258 | kore_buf_replace_string(b, "$arg1$", NULL, 0); |
256 | 259 | } |
257 | 260 | |
258 | if (http_argument_get_string("arg2", &test, &len)) { | |
259 | kore_buf_replace_string(b, "$arg2$", test, len); | |
261 | if (http_argument_get_string(req, "arg2", &test)) { | |
262 | kore_buf_replace_string(b, "$arg2$", test, strlen(test)); | |
260 | 263 | } else { |
261 | 264 | kore_buf_replace_string(b, "$arg2$", NULL, 0); |
262 | 265 | } |
266 | 269 | kore_buf_replace_string(b, "$test2$", NULL, 0); |
267 | 270 | kore_buf_replace_string(b, "$test3$", NULL, 0); |
268 | 271 | |
269 | if (http_argument_get_uint16("id", &r)) | |
272 | if (http_argument_get_uint16(req, "id", &r)) | |
270 | 273 | kore_log(LOG_NOTICE, "id: %d", r); |
271 | 274 | else |
272 | 275 | kore_log(LOG_NOTICE, "No id set"); |
274 | 277 | http_response_header(req, "content-type", "text/html"); |
275 | 278 | d = kore_buf_release(b, &len); |
276 | 279 | http_response(req, 200, d, len); |
277 | kore_mem_free(d); | |
280 | kore_free(d); | |
278 | 281 | |
279 | 282 | return (KORE_RESULT_OK); |
280 | 283 | } |
281 | 284 | |
282 | 285 | for (i = 1; i < 4; i++) { |
283 | 286 | (void)snprintf(name, sizeof(name), "test%d", i); |
284 | if (http_argument_get_string(name, &test, &len)) { | |
287 | if (http_argument_get_string(req, name, &test)) { | |
285 | 288 | (void)snprintf(name, sizeof(name), "$test%d$", i); |
286 | kore_buf_replace_string(b, name, test, len); | |
289 | kore_buf_replace_string(b, name, test, strlen(test)); | |
287 | 290 | } else { |
288 | 291 | (void)snprintf(name, sizeof(name), "$test%d$", i); |
289 | 292 | kore_buf_replace_string(b, name, NULL, 0); |
293 | 296 | http_response_header(req, "content-type", "text/html"); |
294 | 297 | d = kore_buf_release(b, &len); |
295 | 298 | http_response(req, 200, d, len); |
296 | kore_mem_free(d); | |
299 | kore_free(d); | |
297 | 300 | |
298 | 301 | return (KORE_RESULT_OK); |
299 | 302 | } |
0 | # headers build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
11 | 11 | * We'll lookup if the X-Custom-Header is given in the request. |
12 | 12 | * If it is we'll set it as a response header as well. |
13 | 13 | * |
14 | * The value returned by http_request_header() must be freed. | |
15 | * | |
16 | * NOTE: All custom headers you set must be in lower case due to | |
17 | * the SPDYv3 specification requiring this. | |
14 | * The value returned by http_request_header() should not be freed. | |
18 | 15 | */ |
19 | if (http_request_header(req, "x-custom-header", &custom)) { | |
16 | if (http_request_header(req, "x-custom-header", &custom)) | |
20 | 17 | http_response_header(req, "x-custom-header", custom); |
21 | kore_mem_free(custom); | |
22 | } | |
23 | 18 | |
24 | 19 | /* Return 200 with "ok\n" to the client. */ |
25 | 20 | http_response(req, 200, "ok\n", 3); |
0 | # integers build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
10 | 10 | int32_t s32; |
11 | 11 | int64_t s64; |
12 | 12 | u_int64_t u64; |
13 | u_int32_t u32; | |
14 | size_t len; | |
13 | 15 | struct kore_buf *buf; |
14 | u_int32_t u32, len; | |
15 | 16 | u_int8_t c, *data; |
16 | 17 | |
17 | http_populate_arguments(req); | |
18 | buf = kore_buf_create(128); | |
18 | http_populate_get(req); | |
19 | buf = kore_buf_alloc(128); | |
19 | 20 | |
20 | if (http_argument_get_byte("id", &c)) | |
21 | if (http_argument_get_byte(req, "id", &c)) | |
21 | 22 | kore_buf_appendf(buf, "byte\t%c\n", c); |
22 | 23 | |
23 | if (http_argument_get_int16("id", &s16)) | |
24 | if (http_argument_get_int16(req, "id", &s16)) | |
24 | 25 | kore_buf_appendf(buf, "int16\t%d\n", s16); |
25 | 26 | |
26 | if (http_argument_get_uint16("id", &u16)) | |
27 | if (http_argument_get_uint16(req, "id", &u16)) | |
27 | 28 | kore_buf_appendf(buf, "uint16\t%d\n", u16); |
28 | 29 | |
29 | if (http_argument_get_int32("id", &s32)) | |
30 | if (http_argument_get_int32(req, "id", &s32)) | |
30 | 31 | kore_buf_appendf(buf, "int32\t%d\n", s32); |
31 | 32 | |
32 | if (http_argument_get_uint32("id", &u32)) | |
33 | if (http_argument_get_uint32(req, "id", &u32)) | |
33 | 34 | kore_buf_appendf(buf, "uint32\t%d\n", u32); |
34 | 35 | |
35 | if (http_argument_get_int64("id", &s64)) | |
36 | if (http_argument_get_int64(req, "id", &s64)) | |
36 | 37 | kore_buf_appendf(buf, "int64\t%ld\n", s64); |
37 | 38 | |
38 | if (http_argument_get_uint64("id", &u64)) | |
39 | if (http_argument_get_uint64(req, "id", &u64)) | |
39 | 40 | kore_buf_appendf(buf, "uint64\t%lu\n", u64); |
40 | 41 | |
41 | 42 | data = kore_buf_release(buf, &len); |
42 | 43 | http_response(req, 200, data, len); |
43 | kore_mem_free(data); | |
44 | kore_free(data); | |
44 | 45 | |
45 | 46 | return (KORE_RESULT_OK); |
46 | 47 | } |
2 | 2 | In this case we link against yajl (Yet Another JSON library) in order to |
3 | 3 | parse a JSON string that was POSTed to the server. |
4 | 4 | |
5 | Take a peek at conf/build.conf for different build flavors and how to | |
6 | link to other libraries. | |
7 | ||
5 | 8 | Run: |
6 | 9 | ``` |
7 | env LDFLAGS="-lyajl" kore run | |
10 | $Â kore run | |
8 | 11 | ``` |
9 | 12 | |
10 | 13 | Test: |
11 | 14 | ``` |
12 | curl -i -k -d '{"foo":{"bar": "Hello world"}}' https://127.0.0.1:8888 | |
15 | $ curl -i -k -d '{"foo":{"bar": "Hello world"}}' https://127.0.0.1:8888 | |
13 | 16 | ``` |
14 | 17 | |
15 | 18 | The result should echo back the foo.bar JSON path value: Hello world. |
0 | # json_yajl build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | ldflags=-lyajl | |
13 | } | |
14 | ||
15 | #prod { | |
16 | # You can specify additional CFLAGS here which are only | |
17 | # included if you build with the "prod" flavor. | |
18 | #} |
0 | /* | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
0 | 16 | #include <kore/kore.h> |
1 | 17 | #include <kore/http.h> |
2 | 18 | |
7 | 23 | int |
8 | 24 | page(struct http_request *req) |
9 | 25 | { |
26 | ssize_t ret; | |
10 | 27 | struct kore_buf *buf; |
11 | 28 | char *body; |
12 | 29 | yajl_val node, v; |
13 | 30 | char eb[1024]; |
31 | u_int8_t data[BUFSIZ]; | |
14 | 32 | const char *path[] = { "foo", "bar", NULL }; |
15 | 33 | |
16 | 34 | /* We only allow POST/PUT methods. */ |
22 | 40 | } |
23 | 41 | |
24 | 42 | /* |
25 | * Grab the entire body we received as text (NUL-terminated). | |
26 | * Note: this can return NULL and the result MUST be freed. | |
43 | * Read the entire received body into a memory buffer. | |
27 | 44 | */ |
28 | if ((body = http_body_text(req)) == NULL) { | |
29 | http_response(req, 400, NULL, 0); | |
30 | return (KORE_RESULT_OK); | |
45 | buf = kore_buf_alloc(128); | |
46 | for (;;) { | |
47 | ret = http_body_read(req, data, sizeof(data)); | |
48 | if (ret == -1) { | |
49 | kore_buf_free(buf); | |
50 | kore_log(LOG_NOTICE, "error reading body"); | |
51 | http_response(req, 500, NULL, 0); | |
52 | return (KORE_RESULT_OK); | |
53 | } | |
54 | ||
55 | if (ret == 0) | |
56 | break; | |
57 | ||
58 | kore_buf_append(buf, data, ret); | |
31 | 59 | } |
60 | ||
61 | /* Grab our body data as a NUL-terminated string. */ | |
62 | body = kore_buf_stringify(buf, NULL); | |
32 | 63 | |
33 | 64 | /* Parse the body via yajl now. */ |
34 | 65 | node = yajl_tree_parse(body, eb, sizeof(eb)); |
39 | 70 | kore_log(LOG_NOTICE, "parse error: unknown"); |
40 | 71 | } |
41 | 72 | |
42 | kore_mem_free(body); | |
73 | kore_buf_free(buf); | |
43 | 74 | http_response(req, 400, NULL, 0); |
44 | 75 | return (KORE_RESULT_OK); |
45 | 76 | } |
46 | 77 | |
47 | buf = kore_buf_create(128); | |
78 | /* Reuse old buffer, don't need it anymore for body. */ | |
79 | kore_buf_reset(buf); | |
48 | 80 | |
49 | 81 | /* Attempt to grab foo.bar from the JSON tree. */ |
50 | 82 | v = yajl_tree_get(node, path, yajl_t_string); |
56 | 88 | |
57 | 89 | /* Release the JSON tree now. */ |
58 | 90 | yajl_tree_free(node); |
59 | kore_mem_free(body); | |
60 | 91 | |
61 | 92 | /* Respond to the client. */ |
62 | 93 | http_response(req, 200, buf->data, buf->offset); |
0 | This example demonstrates how you can use the JSON-RPC module in your | |
1 | application. | |
2 | ||
3 | Note that the module depends upon the third-party library `yajl` (Yet Another | |
4 | JSON library) to parse and produce messages. | |
5 | ||
6 | As for the `yajl_json` example, conf/build.conf shows how to link to the | |
7 | library. | |
8 | ||
9 | This example needs kore having been compiled with `JSONRPC` (and so `HTTP`) | |
10 | activated. | |
11 | ||
12 | Run: | |
13 | ``` | |
14 | $Â kore run | |
15 | ``` | |
16 | ||
17 | Test: | |
18 | ``` | |
19 | $ curl -i -k \ | |
20 | -d '{"id":1,"jsonrpc":"2.0","method":"echo","params":["Hello world"]}' \ | |
21 | https://127.0.0.1:8888/v1 | |
22 | ``` | |
23 | The result should echo back the string at `params`: Hello world. | |
24 | ||
25 | Alternatively, if you have bats installed: | |
26 | ``` | |
27 | $ bats test/integ/jsonrpc.bats | |
28 | ``` | |
29 | Will run a small test suite. | |
30 | ||
31 | ||
32 | The yajl repo is available @ https://github.com/lloyd/yajl | |
33 | ||
34 | ||
35 | JSONRPC Request Lifetime | |
36 | ------------------------ | |
37 | ||
38 | Currently, one HTTP request will (in most cases) provoke one and only one | |
39 | response. Batch mode is not supported yet, neither is websocket. | |
40 | ||
41 | As such `jsonrpc\_error` and `jsonrpc\_result` do clean the request after call. | |
42 | ||
43 | If however you want to abort the processed request, like by returning | |
44 | `KORE\_RESULT\_ERROR`, after it having been read, you need to clean it by | |
45 | calling `jsonrpc\_destroy\_request`. Other than that you shouldn't think about | |
46 | this function. | |
47 | ||
48 | ||
49 | Message Handling Log | |
50 | -------------------- | |
51 | ||
52 | The `jsonrpc\_request` keeps a log of messages with levels similar to those of | |
53 | syslog. Messages are added with jsonrpc_log(). | |
54 | ||
55 | By default messages of the log are added to the data member of the error | |
56 | responses if at levels EMERG, ERROR, WARNING and NOTICE. | |
57 | ||
58 | If you dont want log messages to be outputted zero the log_levels flag of the | |
59 | jsonrpc_request. | |
60 | ||
61 | ||
62 | Formatting responses | |
63 | -------------------- | |
64 | ||
65 | By default responses are not prettyfied. To do that set the appropriate flag in | |
66 | the jsonrpc_request structure. |
0 | # jsonrpc build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | ldflags=-lyajl | |
13 | } | |
14 | ||
15 | #prod { | |
16 | # You can specify additional CFLAGS here which are only | |
17 | # included if you build with the "prod" flavor. | |
18 | #} |
0 | # Placeholder configuration | |
1 | ||
2 | bind 127.0.0.1 8888 | |
3 | load ./jsonrpc.so | |
4 | ||
5 | tls_dhparam dh2048.pem | |
6 | ||
7 | domain 127.0.0.1 { | |
8 | certfile cert/server.crt | |
9 | certkey cert/server.key | |
10 | ||
11 | static / homepage | |
12 | static /v1 v1 | |
13 | } |
0 | #include <kore/kore.h> | |
1 | #include <kore/http.h> | |
2 | ||
3 | int homepage(struct http_request *); | |
4 | ||
5 | int | |
6 | homepage(struct http_request *req) | |
7 | { | |
8 | static const char response_body[] = "JSON-RPC API\n"; | |
9 | ||
10 | http_response_header(req, "content-type", "text/plain"); | |
11 | http_response(req, 200, response_body, sizeof(response_body) - 1); | |
12 | return (KORE_RESULT_OK); | |
13 | } |
0 | #include <time.h> | |
1 | #include <xlocale.h> | |
2 | #include <yajl/yajl_gen.h> | |
3 | #include <yajl/yajl_tree.h> | |
4 | #include <kore/kore.h> | |
5 | #include <kore/http.h> | |
6 | #include <kore/jsonrpc.h> | |
7 | ||
8 | int v1(struct http_request *); | |
9 | ||
10 | static int | |
11 | write_string(struct jsonrpc_request *req, void *ctx) | |
12 | { | |
13 | const unsigned char *str = (unsigned char *)ctx; | |
14 | ||
15 | return yajl_gen_string(req->gen, str, strlen((const char *)str)); | |
16 | } | |
17 | ||
18 | static int | |
19 | write_string_array_params(struct jsonrpc_request *req, void *ctx) | |
20 | { | |
21 | int status = 0; | |
22 | ||
23 | if (!YAJL_GEN_KO(status = yajl_gen_array_open(req->gen))) { | |
24 | for (size_t i = 0; i < req->params->u.array.len; i++) { | |
25 | yajl_val yajl_str = req->params->u.array.values[i]; | |
26 | char *str = YAJL_GET_STRING(yajl_str); | |
27 | ||
28 | if (YAJL_GEN_KO(status = yajl_gen_string(req->gen, | |
29 | (unsigned char *)str, strlen(str)))) | |
30 | break; | |
31 | } | |
32 | if (status == 0) | |
33 | status = yajl_gen_array_close(req->gen); | |
34 | } | |
35 | ||
36 | return status; | |
37 | } | |
38 | ||
39 | int | |
40 | v1(struct http_request *http_req) | |
41 | { | |
42 | struct jsonrpc_request req; | |
43 | int ret; | |
44 | ||
45 | /* We only allow POST/PUT methods. */ | |
46 | if (http_req->method != HTTP_METHOD_POST && | |
47 | http_req->method != HTTP_METHOD_PUT) { | |
48 | http_response_header(http_req, "allow", "POST, PUT"); | |
49 | http_response(http_req, HTTP_STATUS_METHOD_NOT_ALLOWED, NULL, 0); | |
50 | return (KORE_RESULT_OK); | |
51 | } | |
52 | ||
53 | /* Read JSON-RPC request. */ | |
54 | if ((ret = jsonrpc_read_request(http_req, &req)) != 0) | |
55 | return jsonrpc_error(&req, ret, NULL); | |
56 | ||
57 | /* Echo command takes and gives back params. */ | |
58 | if (strcmp(req.method, "echo") == 0) { | |
59 | if (!YAJL_IS_ARRAY(req.params)) { | |
60 | jsonrpc_log(&req, LOG_ERR, | |
61 | "Echo only accepts positional params"); | |
62 | return jsonrpc_error(&req, JSONRPC_INVALID_PARAMS, NULL); | |
63 | } | |
64 | for (size_t i = 0; i < req.params->u.array.len; i++) { | |
65 | yajl_val v = req.params->u.array.values[i]; | |
66 | if (!YAJL_IS_STRING(v)) { | |
67 | jsonrpc_log(&req, -3, | |
68 | "Echo only accepts strings"); | |
69 | return jsonrpc_error(&req, | |
70 | JSONRPC_INVALID_PARAMS, NULL); | |
71 | } | |
72 | } | |
73 | return jsonrpc_result(&req, write_string_array_params, NULL); | |
74 | } | |
75 | ||
76 | /* Date command displays date and time according to parameters. */ | |
77 | if (strcmp(req.method, "date") == 0) { | |
78 | time_t time_value; | |
79 | struct tm time_info; | |
80 | char timestamp[33]; | |
81 | struct tm *(*gettm)(const time_t *, struct tm *) = | |
82 | localtime_r; | |
83 | ||
84 | if (YAJL_IS_OBJECT(req.params)) { | |
85 | const char *path[] = {"local", NULL}; | |
86 | yajl_val bf; | |
87 | ||
88 | bf = yajl_tree_get(req.params, path, yajl_t_false); | |
89 | if (bf != NULL) | |
90 | gettm = gmtime_r; | |
91 | } else if (req.params != NULL) { | |
92 | jsonrpc_log(&req, LOG_ERR, | |
93 | "Date only accepts named params"); | |
94 | return jsonrpc_error(&req, JSONRPC_INVALID_PARAMS, NULL); | |
95 | } | |
96 | ||
97 | if ((time_value = time(NULL)) == -1) | |
98 | return jsonrpc_error(&req, -2, | |
99 | "Failed to get date time"); | |
100 | ||
101 | if (gettm(&time_value, &time_info) == NULL) | |
102 | return jsonrpc_error(&req, -3, | |
103 | "Failed to get date time info"); | |
104 | ||
105 | memset(timestamp, 0, sizeof(timestamp)); | |
106 | if (strftime_l(timestamp, sizeof(timestamp) - 1, "%c", | |
107 | &time_info, LC_GLOBAL_LOCALE) == 0) | |
108 | return jsonrpc_error(&req, -4, | |
109 | "Failed to get printable date time"); | |
110 | ||
111 | return jsonrpc_result(&req, write_string, timestamp); | |
112 | } | |
113 | ||
114 | return jsonrpc_error(&req, JSONRPC_METHOD_NOT_FOUND, NULL); | |
115 | } |
0 | #!/usr/bin/env bats | |
1 | ||
2 | # Simple and non exhaustive test suite using bats: | |
3 | # https://github.com/sstephenson/bats | |
4 | ||
5 | PIDFILE=run/jsonrpc.pid | |
6 | CONFFILE=conf/jsonrpc.conf | |
7 | ||
8 | # Start and stop have to be tweaked before being used | |
9 | stop_app() { | |
10 | if [ -f "$PIDFILE" ]; then | |
11 | kill -QUIT `cat "$PIDFILE"` | |
12 | sleep 3 | |
13 | fi | |
14 | if [ -f "$PIDFILE" ]; then | |
15 | kill -KILL `cat "$PIDFILE"` | |
16 | sleep 2 | |
17 | fi | |
18 | } | |
19 | ||
20 | start_app() { | |
21 | stop_app | |
22 | kore -nrc "$CONFFILE" | |
23 | } | |
24 | ||
25 | query_with_content_type() { | |
26 | curl -q \ | |
27 | -H "Content-Type: $1" \ | |
28 | -X POST \ | |
29 | --raw \ | |
30 | -d "$2" \ | |
31 | -s -S \ | |
32 | --insecure \ | |
33 | "https://127.0.0.1:8888/v1" | |
34 | } | |
35 | ||
36 | query() { | |
37 | query_with_content_type "application/json" "$1" | |
38 | } | |
39 | ||
40 | grepstr() { | |
41 | declare result=$1 | |
42 | shift | |
43 | printf "%s" "$result" | grep "$@" >/dev/null | |
44 | } | |
45 | ||
46 | printrep() { | |
47 | declare query=$1 | |
48 | declare result=$2 | |
49 | printf "Sent:\n" | |
50 | printf "%s\n" "$query" | |
51 | printf "Received:\n" | |
52 | printf "%s\n" "$result" | |
53 | } | |
54 | ||
55 | @test "requests with no protocol returns nothing" { | |
56 | query='{"method":"foo","id":"foo"}' | |
57 | result=`query "$query"` | |
58 | printrep "$query" "$result" | |
59 | [ "$result" = "" ] | |
60 | } | |
61 | @test "requests with invalid protocol (1) returns nothing" { | |
62 | query='{"jsonrpc":"1.0","method":"foo","id":"foo"}' | |
63 | result=`query "$query"` | |
64 | printrep "$query" "$result" | |
65 | [ "$result" = "" ] | |
66 | } | |
67 | @test "requests with invalid protocol (2) returns nothing" { | |
68 | query='{"jsonrpc":2.0,"method":"foo","id":"foo"}' | |
69 | result=`query "$query"` | |
70 | printrep "$query" "$result" | |
71 | [ "$result" = "" ] | |
72 | } | |
73 | ||
74 | @test "requests with no method raise errors" { | |
75 | query='{"jsonrpc":"2.0","id":"foo"}' | |
76 | result=`query "$query"` | |
77 | printrep "$query" "$result" | |
78 | grepstr "$result" '"error"[ \t\n]*:[ \t\n]*{[ \t\n]*"code"' | |
79 | } | |
80 | @test "requests with invalid method raise errors" { | |
81 | query='{"jsonrpc":"2.0","method":1,"id":"foo"}' | |
82 | result=`query "$query"` | |
83 | printrep "$query" "$result" | |
84 | grepstr "$result" '"error"[ \t\n]*:[ \t\n]*{[ \t\n]*"code"' | |
85 | } | |
86 | @test "requests with unknown method raise errors" { | |
87 | query='{"jsonrpc":"2.0","method":"foobar","id":"foo"}' | |
88 | result=`query "$query"` | |
89 | printrep "$query" "$result" | |
90 | grepstr "$result" '"error"[ \t\n]*:[ \t\n]*{[ \t\n]*"code"' | |
91 | } | |
92 | ||
93 | @test "error responses give back the string request id" { | |
94 | query='{"jsonrpc":"2.0","id":"foo"}' | |
95 | result=`query "$query"` | |
96 | printrep "$query" "$result" | |
97 | grepstr "$result" '"error"[ \t\n]*:[ \t\n]*{[ \t\n]*"code"' | |
98 | grepstr "$result" '"id"[ \t\n]*:[ \t\n]*"foo"' | |
99 | } | |
100 | @test "error responses give back the integer request id" { | |
101 | query='{"jsonrpc":"2.0","id":1}' | |
102 | result=`query "$query"` | |
103 | printrep "$query" "$result" | |
104 | grepstr "$result" '"error"[ \t\n]*:[ \t\n]*{[ \t\n]*"code"' | |
105 | grepstr "$result" '"id"[ \t\n]*:[ \t\n]*1' | |
106 | } | |
107 | @test "result responses give back the string request id" { | |
108 | query='{"jsonrpc":"2.0","method":"echo","params":["foobar"],"id":"tau"}' | |
109 | result=`query "$query"` | |
110 | printrep "$query" "$result" | |
111 | grepstr "$result" '"result"[ \t\n]*:[ \t\n]*[[ \t\n]*"foobar"[ \t\n]*]' | |
112 | grepstr "$result" '"id"[ \t\n]*:[ \t\n]*"tau"' | |
113 | } | |
114 | @test "result responses give back the integer request id" { | |
115 | query='{"jsonrpc":"2.0","method":"echo","params":["foobar"],"id":6}' | |
116 | result=`query "$query"` | |
117 | printrep "$query" "$result" | |
118 | grepstr "$result" '"result"[ \t\n]*:[ \t\n]*[[ \t\n]*"foobar"[ \t\n]*]' | |
119 | grepstr "$result" '"id"[ \t\n]*:[ \t\n]*6' | |
120 | } |
0 | # ktunnel build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
41 | 41 | { |
42 | 42 | char *host, *port; |
43 | 43 | |
44 | /* Don't want to deal with SPDY connections. */ | |
44 | /* Make sure its HTTP. */ | |
45 | 45 | if (req->owner->proto != CONN_PROTO_HTTP) { |
46 | 46 | http_response(req, HTTP_STATUS_BAD_REQUEST, NULL, 0); |
47 | 47 | return (KORE_RESULT_OK); |
48 | 48 | } |
49 | 49 | |
50 | 50 | /* Parse the query string and grab our arguments. */ |
51 | http_populate_arguments(req); | |
52 | if (!http_argument_get_string("host", &host, NULL) || | |
53 | !http_argument_get_string("port", &port, NULL)) { | |
51 | http_populate_get(req); | |
52 | if (!http_argument_get_string(req, "host", &host) || | |
53 | !http_argument_get_string(req, "port", &port)) { | |
54 | 54 | http_response(req, HTTP_STATUS_BAD_REQUEST, NULL, 0); |
55 | 55 | return (KORE_RESULT_OK); |
56 | 56 | } |
114 | 114 | return (KORE_RESULT_ERROR); |
115 | 115 | } |
116 | 116 | |
117 | if (!kore_connection_nonblock(fd)) { | |
117 | if (!kore_connection_nonblock(fd, 1)) { | |
118 | 118 | close(fd); |
119 | 119 | return (KORE_RESULT_ERROR); |
120 | 120 | } |
121 | 121 | |
122 | 122 | cpipe = kore_connection_new(c); |
123 | TAILQ_INSERT_TAIL(&connections, cpipe, list); | |
124 | ||
123 | 125 | cpipe->fd = fd; |
124 | 126 | cpipe->addr.ipv4 = sin; |
125 | 127 | cpipe->read = net_read; |
137 | 139 | c->disconnect = ktunnel_pipe_disconnect; |
138 | 140 | cpipe->disconnect = ktunnel_pipe_disconnect; |
139 | 141 | |
140 | kore_worker_connection_add(cpipe); | |
141 | 142 | kore_connection_start_idletimer(cpipe); |
142 | 143 | kore_platform_event_all(cpipe->fd, cpipe); |
143 | 144 | |
159 | 160 | struct connection *src = nb->owner; |
160 | 161 | struct connection *dst = src->hdlr_extra; |
161 | 162 | |
162 | printf("received %d bytes on pipe %p (-> %p)\n", nb->s_off, src, dst); | |
163 | printf("received %zu bytes on pipe %p (-> %p)\n", nb->s_off, src, dst); | |
163 | 164 | |
164 | net_send_queue(dst, nb->buf, nb->s_off, NULL, NETBUF_LAST_CHAIN); | |
165 | net_send_queue(dst, nb->buf, nb->s_off); | |
165 | 166 | net_send_flush(dst); |
166 | 167 | net_recv_reset(src, NETBUF_SEND_PAYLOAD_MAX, ktunnel_pipe_data); |
167 | 168 | |
179 | 180 | printf("ktunnel_pipe_disconnect(%p)->%p\n", c, cpipe); |
180 | 181 | |
181 | 182 | if (cpipe != NULL) { |
182 | /* Prevent Kore from calling kore_mem_free() on hdlr_extra. */ | |
183 | /* Prevent Kore from calling kore_free() on hdlr_extra. */ | |
183 | 184 | c->hdlr_extra = NULL; |
184 | 185 | kore_connection_disconnect(cpipe); |
185 | 186 | } |
0 | Kore message framework example | |
1 | ||
2 | Run: | |
3 | ``` | |
4 | # kore run | |
5 | ``` | |
6 | ||
7 | Test: | |
8 | ``` | |
9 | Perform a simple GET request against the root page. | |
10 | This should trigger the example app to send a message | |
11 | to the other workers which will display it. | |
12 | ||
13 | # curl -k https://127.0.0.1:8888 | |
14 | ``` |
0 | # messaging build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
0 | # Placeholder configuration | |
1 | ||
2 | bind 127.0.0.1 8888 | |
3 | load ./messaging.so init | |
4 | tls_dhparam dh2048.pem | |
5 | workers 4 | |
6 | ||
7 | domain 127.0.0.1 { | |
8 | certfile cert/server.crt | |
9 | certkey cert/server.key | |
10 | static / page | |
11 | } |
0 | /* | |
1 | * Copyright (c) 2015 Joris Vink <joris@coders.se> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | #include <kore/kore.h> | |
17 | #include <kore/http.h> | |
18 | ||
19 | /* | |
20 | * This example demonstrates how to use the messaging framework | |
21 | * in Kore. This framework allows you to send messages between | |
22 | * your workers with custom callbacks defined per message ID. | |
23 | */ | |
24 | ||
25 | /* Your code shouldn't use IDs < 100. */ | |
26 | #define MY_MESSAGE_ID 100 | |
27 | ||
28 | int init(int); | |
29 | int page(struct http_request *); | |
30 | void received_message(struct kore_msg *, const void *); | |
31 | ||
32 | /* Initialization callback. */ | |
33 | int | |
34 | init(int state) | |
35 | { | |
36 | if (state == KORE_MODULE_UNLOAD) | |
37 | return (KORE_RESULT_OK); | |
38 | ||
39 | /* | |
40 | * Register our message callback when the module is initialized. | |
41 | * kore_msg_register() fails if the message ID already exists, | |
42 | * but in our case that is OK. | |
43 | */ | |
44 | (void)kore_msg_register(MY_MESSAGE_ID, received_message); | |
45 | ||
46 | return (KORE_RESULT_OK); | |
47 | } | |
48 | ||
49 | /* | |
50 | * Callback for receiving a message MY_MESSAGE_ID. | |
51 | */ | |
52 | void | |
53 | received_message(struct kore_msg *msg, const void *data) | |
54 | { | |
55 | kore_log(LOG_INFO, "got message from %u (%d bytes): %.*s", msg->src, | |
56 | msg->length, msg->length, (const char *)data); | |
57 | } | |
58 | ||
59 | /* | |
60 | * Page request which will send a message to all other workers | |
61 | * with the ID set to MY_MESSAGE_ID and a payload of "hello". | |
62 | */ | |
63 | int | |
64 | page(struct http_request *req) | |
65 | { | |
66 | /* Send to all workers first. */ | |
67 | kore_msg_send(KORE_MSG_WORKER_ALL, MY_MESSAGE_ID, "hello", 5); | |
68 | ||
69 | /* Now send something to worker number #2 only. */ | |
70 | kore_msg_send(2, MY_MESSAGE_ID, "hello number 2", 14); | |
71 | ||
72 | http_response(req, 200, NULL, 0); | |
73 | return (KORE_RESULT_OK); | |
74 | } |
0 | Kore NOHTTP example | |
1 | ||
2 | Note that this example only works if Kore was built with NOHTTP=1. | |
3 | ||
4 | Run: | |
5 | ``` | |
6 | $ kore run | |
7 | ``` | |
8 | ||
9 | Test: | |
10 | ``` | |
11 | Connect to the server using openssl s_client, you will notice | |
12 | that anything sent is submitted back to your client. | |
13 | ||
14 | $ openssl s_client -connect 127.0.0.1:8888 | |
15 | ``` |
0 | # nohttp build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
0 | # Kore can be used as a network layer if it was | |
1 | # built with NOHTTP=1. | |
2 | # | |
3 | # With this you can bind a callback for every new | |
4 | # connection that has been established. For TLS connections | |
5 | # the callback is called after the TLS handshake is completed. | |
6 | ||
7 | # We must load the module first as we need the callback from it. | |
8 | load ./nohttp.so | |
9 | ||
10 | # Listen on port 8888 and call connection_setup for each new connection. | |
11 | bind 127.0.0.1 8888 connection_setup | |
12 | ||
13 | # TLS dh params. | |
14 | tls_dhparam dh2048.pem | |
15 | ||
16 | # We must still define a domain to use TLS. This might go away | |
17 | # in the future for NOHTTP=1 | |
18 | domain 127.0.0.1 { | |
19 | certfile cert/server.crt | |
20 | certkey cert/server.key | |
21 | } |
0 | /* | |
1 | * Copyright (c) 2015 Joris Vink <joris@coders.se> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | /* | |
17 | * Example of using Kore as a network application server. | |
18 | * | |
19 | * We will get called for every new connection that has been established. | |
20 | * For TLS connections we will get called after the TLS handshake completed. | |
21 | * | |
22 | * From the setup we can queue up our own read commands and do whatever we | |
23 | * like with the newly connected client. | |
24 | */ | |
25 | ||
26 | #include <kore/kore.h> | |
27 | ||
28 | void connection_setup(struct connection *); | |
29 | int connection_handle(struct connection *); | |
30 | int connection_recv_data(struct netbuf *); | |
31 | ||
32 | void | |
33 | connection_setup(struct connection *c) | |
34 | { | |
35 | kore_log(LOG_NOTICE, "%p: new connection", c); | |
36 | ||
37 | /* | |
38 | * Setup a read command that will read up to 128 bytes and will | |
39 | * always call the callback connection_recv_data even if not all | |
40 | * 128 bytes were read. | |
41 | */ | |
42 | net_recv_queue(c, 128, NETBUF_CALL_CB_ALWAYS, connection_recv_data); | |
43 | ||
44 | /* We are responsible for setting the connection state. */ | |
45 | c->state = CONN_STATE_ESTABLISHED; | |
46 | ||
47 | /* Override the handle function, called when new events occur. */ | |
48 | c->handle = connection_handle; | |
49 | } | |
50 | ||
51 | /* | |
52 | * This function is called everytime a new event is triggered on the | |
53 | * connection. In this demo we just use it as a stub for the normal | |
54 | * callback kore_connection_handle(). | |
55 | * | |
56 | * In this callback you would generally look at the state of the connection | |
57 | * in c->state and perform the required actions like writing / reading using | |
58 | * net_send_flush() or net_recv_flush() if CONN_SEND_POSSIBLE or | |
59 | * CONN_READ_POSSIBLE are set respectively. Returning KORE_RESULT_ERROR from | |
60 | * this callback will disconnect the connection alltogether. | |
61 | */ | |
62 | int | |
63 | connection_handle(struct connection *c) | |
64 | { | |
65 | kore_log(LOG_NOTICE, "connection_handle: %p", c); | |
66 | return (kore_connection_handle(c)); | |
67 | } | |
68 | ||
69 | /* | |
70 | * This function is called everytime we get up to 128 bytes of data. | |
71 | * The connection can be found under nb->owner. | |
72 | * The data received can be found under nb->buf. | |
73 | * The length of the received data can be found under s_off. | |
74 | */ | |
75 | int | |
76 | connection_recv_data(struct netbuf *nb) | |
77 | { | |
78 | struct connection *c = (struct connection *)nb->owner; | |
79 | ||
80 | kore_log(LOG_NOTICE, "%p: received %u bytes", c, nb->s_off); | |
81 | ||
82 | /* We will just dump these back to the client. */ | |
83 | net_send_queue(c, nb->buf, nb->s_off); | |
84 | net_send_flush(c); | |
85 | ||
86 | /* Now reset the receive command for the next one. */ | |
87 | net_recv_reset(c, 128, connection_recv_data); | |
88 | ||
89 | return (KORE_RESULT_OK); | |
90 | } |
0 | # parameters build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
21 | 21 | int |
22 | 22 | page(struct http_request *req) |
23 | 23 | { |
24 | int p; | |
25 | 24 | u_int16_t id; |
26 | u_int32_t len; | |
27 | 25 | char *sid; |
28 | 26 | struct kore_buf *buf; |
29 | 27 | |
39 | 37 | * See conf/parameters.conf on how that is done, this is an |
40 | 38 | * important step as without the params block you will never |
41 | 39 | * get any parameters returned from Kore. |
42 | * | |
43 | * http_populate_arguments() returns the number of arguments | |
44 | * that were successfully processed and are available. | |
45 | 40 | */ |
46 | p = http_populate_arguments(req); | |
47 | ||
48 | /* If we had no arguments available what so ever, return 400. */ | |
49 | if (p == 0) { | |
50 | http_response(req, 400, NULL, 0); | |
51 | return (KORE_RESULT_OK); | |
52 | } | |
41 | http_populate_get(req); | |
53 | 42 | |
54 | 43 | /* |
55 | 44 | * Lets grab the "id" parameter if available. Kore can obtain |
57 | 46 | * |
58 | 47 | * In this scenario, lets grab it both as an actual string and |
59 | 48 | * as an u_int16_t (unsigned short). |
60 | * | |
61 | * If you grab it as a string, you can immediately ask for | |
62 | * the correct length as well, excluding the NUL terminator. | |
63 | 49 | * |
64 | 50 | * When trying to obtain a parameter as something else then |
65 | 51 | * a string, Kore will automatically check if the value fits |
69 | 55 | * and Kore will return an error when trying to read it as such. |
70 | 56 | */ |
71 | 57 | |
72 | buf = kore_buf_create(128); | |
58 | buf = kore_buf_alloc(128); | |
73 | 59 | |
74 | 60 | /* Grab it as a string, we shouldn't free the result in sid. */ |
75 | if (http_argument_get_string("id", &sid, &len)) | |
76 | kore_buf_appendf(buf, "id as a string: '%s' (%d)\n", sid, len); | |
61 | if (http_argument_get_string(req, "id", &sid)) | |
62 | kore_buf_appendf(buf, "id as a string: '%s'\n", sid); | |
77 | 63 | |
78 | 64 | /* Grab it as an actual u_int16_t. */ |
79 | if (http_argument_get_uint16("id", &id)) | |
65 | if (http_argument_get_uint16(req, "id", &id)) | |
80 | 66 | kore_buf_appendf(buf, "id as an u_int16_t: %d\n", id); |
81 | 67 | |
82 | 68 | /* Now return the result to the client with a 200 status code. */ |
0 | # pgsql build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
15 | 15 | |
16 | 16 | /* |
17 | 17 | * This example demonstrates on how to use state machines and |
18 | * asynchronous pgsql queries. | |
18 | * asynchronous pgsql queries. For a synchronous query example | |
19 | * see the pgsql-sync/ example under the examples/ directory. | |
19 | 20 | * |
20 | 21 | * While this example might seem overly complex for a simple pgsql |
21 | 22 | * query, there is a reason behind its complexity: |
35 | 36 | #include <kore/http.h> |
36 | 37 | #include <kore/pgsql.h> |
37 | 38 | |
38 | #define REQ_STATE_QUERY 0 | |
39 | #define REQ_STATE_DB_WAIT 1 | |
40 | #define REQ_STATE_DB_READ 2 | |
41 | #define REQ_STATE_ERROR 3 | |
42 | #define REQ_STATE_DONE 4 | |
39 | #define REQ_STATE_INIT 0 | |
40 | #define REQ_STATE_QUERY 1 | |
41 | #define REQ_STATE_DB_WAIT 2 | |
42 | #define REQ_STATE_DB_READ 3 | |
43 | #define REQ_STATE_ERROR 4 | |
44 | #define REQ_STATE_DONE 5 | |
43 | 45 | |
44 | 46 | int init(int); |
45 | 47 | int page(struct http_request *); |
46 | 48 | |
49 | static int request_perform_init(struct http_request *); | |
47 | 50 | static int request_perform_query(struct http_request *); |
48 | 51 | static int request_db_wait(struct http_request *); |
49 | 52 | static int request_db_read(struct http_request *); |
51 | 54 | static int request_done(struct http_request *); |
52 | 55 | |
53 | 56 | struct http_state mystates[] = { |
57 | { "REQ_STATE_INIT", request_perform_init }, | |
54 | 58 | { "REQ_STATE_QUERY", request_perform_query }, |
55 | 59 | { "REQ_STATE_DB_WAIT", request_db_wait }, |
56 | 60 | { "REQ_STATE_DB_READ", request_db_read }, |
61 | 65 | #define mystates_size (sizeof(mystates) / sizeof(mystates[0])) |
62 | 66 | |
63 | 67 | struct rstate { |
68 | int cnt; | |
64 | 69 | struct kore_pgsql sql; |
65 | 70 | }; |
66 | 71 | |
68 | 73 | int |
69 | 74 | init(int state) |
70 | 75 | { |
71 | /* Set our connection string. */ | |
72 | pgsql_conn_string = "host=/var/run/postgresql/ dbname=test"; | |
76 | /* Register our database. */ | |
77 | kore_pgsql_register("db", "host=/tmp dbname=test"); | |
73 | 78 | |
74 | 79 | return (KORE_RESULT_OK); |
75 | 80 | } |
83 | 88 | return (http_state_run(mystates, mystates_size, req)); |
84 | 89 | } |
85 | 90 | |
86 | /* The initial state, we setup our context and fire off the pgsql query. */ | |
91 | /* Initialize our PGSQL data structure and prepare for an async query. */ | |
92 | int | |
93 | request_perform_init(struct http_request *req) | |
94 | { | |
95 | struct rstate *state; | |
96 | ||
97 | /* Setup our state context (if not yet set). */ | |
98 | if (req->hdlr_extra == NULL) { | |
99 | state = kore_malloc(sizeof(*state)); | |
100 | req->hdlr_extra = state; | |
101 | } else { | |
102 | state = req->hdlr_extra; | |
103 | } | |
104 | ||
105 | /* Initialize our kore_pgsql data structure. */ | |
106 | if (!kore_pgsql_query_init(&state->sql, req, "db", KORE_PGSQL_ASYNC)) { | |
107 | /* If the state was still INIT, we'll try again later. */ | |
108 | if (state->sql.state == KORE_PGSQL_STATE_INIT) { | |
109 | req->fsm_state = REQ_STATE_INIT; | |
110 | return (HTTP_STATE_RETRY); | |
111 | } | |
112 | ||
113 | kore_pgsql_logerror(&state->sql); | |
114 | req->fsm_state = REQ_STATE_ERROR; | |
115 | } else { | |
116 | req->fsm_state = REQ_STATE_QUERY; | |
117 | } | |
118 | ||
119 | return (HTTP_STATE_CONTINUE); | |
120 | } | |
121 | ||
122 | /* After setting everything up we will execute our async query. */ | |
87 | 123 | int |
88 | 124 | request_perform_query(struct http_request *req) |
89 | 125 | { |
90 | struct rstate *state; | |
91 | ||
92 | /* Setup our state context. */ | |
93 | state = kore_malloc(sizeof(*state)); | |
94 | ||
95 | /* Attach the state to our request. */ | |
96 | req->hdlr_extra = state; | |
126 | struct rstate *state = req->hdlr_extra; | |
97 | 127 | |
98 | 128 | /* We want to move to read result after this. */ |
99 | 129 | req->fsm_state = REQ_STATE_DB_WAIT; |
100 | 130 | |
101 | 131 | /* Fire off the query. */ |
102 | if (!kore_pgsql_query(&state->sql, req, "SELECT * FROM coders")) { | |
103 | /* If the state was still INIT, we'll try again later. */ | |
104 | if (state->sql.state == KORE_PGSQL_STATE_INIT) { | |
105 | req->fsm_state = REQ_STATE_QUERY; | |
106 | return (HTTP_STATE_RETRY); | |
107 | } | |
108 | ||
132 | if (!kore_pgsql_query(&state->sql, "SELECT * FROM coders")) { | |
109 | 133 | /* |
110 | 134 | * Let the state machine continue immediately since we |
111 | 135 | * have an error anyway. |
194 | 218 | return (HTTP_STATE_COMPLETE); |
195 | 219 | } |
196 | 220 | |
197 | /* Request was completed succesfully. */ | |
221 | /* Request was completed successfully. */ | |
198 | 222 | int |
199 | 223 | request_done(struct http_request *req) |
200 | 224 | { |
0 | # pgsql-sync build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
0 | # Placeholder configuration | |
1 | ||
2 | bind 127.0.0.1 8888 | |
3 | load ./pgsql-sync.so init | |
4 | tls_dhparam dh2048.pem | |
5 | ||
6 | domain 127.0.0.1 { | |
7 | certfile cert/server.crt | |
8 | certkey cert/server.key | |
9 | static / page | |
10 | } |
0 | /* | |
1 | * Copyright (c) 2015 Joris Vink <joris@coders.se> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | /* | |
17 | * This example demonstrates how to use synchronous PGSQL queries | |
18 | * with Kore. For an asynchronous example see pgsql/ under examples/. | |
19 | * | |
20 | * This example does the same as the asynchronous one, select all entries | |
21 | * from a table called "coders". | |
22 | */ | |
23 | ||
24 | #include <kore/kore.h> | |
25 | #include <kore/http.h> | |
26 | #include <kore/pgsql.h> | |
27 | ||
28 | int init(int); | |
29 | int page(struct http_request *); | |
30 | ||
31 | /* Called when our module is loaded (see config) */ | |
32 | int | |
33 | init(int state) | |
34 | { | |
35 | /* Register our database. */ | |
36 | kore_pgsql_register("db", "host=/tmp dbname=test"); | |
37 | ||
38 | return (KORE_RESULT_OK); | |
39 | } | |
40 | ||
41 | /* Page handler entry point (see config) */ | |
42 | int | |
43 | page(struct http_request *req) | |
44 | { | |
45 | struct kore_pgsql sql; | |
46 | char *name; | |
47 | int rows, i; | |
48 | ||
49 | req->status = HTTP_STATUS_INTERNAL_ERROR; | |
50 | ||
51 | /* | |
52 | * Initialise our kore_pgsql data structure with the database name | |
53 | * we want to connect to (note that we registered this earlier with | |
54 | * kore_pgsql_register()). We also say we will perform a synchronous | |
55 | * query (KORE_PGSQL_SYNC) and we do not need to pass our http_request | |
56 | * so we pass NULL instead. | |
57 | */ | |
58 | if (!kore_pgsql_query_init(&sql, NULL, "db", KORE_PGSQL_SYNC)) { | |
59 | kore_pgsql_logerror(&sql); | |
60 | goto out; | |
61 | } | |
62 | ||
63 | /* | |
64 | * Now we can fire off the query, once it returns we either have | |
65 | * a result on which we can operate or an error occured. | |
66 | */ | |
67 | if (!kore_pgsql_query(&sql, "SELECT * FROM coders")) { | |
68 | kore_pgsql_logerror(&sql); | |
69 | goto out; | |
70 | } | |
71 | ||
72 | /* | |
73 | * Iterate over the result and dump it to somewhere. | |
74 | */ | |
75 | rows = kore_pgsql_ntuples(&sql); | |
76 | for (i = 0; i < rows; i++) { | |
77 | name = kore_pgsql_getvalue(&sql, i, 0); | |
78 | kore_log(LOG_NOTICE, "name: '%s'", name); | |
79 | } | |
80 | ||
81 | /* All good. */ | |
82 | req->status = HTTP_STATUS_OK; | |
83 | ||
84 | out: | |
85 | http_response(req, req->status, NULL, 0); | |
86 | ||
87 | /* Don't forget to cleanup the kore_pgsql data structure. */ | |
88 | kore_pgsql_cleanup(&sql); | |
89 | ||
90 | return (KORE_RESULT_OK); | |
91 | } |
0 | Kore example of tasks and websockets. | |
1 | ||
2 | This example connects Kore via task to a named unix pipe and | |
3 | spews out any output to all connected websocket clients. | |
4 | ||
5 | Before you run this make the pipe: | |
6 | $ mkfifo /tmp/pipe | |
7 | ||
8 | Run: | |
9 | ``` | |
10 | $ kore run | |
11 | ``` | |
12 | ||
13 | Test: | |
14 | ``` | |
15 | Open a browser that does websockets, surf to https://127.0.0.1:8888 | |
16 | or whatever configured IP you have in the config. | |
17 | ||
18 | Hit the connect button to open a websocket session. | |
19 | ||
20 | Now connect a writer endpoint to the named pipe (/tmp/pipe): | |
21 | $ echo "hello" > /tmp/pipe | |
22 | ||
23 | You should see the result in your browser. | |
24 | ``` |
0 | <!DOCTYPE> | |
1 | <html> | |
2 | <head> | |
3 | <script> | |
4 | var socket = null; | |
5 | var sent = 0; | |
6 | var recv = 0; | |
7 | var length = 65536; | |
8 | ||
9 | function open(evt) { | |
10 | } | |
11 | ||
12 | function message(msg) { | |
13 | } | |
14 | ||
15 | function onmessage(evt) { | |
16 | var obj = document.getElementById("log"); | |
17 | obj.innerHTML = "<p>" + evt.data + "</p>" + obj.innerHTML; | |
18 | } | |
19 | ||
20 | function connect() { | |
21 | socket = new WebSocket("wss://127.0.0.1:8888/connect"); | |
22 | ||
23 | socket.onopen = function(evt) { open(evt) }; | |
24 | socket.onclose = function(evt) { alert("closed"); }; | |
25 | socket.onmessage = function(evt) { onmessage(evt) }; | |
26 | socket.onerror = function(evt) { alert("onerror"); }; | |
27 | } | |
28 | </script> | |
29 | </head> | |
30 | ||
31 | <body> | |
32 | ||
33 | <form action="/" onsubmit="connect(); return false;"> | |
34 | <input type="submit" value="connect"> | |
35 | </form> | |
36 | ||
37 | <div id="log"> | |
38 | </div> | |
39 | ||
40 | </body> | |
41 | </html> |
0 | # pipe_task build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
0 | # Kore pipe_task example | |
1 | ||
2 | bind 127.0.0.1 8888 | |
3 | load ./pipe_task.so init | |
4 | ||
5 | tls_dhparam dh2048.pem | |
6 | ||
7 | websocket_maxframe 65536 | |
8 | websocket_timeout 10000 | |
9 | ||
10 | domain 127.0.0.1 { | |
11 | certfile cert/server.crt | |
12 | certkey cert/server.key | |
13 | ||
14 | static / page | |
15 | static /connect page_ws_connect | |
16 | } |
0 | /* | |
1 | * Copyright (c) 2015 Joris Vink <joris@coders.se> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | /* | |
17 | * This example demos Kore its task and websocket capabilities. | |
18 | * | |
19 | * It will spawn a task which connects to a named pipe and writes | |
20 | * responses to all connected websocket clients. | |
21 | */ | |
22 | ||
23 | #include <sys/param.h> | |
24 | #include <sys/socket.h> | |
25 | #include <sys/un.h> | |
26 | ||
27 | #include <kore/kore.h> | |
28 | #include <kore/http.h> | |
29 | #include <kore/tasks.h> | |
30 | ||
31 | #include <fcntl.h> | |
32 | #include <unistd.h> | |
33 | ||
34 | #include "assets.h" | |
35 | ||
36 | int init(int); | |
37 | int page(struct http_request *); | |
38 | int page_ws_connect(struct http_request *); | |
39 | ||
40 | void websocket_connect(struct connection *); | |
41 | void websocket_disconnect(struct connection *); | |
42 | void websocket_message(struct connection *, | |
43 | u_int8_t, void *, size_t); | |
44 | ||
45 | int pipe_reader(struct kore_task *); | |
46 | void pipe_data_available(struct kore_task *); | |
47 | ||
48 | /* Websocket callbacks. */ | |
49 | struct kore_wscbs wscbs = { | |
50 | websocket_connect, | |
51 | websocket_message, | |
52 | websocket_disconnect | |
53 | }; | |
54 | ||
55 | /* Our pipe reader. */ | |
56 | struct kore_task pipe_task; | |
57 | ||
58 | /* Module init function (see config). */ | |
59 | int | |
60 | init(int state) | |
61 | { | |
62 | /* Do not allow reload. */ | |
63 | if (state == KORE_MODULE_UNLOAD) | |
64 | return (KORE_RESULT_ERROR); | |
65 | ||
66 | /* Only do this on a dedicated worker. */ | |
67 | if (worker->id != 0) | |
68 | return (KORE_RESULT_OK); | |
69 | ||
70 | /* Create our task. */ | |
71 | kore_task_create(&pipe_task, pipe_reader); | |
72 | ||
73 | /* Bind a callback whenever data is available from task. */ | |
74 | kore_task_bind_callback(&pipe_task, pipe_data_available); | |
75 | ||
76 | /* Start the task. */ | |
77 | kore_task_run(&pipe_task); | |
78 | ||
79 | return (KORE_RESULT_OK); | |
80 | } | |
81 | ||
82 | /* Called whenever we get a new websocket connection. */ | |
83 | void | |
84 | websocket_connect(struct connection *c) | |
85 | { | |
86 | kore_log(LOG_NOTICE, "%p: connected", c); | |
87 | } | |
88 | ||
89 | /* Called whenever we receive a websocket message from a client. */ | |
90 | void | |
91 | websocket_message(struct connection *c, u_int8_t op, void *data, size_t len) | |
92 | { | |
93 | /* Not doing anything with this. */ | |
94 | } | |
95 | ||
96 | /* Called whenever a websocket goes away. */ | |
97 | void | |
98 | websocket_disconnect(struct connection *c) | |
99 | { | |
100 | kore_log(LOG_NOTICE, "%p: disconnecting", c); | |
101 | } | |
102 | ||
103 | /* The / page. */ | |
104 | int | |
105 | page(struct http_request *req) | |
106 | { | |
107 | http_response_header(req, "content-type", "text/html"); | |
108 | http_response(req, 200, asset_frontend_html, asset_len_frontend_html); | |
109 | ||
110 | return (KORE_RESULT_OK); | |
111 | } | |
112 | ||
113 | /* The /connect page. */ | |
114 | int | |
115 | page_ws_connect(struct http_request *req) | |
116 | { | |
117 | kore_websocket_handshake(req, &wscbs); | |
118 | return (KORE_RESULT_OK); | |
119 | } | |
120 | ||
121 | /* | |
122 | * The pipe reader task. This task simply waits for a writer end | |
123 | * on a named pipe and reads from it. The bytes read are written | |
124 | * on the task channel because the task does not own any connection | |
125 | * data structures and shouldn't reference them directly. | |
126 | */ | |
127 | int | |
128 | pipe_reader(struct kore_task *t) | |
129 | { | |
130 | int fd; | |
131 | ssize_t ret; | |
132 | u_int8_t buf[BUFSIZ]; | |
133 | ||
134 | fd = -1; | |
135 | ||
136 | /* Just run forever. */ | |
137 | for (;;) { | |
138 | /* Attempt to open the pipe if needed. */ | |
139 | if (fd == -1) { | |
140 | kore_log(LOG_NOTICE, "waiting for writer"); | |
141 | ||
142 | if ((fd = open("/tmp/pipe", O_RDONLY)) == -1) { | |
143 | kore_log(LOG_NOTICE, "failed to open pipe"); | |
144 | sleep(10); | |
145 | continue; | |
146 | } | |
147 | ||
148 | kore_log(LOG_NOTICE, "writer connected"); | |
149 | } | |
150 | ||
151 | /* Got a writer on the other end so start reading. */ | |
152 | ret = read(fd, buf, sizeof(buf)); | |
153 | if (ret == -1) { | |
154 | kore_log(LOG_ERR, "read error on pipe"); | |
155 | (void)close(fd); | |
156 | fd = -1; | |
157 | continue; | |
158 | } | |
159 | ||
160 | if (ret == 0) { | |
161 | kore_log(LOG_NOTICE, "writer disconnected"); | |
162 | (void)close(fd); | |
163 | fd = -1; | |
164 | continue; | |
165 | } | |
166 | ||
167 | kore_log(LOG_NOTICE, "got %ld bytes from pipe", ret); | |
168 | ||
169 | /* | |
170 | * Write data on the task channel so our main event loop | |
171 | * will call the registered callback. | |
172 | */ | |
173 | kore_task_channel_write(t, buf, ret); | |
174 | } | |
175 | ||
176 | return (KORE_RESULT_OK); | |
177 | } | |
178 | ||
179 | /* Called on the main event loop whenever a task event fires. */ | |
180 | void | |
181 | pipe_data_available(struct kore_task *t) | |
182 | { | |
183 | size_t len; | |
184 | u_int8_t buf[BUFSIZ]; | |
185 | ||
186 | /* Deal with the task finishing, we could restart it from here. */ | |
187 | if (kore_task_finished(t)) { | |
188 | kore_log(LOG_WARNING, "task finished"); | |
189 | return; | |
190 | } | |
191 | ||
192 | /* Read data from the task channel. */ | |
193 | len = kore_task_channel_read(t, buf, sizeof(buf)); | |
194 | if (len > sizeof(buf)) | |
195 | kore_log(LOG_WARNING, "truncated data from task"); | |
196 | ||
197 | /* Broadcast it to all connected websocket clients. */ | |
198 | kore_log(LOG_NOTICE, "got %d bytes from task", len); | |
199 | kore_websocket_broadcast(NULL, WEBSOCKET_OP_TEXT, | |
200 | buf, len, WEBSOCKET_BROADCAST_GLOBAL); | |
201 | } |
0 | # sse build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
4 | 4 | tls_dhparam dh2048.pem |
5 | 5 | |
6 | 6 | http_keepalive_time 600 |
7 | spdy_idle_time 600 | |
8 | 7 | |
9 | 8 | domain 127.0.0.1 { |
10 | 9 | certfile cert/server.crt |
15 | 15 | |
16 | 16 | /* |
17 | 17 | * Simple example of how SSE (Server Side Events) could be used in Kore. |
18 | * We deal with SSE both over normal HTTP/1.1 and over SPDY connections. | |
19 | 18 | * |
20 | 19 | * Upon new arrivals, a join event is broadcast to all clients. |
21 | 20 | * If a client goes away a leave event is broadcasted. |
28 | 27 | |
29 | 28 | #include "assets.h" |
30 | 29 | |
30 | void sse_ping(void *, u_int64_t); | |
31 | 31 | int page(struct http_request *); |
32 | 32 | int subscribe(struct http_request *); |
33 | 33 | void sse_disconnect(struct connection *); |
34 | void sse_ping(void *, u_int64_t, u_int64_t); | |
35 | 34 | void sse_send(struct connection *, void *, size_t); |
36 | 35 | void sse_broadcast(struct connection *, void *, size_t); |
37 | void sse_spdy_stream_closed(struct connection *, struct spdy_stream *); | |
38 | 36 | int check_header(struct http_request *, const char *, const char *); |
39 | 37 | |
40 | 38 | /* |
42 | 40 | * to their hdlr_extra pointer member. |
43 | 41 | */ |
44 | 42 | struct sse_state { |
45 | struct spdy_stream *stream; | |
46 | 43 | struct kore_timer *timer; |
47 | 44 | }; |
48 | 45 | |
93 | 90 | /* Set a disconnection method so we know when this client goes away. */ |
94 | 91 | req->owner->disconnect = sse_disconnect; |
95 | 92 | |
96 | /* For non SPDY clients we do not expect any more data to arrive. */ | |
97 | if (req->owner->proto != CONN_PROTO_SPDY) | |
98 | req->owner->flags |= CONN_READ_BLOCK; | |
93 | /* We do not expect any more data to arrive. */ | |
94 | req->owner->flags |= CONN_READ_BLOCK; | |
99 | 95 | |
100 | 96 | /* Allocate a state to be carried by our connection. */ |
101 | 97 | state = kore_malloc(sizeof(*state)); |
102 | state->stream = req->stream; | |
103 | 98 | req->owner->hdlr_extra = state; |
104 | ||
105 | /* SSE over SPDY will need this extra love. */ | |
106 | if (req->owner->proto == CONN_PROTO_SPDY) { | |
107 | /* Unset the http request attached to our SPDY stream. */ | |
108 | req->stream->httpreq = NULL; | |
109 | ||
110 | /* | |
111 | * Do not let the stream close unless a RST occurs or | |
112 | * until we close it ourselves. | |
113 | */ | |
114 | req->stream->flags |= SPDY_NO_CLOSE; | |
115 | ||
116 | /* Set a callback in case this stream gets a RST. */ | |
117 | req->stream->onclose = sse_spdy_stream_closed; | |
118 | } | |
119 | 99 | |
120 | 100 | /* Now start a timer to send a ping back every 10 second. */ |
121 | 101 | state->timer = kore_timer_add(sse_ping, 10000, req->owner, 0); |
134 | 114 | struct connection *c; |
135 | 115 | |
136 | 116 | /* Broadcast the message to all other clients. */ |
137 | TAILQ_FOREACH(c, &worker_clients, list) { | |
117 | TAILQ_FOREACH(c, &connections, list) { | |
138 | 118 | if (c == src) |
139 | 119 | continue; |
140 | 120 | sse_send(c, data, len); |
150 | 130 | if (state == NULL) |
151 | 131 | return; |
152 | 132 | |
153 | /* SPDY connections need this extra bit of magic. */ | |
154 | if (c->proto == CONN_PROTO_SPDY) { | |
155 | if (state->stream == NULL) { | |
156 | kore_log(LOG_ERR, "no SPDY stream for sse_send()"); | |
157 | kore_connection_disconnect(c); | |
158 | return; | |
159 | } | |
160 | ||
161 | /* | |
162 | * Tell Kore to send a dataframe prelude + increase the | |
163 | * length of our stream to be sent. | |
164 | */ | |
165 | if (state->stream->send_size == 0) | |
166 | state->stream->flags |= SPDY_DATAFRAME_PRELUDE; | |
167 | state->stream->send_size += len; | |
168 | } | |
169 | ||
170 | 133 | /* Queue outgoing data now. */ |
171 | net_send_queue(c, data, len, state->stream, NETBUF_LAST_CHAIN); | |
134 | net_send_queue(c, data, len); | |
172 | 135 | net_send_flush(c); |
173 | 136 | } |
174 | 137 | |
175 | 138 | void |
176 | sse_ping(void *arg, u_int64_t now, u_int64_t delta) | |
139 | sse_ping(void *arg, u_int64_t now) | |
177 | 140 | { |
178 | 141 | struct connection *c = arg; |
179 | 142 | char *ping = "event:ping\ndata:\n\n"; |
193 | 156 | /* Tell others we are leaving. */ |
194 | 157 | sse_broadcast(c, leaving, strlen(leaving)); |
195 | 158 | |
196 | /* Make sure we cleanup our hooked stream if any. */ | |
197 | if (c->proto == CONN_PROTO_SPDY && state->stream != NULL) { | |
198 | state->stream->onclose = NULL; | |
199 | spdy_stream_close(c, state->stream, SPDY_REMOVE_NETBUFS); | |
200 | } | |
201 | ||
202 | 159 | /* Kill our timer and free/remove the state. */ |
203 | 160 | kore_timer_remove(state->timer); |
204 | kore_mem_free(state); | |
161 | kore_free(state); | |
205 | 162 | |
206 | 163 | /* Prevent us to be called again. */ |
207 | 164 | c->hdlr_extra = NULL; |
208 | 165 | c->disconnect = NULL; |
209 | } | |
210 | ||
211 | void | |
212 | sse_spdy_stream_closed(struct connection *c, struct spdy_stream *s) | |
213 | { | |
214 | struct sse_state *state = c->hdlr_extra; | |
215 | ||
216 | /* Paranoia. */ | |
217 | if (state->stream != s) { | |
218 | state->stream = NULL; | |
219 | kore_connection_disconnect(c); | |
220 | return; | |
221 | } | |
222 | ||
223 | /* Set our stream to NULL and call sse_disconnect. */ | |
224 | state->stream = NULL; | |
225 | sse_disconnect(c); | |
226 | 166 | } |
227 | 167 | |
228 | 168 | int |
236 | 176 | } |
237 | 177 | |
238 | 178 | if (strcmp(hdr, value)) { |
239 | kore_mem_free(hdr); | |
240 | 179 | http_response(req, 400, NULL, 0); |
241 | 180 | return (KORE_RESULT_ERROR); |
242 | 181 | } |
243 | 182 | |
244 | kore_mem_free(hdr); | |
245 | 183 | return (KORE_RESULT_OK); |
246 | 184 | } |
5 | 5 | |
6 | 6 | Build: |
7 | 7 | ``` |
8 | # env LDFLAGS="-I/path/to/libcurl -lcurl" kore build | |
8 | $ kore build | |
9 | 9 | ``` |
10 | 10 | |
11 | 11 | Run: |
12 | 12 | ``` |
13 | # kore run | |
13 | $ kore run | |
14 | 14 | ``` |
15 | 15 | |
16 | 16 | Test: |
17 | 17 | ``` |
18 | # curl -i -k https://127.0.0.1:8888/?user=astring | |
18 | $ curl -i -k https://127.0.0.1:8888/?user=astring | |
19 | 19 | The returned data must match what you supplied in user ([a-z] string) |
20 | 20 | ``` |
0 | # tasks build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | ldflags=-lcurl | |
13 | } | |
14 | ||
15 | #prod { | |
16 | # You can specify additional CFLAGS here which are only | |
17 | # included if you build with the "prod" flavor. | |
18 | #} |
3 | 3 | load ./tasks.so |
4 | 4 | |
5 | 5 | tls_dhparam dh2048.pem |
6 | ||
7 | task_threads 4 | |
8 | worker_max_connections 1000 | |
9 | http_keepalive_time 0 | |
6 | 10 | |
7 | 11 | validator v_user regex ^[a-z]*$ |
8 | 12 |
57 | 57 | */ |
58 | 58 | if (req->hdlr_extra == NULL) { |
59 | 59 | /* Grab the user argument */ |
60 | http_populate_arguments(req); | |
61 | if (!http_argument_get_string("user", &user, &len)) { | |
60 | http_populate_get(req); | |
61 | if (!http_argument_get_string(req, "user", &user)) { | |
62 | 62 | http_response(req, 500, "ERROR\n", 6); |
63 | 63 | return (KORE_RESULT_OK); |
64 | 64 | } |
86 | 86 | * GET request to its channel. |
87 | 87 | */ |
88 | 88 | kore_task_run(&state->task); |
89 | kore_task_channel_write(&state->task, user, len); | |
89 | kore_task_channel_write(&state->task, user, strlen(user)); | |
90 | 90 | |
91 | 91 | /* |
92 | 92 | * Tell Kore to retry us later. |
141 | 141 | int |
142 | 142 | post_back(struct http_request *req) |
143 | 143 | { |
144 | u_int32_t len; | |
145 | 144 | char *user; |
146 | 145 | |
147 | 146 | if (req->method != HTTP_METHOD_POST) { |
149 | 148 | return (KORE_RESULT_OK); |
150 | 149 | } |
151 | 150 | |
152 | http_populate_arguments(req); | |
153 | if (!http_argument_get_string("user", &user, &len)) { | |
151 | http_populate_post(req); | |
152 | if (!http_argument_get_string(req, "user", &user)) { | |
154 | 153 | http_response(req, 500, NULL, 0); |
155 | 154 | return (KORE_RESULT_OK); |
156 | 155 | } |
157 | 156 | |
158 | 157 | /* Simply echo the supplied user argument back. */ |
159 | http_response(req, 200, user, len); | |
158 | http_response(req, 200, user, strlen(user)); | |
160 | 159 | |
161 | 160 | return (KORE_RESULT_OK); |
162 | 161 | } |
193 | 192 | if ((curl = curl_easy_init()) == NULL) |
194 | 193 | return (KORE_RESULT_ERROR); |
195 | 194 | |
196 | b = kore_buf_create(128); | |
195 | b = kore_buf_alloc(128); | |
197 | 196 | |
198 | 197 | /* Do CURL magic. */ |
199 | 198 | curl_easy_setopt(curl, CURLOPT_POST, 1); |
218 | 217 | */ |
219 | 218 | data = kore_buf_release(b, &len); |
220 | 219 | kore_task_channel_write(t, data, len); |
221 | kore_mem_free(data); | |
220 | kore_free(data); | |
222 | 221 | |
223 | 222 | return (KORE_RESULT_OK); |
224 | 223 | } |
0 | Kore as a TLS-proxy. | |
1 | ||
2 | Edit src/proxy.c and add your backends to the backends[] data structure. | |
3 | ||
4 | If you want to reduce attack surface you can build Kore with NOHTTP=1 to | |
5 | completely remove the HTTP component and only run the net code. | |
6 | ||
7 | Run: | |
8 | ``` | |
9 | $ kore run | |
10 | ``` | |
11 | ||
12 | Test: | |
13 | ``` | |
14 | Connect to the server and notice that it proxies data between you | |
15 | and your destination. | |
16 | ||
17 | $ openssl s_client -connect 127.0.0.1:8888 | |
18 | ``` |
0 | # tls-proxy build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
0 | # Kore as a TLS proxy configuration. | |
1 | ||
2 | load ./tls-proxy.so | |
3 | tls_dhparam dh2048.pem | |
4 | ||
5 | # | |
6 | # Bind the proxy to a given IP and port. For every | |
7 | # connection we receive we will call client_setup | |
8 | # so it can kick things in action. | |
9 | # | |
10 | bind 127.0.0.1 8888 client_setup | |
11 | ||
12 | # Setup domain for TLS usage. | |
13 | domain localhost { | |
14 | certfile cert/server.crt | |
15 | certkey cert/server.key | |
16 | } |
0 | /* | |
1 | * Copyright (c) 2015 Joris Vink <joris@coders.se> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | #include <sys/param.h> | |
17 | #include <sys/socket.h> | |
18 | ||
19 | #include <kore/kore.h> | |
20 | ||
21 | /* | |
22 | * In this example Kore acts as a TLS proxy shuffling data between | |
23 | * an encrypted connection and a plain text backend. | |
24 | * | |
25 | * It will look at the TLS SNI extension to figure out what backend | |
26 | * to use for the connection when it comes in. | |
27 | * | |
28 | * Add your backends to the data structure below. | |
29 | */ | |
30 | ||
31 | /* Default timeouts, 5 seconds for connecting, 15 seconds otherwise. */ | |
32 | #define PROXY_TIMEOUT (15 * 1000) | |
33 | #define PROXY_CONNECT_TIMEOUT (5 * 1000) | |
34 | ||
35 | /* All domains and their backends. */ | |
36 | struct { | |
37 | const char *name; | |
38 | const char *ip; | |
39 | const u_int16_t port; | |
40 | } backends[] = { | |
41 | { "localhost", "127.0.0.1", 8080 }, | |
42 | { NULL, NULL, 0 } | |
43 | }; | |
44 | ||
45 | int client_handle(struct connection *); | |
46 | void client_setup(struct connection *); | |
47 | ||
48 | void disconnect(struct connection *); | |
49 | int pipe_data(struct netbuf *); | |
50 | ||
51 | int backend_handle_connect(struct connection *); | |
52 | int backend_handle_default(struct connection *); | |
53 | ||
54 | /* | |
55 | * Called for every new connection on a certain ip/port. Which one is | |
56 | * configured in the TLS proxy its configuration file. | |
57 | */ | |
58 | void | |
59 | client_setup(struct connection *c) | |
60 | { | |
61 | int i, fd; | |
62 | struct connection *backend; | |
63 | ||
64 | /* Paranoia. */ | |
65 | if (c->ssl->session == NULL || | |
66 | c->ssl->session->tlsext_hostname == NULL) { | |
67 | kore_connection_disconnect(c); | |
68 | return; | |
69 | } | |
70 | ||
71 | /* Figure out what backend to use. */ | |
72 | for (i = 0; backends[i].name != NULL; i++) { | |
73 | if (!strcasecmp(backends[i].name, | |
74 | c->ssl->session->tlsext_hostname)) | |
75 | break; | |
76 | } | |
77 | ||
78 | /* If we don't have any backends, we just disconnect the client. */ | |
79 | if (backends[i].name == NULL) { | |
80 | kore_connection_disconnect(c); | |
81 | return; | |
82 | } | |
83 | ||
84 | /* Create new socket for the backend connection. */ | |
85 | if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { | |
86 | kore_log(LOG_ERR, "socket(): %s", errno_s); | |
87 | kore_connection_disconnect(c); | |
88 | return; | |
89 | } | |
90 | ||
91 | /* Set it to non blocking as well. */ | |
92 | if (!kore_connection_nonblock(fd, 1)) { | |
93 | close(fd); | |
94 | kore_connection_disconnect(c); | |
95 | return; | |
96 | } | |
97 | ||
98 | /* Grab a new connection from Kore to hook backend into. */ | |
99 | backend = kore_connection_new(NULL); | |
100 | ||
101 | /* Prepare our connection. */ | |
102 | backend->addrtype = AF_INET; | |
103 | backend->addr.ipv4.sin_family = AF_INET; | |
104 | backend->addr.ipv4.sin_port = htons(backends[i].port); | |
105 | backend->addr.ipv4.sin_addr.s_addr = inet_addr(backends[i].ip); | |
106 | ||
107 | /* Set the file descriptor for the backend. */ | |
108 | backend->fd = fd; | |
109 | ||
110 | /* Default write/read callbacks for backend. */ | |
111 | backend->read = net_read; | |
112 | backend->write = net_write; | |
113 | ||
114 | /* Connection type (unknown to Kore). */ | |
115 | backend->proto = CONN_PROTO_UNKNOWN; | |
116 | backend->state = CONN_STATE_ESTABLISHED; | |
117 | ||
118 | /* The backend idle timer is set first to connection timeout. */ | |
119 | backend->idle_timer.length = PROXY_CONNECT_TIMEOUT; | |
120 | ||
121 | /* The client idle timer is set to default idle time. */ | |
122 | c->idle_timer.length = PROXY_TIMEOUT; | |
123 | ||
124 | /* Now link both the client and the backend connection together. */ | |
125 | c->hdlr_extra = backend; | |
126 | backend->hdlr_extra = c; | |
127 | ||
128 | /* | |
129 | * The handle function pointer for the backend is set to the | |
130 | * backend_handle_connect() while connecting. | |
131 | */ | |
132 | c->handle = client_handle; | |
133 | backend->handle = backend_handle_connect; | |
134 | ||
135 | /* Set the disconnect method for both connections. */ | |
136 | c->disconnect = disconnect; | |
137 | backend->disconnect = disconnect; | |
138 | ||
139 | /* Queue write events for the backend connection for now. */ | |
140 | kore_platform_schedule_write(backend->fd, backend); | |
141 | ||
142 | /* Start idle timer for the backend. */ | |
143 | kore_connection_start_idletimer(backend); | |
144 | ||
145 | /* Set our client connection to established. */ | |
146 | c->state = CONN_STATE_ESTABLISHED; | |
147 | ||
148 | /* Insert the backend into the list of Kore connections. */ | |
149 | TAILQ_INSERT_TAIL(&connections, backend, list); | |
150 | ||
151 | /* Kick off connecting. */ | |
152 | backend->flags |= CONN_WRITE_POSSIBLE; | |
153 | backend->handle(backend); | |
154 | } | |
155 | ||
156 | /* | |
157 | * This function is called for backends while they are connecting. | |
158 | * In here we check for write events and attempt to connect() to the | |
159 | * backend. | |
160 | * | |
161 | * Once a connection is established we set the backend handle function | |
162 | * pointer to the backend_handle_default() callback and setup the reads | |
163 | * for both the backend and the client connection we received. | |
164 | */ | |
165 | int | |
166 | backend_handle_connect(struct connection *c) | |
167 | { | |
168 | int ret; | |
169 | struct connection *src; | |
170 | ||
171 | /* We will get a write notification when we can progress. */ | |
172 | if (!(c->flags & CONN_WRITE_POSSIBLE)) | |
173 | return (KORE_RESULT_OK); | |
174 | ||
175 | kore_connection_stop_idletimer(c); | |
176 | ||
177 | /* Attempt connecting. */ | |
178 | ret = connect(c->fd, (struct sockaddr *)&c->addr.ipv4, | |
179 | sizeof(c->addr.ipv4)); | |
180 | ||
181 | /* If we failed check why, we are non blocking. */ | |
182 | if (ret == -1) { | |
183 | /* If we got a real error, disconnect. */ | |
184 | if (errno != EALREADY && errno != EINPROGRESS && | |
185 | errno != EISCONN) { | |
186 | kore_log(LOG_ERR, "connect(): %s", errno_s); | |
187 | return (KORE_RESULT_ERROR); | |
188 | } | |
189 | ||
190 | /* Clean the write flag, we'll be called later. */ | |
191 | if (errno != EISCONN) { | |
192 | c->flags &= ~CONN_WRITE_POSSIBLE; | |
193 | kore_connection_start_idletimer(c); | |
194 | return (KORE_RESULT_OK); | |
195 | } | |
196 | } | |
197 | ||
198 | /* The connection to the backend succeeded. */ | |
199 | c->handle = backend_handle_default; | |
200 | ||
201 | /* Setup read calls for both backend and its client. */ | |
202 | net_recv_queue(c, NETBUF_SEND_PAYLOAD_MAX, | |
203 | NETBUF_CALL_CB_ALWAYS, pipe_data); | |
204 | net_recv_queue(c->hdlr_extra, NETBUF_SEND_PAYLOAD_MAX, | |
205 | NETBUF_CALL_CB_ALWAYS, pipe_data); | |
206 | ||
207 | /* Allow for all events now. */ | |
208 | kore_connection_start_idletimer(c); | |
209 | kore_platform_event_all(c->fd, c); | |
210 | ||
211 | /* Allow events from source now. */ | |
212 | src = c->hdlr_extra; | |
213 | kore_platform_event_all(src->fd, src); | |
214 | ||
215 | /* Now lets start. */ | |
216 | return (c->handle(c)); | |
217 | } | |
218 | ||
219 | /* | |
220 | * Called for connection activity on a backend, just forwards | |
221 | * to the default Kore connection handling for now. | |
222 | */ | |
223 | int | |
224 | backend_handle_default(struct connection *c) | |
225 | { | |
226 | return (kore_connection_handle(c)); | |
227 | } | |
228 | ||
229 | /* | |
230 | * Called for connection activity on a client, just forwards | |
231 | * to the default Kore connection handling for now. | |
232 | */ | |
233 | int | |
234 | client_handle(struct connection *c) | |
235 | { | |
236 | return (kore_connection_handle(c)); | |
237 | } | |
238 | ||
239 | /* | |
240 | * Called whenever a client or its backend have disconnected. | |
241 | * This will disconnect the matching paired connection as well. | |
242 | */ | |
243 | void | |
244 | disconnect(struct connection *c) | |
245 | { | |
246 | struct connection *pair = c->hdlr_extra; | |
247 | ||
248 | c->hdlr_extra = NULL; | |
249 | ||
250 | if (pair != NULL) { | |
251 | pair->hdlr_extra = NULL; | |
252 | kore_connection_disconnect(pair); | |
253 | } | |
254 | } | |
255 | ||
256 | /* | |
257 | * Called whenever data is available that must be piped through | |
258 | * to the paired connection. (client<>backend or backend<>client). | |
259 | */ | |
260 | int | |
261 | pipe_data(struct netbuf *nb) | |
262 | { | |
263 | struct connection *src = nb->owner; | |
264 | struct connection *dst = src->hdlr_extra; | |
265 | ||
266 | /* Flush data out towards destination. */ | |
267 | net_send_queue(dst, nb->buf, nb->s_off); | |
268 | net_send_flush(dst); | |
269 | ||
270 | /* Reset read for source. */ | |
271 | net_recv_reset(src, NETBUF_SEND_PAYLOAD_MAX, pipe_data); | |
272 | ||
273 | return (KORE_RESULT_OK); | |
274 | } |
0 | # upload build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
0 | # Placeholder configuration | |
1 | ||
2 | bind 127.0.0.1 8888 | |
3 | load ./upload.so | |
4 | ||
5 | tls_dhparam dh2048.pem | |
6 | ||
7 | http_body_max 1024000000 | |
8 | http_body_disk_offload 4096 | |
9 | ||
10 | domain 127.0.0.1 { | |
11 | certfile cert/server.crt | |
12 | certkey cert/server.key | |
13 | ||
14 | static / page | |
15 | } |
0 | /* | |
1 | * Copyright (c) 2016 Joris Vink <joris@coders.se> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | /* | |
17 | * This example demonstrates how to properly deal with file uploads | |
18 | * coming from a multipart form. | |
19 | * | |
20 | * The basics are quite trivial: | |
21 | * 1) call http_populate_multipart_form() | |
22 | * 2) find the file using http_file_lookup(). | |
23 | * 3) read the file data using http_file_read(). | |
24 | * | |
25 | * In this example the contents is written to a newly created file | |
26 | * on the server that matches the naming given by the uploader. | |
27 | * | |
28 | * Note that the above is probably not what you want to do in real life. | |
29 | */ | |
30 | ||
31 | #include <kore/kore.h> | |
32 | #include <kore/http.h> | |
33 | ||
34 | #include <fcntl.h> | |
35 | #include <unistd.h> | |
36 | ||
37 | int page(struct http_request *); | |
38 | ||
39 | int | |
40 | page(struct http_request *req) | |
41 | { | |
42 | int fd; | |
43 | struct http_file *file; | |
44 | u_int8_t buf[BUFSIZ]; | |
45 | ssize_t ret, written; | |
46 | ||
47 | /* Only deal with POSTs. */ | |
48 | if (req->method != HTTP_METHOD_POST) { | |
49 | http_response(req, 405, NULL, 0); | |
50 | return (KORE_RESULT_OK); | |
51 | } | |
52 | ||
53 | /* Parse the multipart data that was present. */ | |
54 | http_populate_multipart_form(req); | |
55 | ||
56 | /* Find our file. */ | |
57 | if ((file = http_file_lookup(req, "file")) == NULL) { | |
58 | http_response(req, 400, NULL, 0); | |
59 | return (KORE_RESULT_OK); | |
60 | } | |
61 | ||
62 | /* Open dump file where we will write file contents. */ | |
63 | fd = open(file->filename, O_CREAT | O_TRUNC | O_WRONLY, 0700); | |
64 | if (fd == -1) { | |
65 | http_response(req, 500, NULL, 0); | |
66 | return (KORE_RESULT_OK); | |
67 | } | |
68 | ||
69 | /* While we have data from http_file_read(), write it. */ | |
70 | /* Alternatively you could look at file->offset and file->length. */ | |
71 | ret = KORE_RESULT_ERROR; | |
72 | for (;;) { | |
73 | ret = http_file_read(file, buf, sizeof(buf)); | |
74 | if (ret == -1) { | |
75 | kore_log(LOG_ERR, "failed to read from file"); | |
76 | http_response(req, 500, NULL, 0); | |
77 | goto cleanup; | |
78 | } | |
79 | ||
80 | if (ret == 0) | |
81 | break; | |
82 | ||
83 | written = write(fd, buf, ret); | |
84 | if (written == -1) { | |
85 | kore_log(LOG_ERR,"write(%s): %s", | |
86 | file->filename, errno_s); | |
87 | http_response(req, 500, NULL, 0); | |
88 | goto cleanup; | |
89 | } | |
90 | ||
91 | if (written != ret) { | |
92 | kore_log(LOG_ERR, "partial write on %s", | |
93 | file->filename); | |
94 | http_response(req, 500, NULL, 0); | |
95 | goto cleanup; | |
96 | } | |
97 | } | |
98 | ||
99 | ret = KORE_RESULT_OK; | |
100 | http_response(req, 200, NULL, 0); | |
101 | kore_log(LOG_INFO, "file '%s' successfully received", | |
102 | file->filename); | |
103 | ||
104 | cleanup: | |
105 | if (close(fd) == -1) | |
106 | kore_log(LOG_WARNING, "close(%s): %s", file->filename, errno_s); | |
107 | ||
108 | if (ret == KORE_RESULT_ERROR) { | |
109 | if (unlink(file->filename) == -1) { | |
110 | kore_log(LOG_WARNING, "unlink(%s): %s", | |
111 | file->filename, errno_s); | |
112 | } | |
113 | ret = KORE_RESULT_OK; | |
114 | } | |
115 | ||
116 | return (KORE_RESULT_OK); | |
117 | } |
0 | # video_stream build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
4 | 4 | |
5 | 5 | tls_dhparam dh2048.pem |
6 | 6 | |
7 | spdy_idle_time 600 | |
8 | 7 | http_keepalive_time 600 |
9 | 8 | |
10 | 9 | domain 127.0.0.1 { |
36 | 36 | TAILQ_ENTRY(video) list; |
37 | 37 | }; |
38 | 38 | |
39 | void init(int); | |
39 | int init(int); | |
40 | 40 | int serve_page(struct http_request *); |
41 | 41 | int video_stream(struct http_request *); |
42 | 42 | |
47 | 47 | |
48 | 48 | TAILQ_HEAD(, video) videos; |
49 | 49 | |
50 | void | |
50 | int | |
51 | 51 | init(int state) |
52 | 52 | { |
53 | switch (state) { | |
54 | case KORE_MODULE_LOAD: | |
55 | TAILQ_INIT(&videos); | |
56 | break; | |
57 | case KORE_MODULE_UNLOAD: | |
58 | fatal("cannot reload this module, i should fix this"); | |
59 | break; | |
60 | } | |
53 | if (state == KORE_MODULE_UNLOAD) { | |
54 | kore_log(LOG_NOTICE, "not reloading module"); | |
55 | return (KORE_RESULT_ERROR); | |
56 | } | |
57 | ||
58 | TAILQ_INIT(&videos); | |
59 | return (KORE_RESULT_OK); | |
61 | 60 | } |
62 | 61 | |
63 | 62 | int |
186 | 185 | |
187 | 186 | close(v->fd); |
188 | 187 | TAILQ_REMOVE(&videos, v, list); |
189 | kore_mem_free(v->path); | |
190 | kore_mem_free(v); | |
188 | kore_free(v->path); | |
189 | kore_free(v); | |
191 | 190 | |
192 | 191 | http_response(req, 500, NULL, 0); |
193 | 192 | return (KORE_RESULT_ERROR); |
201 | 200 | v->path = kore_strdup(fpath); |
202 | 201 | |
203 | 202 | if ((v->fd = open(fpath, O_RDONLY)) == -1) { |
204 | kore_mem_free(v->path); | |
205 | kore_mem_free(v); | |
203 | kore_free(v->path); | |
204 | kore_free(v); | |
206 | 205 | |
207 | 206 | if (errno == ENOENT) |
208 | 207 | http_response(req, 404, NULL, 0); |
214 | 213 | |
215 | 214 | if (fstat(v->fd, &st) == -1) { |
216 | 215 | close(v->fd); |
217 | kore_mem_free(v->path); | |
218 | kore_mem_free(v); | |
216 | kore_free(v->path); | |
217 | kore_free(v); | |
219 | 218 | |
220 | 219 | http_response(req, 500, NULL, 0); |
221 | 220 | return (KORE_RESULT_ERROR); |
224 | 223 | v->size = st.st_size; |
225 | 224 | if (!video_mmap(req, v)) { |
226 | 225 | close(v->fd); |
227 | kore_mem_free(v->path); | |
228 | kore_mem_free(v); | |
226 | kore_free(v->path); | |
227 | kore_free(v); | |
229 | 228 | |
230 | 229 | http_response(req, 500, NULL, 0); |
231 | 230 | return (KORE_RESULT_ERROR); |
0 | # websocket build config | |
1 | # You can switch flavors using: kore flavor [newflavor] | |
2 | ||
3 | # The cflags below are shared between flavors | |
4 | cflags=-Wall -Wmissing-declarations -Wshadow | |
5 | cflags=-Wstrict-prototypes -Wmissing-prototypes | |
6 | cflags=-Wpointer-arith -Wcast-qual -Wsign-compare | |
7 | ||
8 | dev { | |
9 | # These cflags are added to the shared ones when | |
10 | # you build the "dev" flavor. | |
11 | cflags=-g | |
12 | } | |
13 | ||
14 | #prod { | |
15 | # You can specify additional CFLAGS here which are only | |
16 | # included if you build with the "prod" flavor. | |
17 | #} |
3 | 3 | load ./websocket.so |
4 | 4 | |
5 | 5 | tls_dhparam dh2048.pem |
6 | ||
7 | # Increase workers so connections are spread | |
8 | # across them to demonstrate WEBSOCKET_BROADCAST_GLOBAL. | |
9 | workers 4 | |
6 | 10 | |
7 | 11 | websocket_maxframe 65536 |
8 | 12 | websocket_timeout 20 |
43 | 43 | void |
44 | 44 | websocket_message(struct connection *c, u_int8_t op, void *data, size_t len) |
45 | 45 | { |
46 | kore_websocket_broadcast(c, op, data, len, WEBSOCKET_BROADCAST_LOCAL); | |
46 | kore_websocket_broadcast(c, op, data, len, WEBSOCKET_BROADCAST_GLOBAL); | |
47 | 47 | } |
48 | 48 | |
49 | 49 | void |
13 | 13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 14 | */ |
15 | 15 | |
16 | #if !defined(KORE_NO_HTTP) | |
17 | ||
16 | 18 | #ifndef __H_HTTP_H |
17 | 19 | #define __H_HTTP_H |
18 | 20 | |
23 | 25 | #define HTTP_KEEPALIVE_TIME 20 |
24 | 26 | #define HTTP_HSTS_ENABLE 31536000 |
25 | 27 | #define HTTP_HEADER_MAX_LEN 4096 |
26 | #define HTTP_BODY_MAX_LEN 10240000 | |
28 | #define HTTP_BODY_MAX_LEN 1024000 | |
27 | 29 | #define HTTP_URI_LEN 2000 |
28 | 30 | #define HTTP_USERAGENT_LEN 256 |
29 | 31 | #define HTTP_REQ_HEADER_MAX 25 |
30 | #define HTTP_MAX_QUERY_ARGS 10 | |
32 | #define HTTP_MAX_QUERY_ARGS 20 | |
31 | 33 | #define HTTP_MAX_COOKIES 10 |
32 | 34 | #define HTTP_REQUEST_LIMIT 1000 |
35 | #define HTTP_BODY_DISK_PATH "tmp_files" | |
36 | #define HTTP_BODY_DISK_OFFLOAD 0 | |
37 | #define HTTP_BODY_PATH_MAX 256 | |
38 | #define HTTP_BOUNDARY_MAX 80 | |
33 | 39 | |
34 | 40 | #define HTTP_ARG_TYPE_RAW 0 |
35 | 41 | #define HTTP_ARG_TYPE_BYTE 1 |
55 | 61 | |
56 | 62 | struct http_arg { |
57 | 63 | char *name; |
58 | void *value; | |
59 | u_int32_t len; | |
60 | ||
61 | 64 | char *s_value; |
62 | u_int32_t s_len; | |
63 | 65 | |
64 | 66 | TAILQ_ENTRY(http_arg) list; |
65 | 67 | }; |
66 | 68 | |
67 | #define COPY_ARG_TYPE(v, l, t) \ | |
69 | #define COPY_ARG_TYPE(v, t) \ | |
68 | 70 | do { \ |
69 | if (l != NULL) \ | |
70 | *l = sizeof(t); \ | |
71 | 71 | *(t *)nout = v; \ |
72 | } while (0); | |
72 | } while (0) | |
73 | 73 | |
74 | 74 | #define COPY_ARG_INT64(type, sign) \ |
75 | 75 | do { \ |
78 | 78 | nval = (type)kore_strtonum64(q->s_value, sign, &err); \ |
79 | 79 | if (err != KORE_RESULT_OK) \ |
80 | 80 | return (KORE_RESULT_ERROR); \ |
81 | COPY_ARG_TYPE(nval, len, type); \ | |
82 | } while (0); | |
81 | COPY_ARG_TYPE(nval, type); \ | |
82 | } while (0) | |
83 | 83 | |
84 | 84 | #define COPY_ARG_INT(min, max, type) \ |
85 | 85 | do { \ |
88 | 88 | nval = kore_strtonum(q->s_value, 10, min, max, &err); \ |
89 | 89 | if (err != KORE_RESULT_OK) \ |
90 | 90 | return (KORE_RESULT_ERROR); \ |
91 | COPY_ARG_TYPE(nval, len, type); \ | |
92 | } while (0); | |
93 | ||
94 | #define CACHE_STRING() \ | |
95 | do { \ | |
96 | if (q->s_value == NULL) { \ | |
97 | q->s_len = q->len + 1; \ | |
98 | q->s_value = kore_malloc(q->s_len); \ | |
99 | kore_strlcpy(q->s_value, q->value, q->s_len); \ | |
100 | } \ | |
101 | } while (0); | |
91 | COPY_ARG_TYPE(nval, type); \ | |
92 | } while (0) | |
102 | 93 | |
103 | 94 | #define COPY_AS_INTTYPE_64(type, sign) \ |
104 | 95 | do { \ |
105 | 96 | if (nout == NULL) \ |
106 | 97 | return (KORE_RESULT_ERROR); \ |
107 | CACHE_STRING(); \ | |
108 | 98 | COPY_ARG_INT64(type, sign); \ |
109 | } while (0); | |
99 | } while (0) | |
110 | 100 | |
111 | 101 | #define COPY_AS_INTTYPE(min, max, type) \ |
112 | 102 | do { \ |
113 | 103 | if (nout == NULL) \ |
114 | 104 | return (KORE_RESULT_ERROR); \ |
115 | CACHE_STRING(); \ | |
116 | 105 | COPY_ARG_INT(min, max, type); \ |
117 | } while (0); | |
118 | ||
119 | #define http_argument_type(r, n, so, no, l, t) \ | |
120 | http_argument_get(r, n, so, no, l, t) | |
121 | ||
122 | #define http_argument_get_string(n, o, l) \ | |
123 | http_argument_type(req, n, (void **)o, NULL, l, HTTP_ARG_TYPE_STRING) | |
124 | ||
125 | #define http_argument_get_byte(n, o) \ | |
126 | http_argument_type(req, n, NULL, o, NULL, HTTP_ARG_TYPE_BYTE) | |
127 | ||
128 | #define http_argument_get_uint16(n, o) \ | |
129 | http_argument_type(req, n, NULL, o, NULL, HTTP_ARG_TYPE_UINT16) | |
130 | ||
131 | #define http_argument_get_int16(n, o) \ | |
132 | http_argument_type(req, n, NULL, o, NULL, HTTP_ARG_TYPE_INT16) | |
133 | ||
134 | #define http_argument_get_uint32(n, o) \ | |
135 | http_argument_type(req, n, NULL, o, NULL, HTTP_ARG_TYPE_UINT32) | |
136 | ||
137 | #define http_argument_get_int32(n, o) \ | |
138 | http_argument_type(req, n, NULL, o, NULL, HTTP_ARG_TYPE_INT32) | |
139 | ||
140 | #define http_argument_get_uint64(n, o) \ | |
141 | http_argument_type(req, n, NULL, o, NULL, HTTP_ARG_TYPE_UINT64) | |
142 | ||
143 | #define http_argument_get_int64(n, o) \ | |
144 | http_argument_type(req, n, NULL, o, NULL, HTTP_ARG_TYPE_INT64) | |
106 | } while (0) | |
107 | ||
108 | #define http_argument_type(r, n, so, no, t) \ | |
109 | http_argument_get(r, n, so, no, t) | |
110 | ||
111 | #define http_argument_get_string(r, n, o) \ | |
112 | http_argument_type(r, n, (void **)o, NULL, HTTP_ARG_TYPE_STRING) | |
113 | ||
114 | #define http_argument_get_byte(r, n, o) \ | |
115 | http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_BYTE) | |
116 | ||
117 | #define http_argument_get_uint16(r, n, o) \ | |
118 | http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_UINT16) | |
119 | ||
120 | #define http_argument_get_int16(r, n, o) \ | |
121 | http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_INT16) | |
122 | ||
123 | #define http_argument_get_uint32(r, n, o) \ | |
124 | http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_UINT32) | |
125 | ||
126 | #define http_argument_get_int32(r, n, o) \ | |
127 | http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_INT32) | |
128 | ||
129 | #define http_argument_get_uint64(r, n, o) \ | |
130 | http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_UINT64) | |
131 | ||
132 | #define http_argument_get_int64(r, n, o) \ | |
133 | http_argument_type(r, n, NULL, o, HTTP_ARG_TYPE_INT64) | |
145 | 134 | |
146 | 135 | |
147 | 136 | struct http_file { |
148 | 137 | char *name; |
149 | 138 | char *filename; |
150 | ||
151 | u_int8_t *data; | |
152 | u_int32_t len; | |
153 | ||
139 | size_t position; | |
140 | size_t offset; | |
141 | size_t length; | |
142 | struct http_request *req; | |
154 | 143 | TAILQ_ENTRY(http_file) list; |
155 | 144 | }; |
156 | 145 | |
160 | 149 | #define HTTP_METHOD_DELETE 3 |
161 | 150 | #define HTTP_METHOD_HEAD 4 |
162 | 151 | |
163 | #define HTTP_REQUEST_COMPLETE 0x01 | |
164 | #define HTTP_REQUEST_DELETE 0x02 | |
165 | #define HTTP_REQUEST_SLEEPING 0x04 | |
166 | #define HTTP_REQUEST_PGSQL_QUEUE 0x10 | |
167 | #define HTTP_REQUEST_EXPECT_BODY 0x20 | |
168 | #define HTTP_REQUEST_RETAIN_EXTRA 0x40 | |
169 | #define HTTP_REQUEST_NO_CONTENT_LENGTH 0x80 | |
152 | #define HTTP_REQUEST_COMPLETE 0x0001 | |
153 | #define HTTP_REQUEST_DELETE 0x0002 | |
154 | #define HTTP_REQUEST_SLEEPING 0x0004 | |
155 | #define HTTP_REQUEST_PGSQL_QUEUE 0x0010 | |
156 | #define HTTP_REQUEST_EXPECT_BODY 0x0020 | |
157 | #define HTTP_REQUEST_RETAIN_EXTRA 0x0040 | |
158 | #define HTTP_REQUEST_NO_CONTENT_LENGTH 0x0080 | |
159 | #define HTTP_REQUEST_AUTHED 0x0100 | |
170 | 160 | |
171 | 161 | struct kore_task; |
172 | 162 | |
173 | 163 | struct http_request { |
174 | 164 | u_int8_t method; |
175 | u_int8_t flags; | |
176 | 165 | u_int8_t fsm_state; |
166 | u_int16_t flags; | |
177 | 167 | u_int16_t status; |
178 | 168 | u_int64_t start; |
179 | 169 | u_int64_t end; |
182 | 172 | char *path; |
183 | 173 | char *agent; |
184 | 174 | struct connection *owner; |
185 | struct spdy_stream *stream; | |
186 | 175 | struct kore_buf *http_body; |
176 | int http_body_fd; | |
177 | char *http_body_path; | |
178 | size_t http_body_length; | |
179 | size_t http_body_offset; | |
180 | size_t content_length; | |
187 | 181 | void *hdlr_extra; |
188 | 182 | char *query_string; |
189 | u_int8_t *multipart_body; | |
190 | 183 | struct kore_module_handle *hdlr; |
191 | 184 | |
192 | 185 | LIST_HEAD(, kore_task) tasks; |
211 | 204 | extern u_int64_t http_hsts_enable; |
212 | 205 | extern u_int16_t http_keepalive_time; |
213 | 206 | extern u_int32_t http_request_limit; |
207 | extern u_int64_t http_body_disk_offload; | |
208 | extern char *http_body_disk_path; | |
209 | ||
210 | void kore_accesslog(struct http_request *); | |
214 | 211 | |
215 | 212 | void http_init(void); |
213 | void http_cleanup(void); | |
216 | 214 | void http_process(void); |
217 | 215 | const char *http_status_text(int); |
216 | const char *http_method_text(int); | |
218 | 217 | time_t http_date_to_time(char *); |
219 | 218 | void http_request_free(struct http_request *); |
220 | 219 | void http_request_sleep(struct http_request *); |
221 | 220 | void http_request_wakeup(struct http_request *); |
222 | char *http_body_text(struct http_request *); | |
223 | void http_process_request(struct http_request *, int); | |
224 | u_int8_t *http_body_bytes(struct http_request *, u_int32_t *); | |
225 | void http_response(struct http_request *, int, void *, u_int32_t); | |
221 | void http_process_request(struct http_request *); | |
222 | ssize_t http_body_read(struct http_request *, void *, size_t); | |
223 | void http_response(struct http_request *, int, const void *, size_t); | |
226 | 224 | void http_response_stream(struct http_request *, int, void *, |
227 | u_int64_t, int (*cb)(struct netbuf *), void *); | |
225 | size_t, int (*cb)(struct netbuf *), void *); | |
228 | 226 | int http_request_header(struct http_request *, |
229 | 227 | const char *, char **); |
230 | 228 | void http_response_header(struct http_request *, |
231 | 229 | const char *, const char *); |
232 | int http_request_new(struct connection *, struct spdy_stream *, | |
233 | const char *, const char *, const char *, const char *, | |
230 | int http_request_new(struct connection *, const char *, | |
231 | const char *, const char *, const char *, | |
234 | 232 | struct http_request **); |
235 | 233 | int http_state_run(struct http_state *, u_int8_t, |
236 | 234 | struct http_request *); |
237 | 235 | |
238 | 236 | int http_argument_urldecode(char *); |
239 | 237 | int http_header_recv(struct netbuf *); |
240 | int http_generic_404(struct http_request *); | |
241 | int http_populate_arguments(struct http_request *); | |
242 | int http_populate_multipart_form(struct http_request *, int *); | |
238 | void http_populate_get(struct http_request *); | |
239 | void http_populate_post(struct http_request *); | |
240 | void http_populate_multipart_form(struct http_request *); | |
243 | 241 | int http_argument_get(struct http_request *, |
244 | const char *, void **, void *, u_int32_t *, int); | |
245 | int http_file_lookup(struct http_request *, const char *, char **, | |
246 | u_int8_t **, u_int32_t *); | |
247 | ||
248 | void kore_accesslog(struct http_request *); | |
242 | const char *, void **, void *, int); | |
243 | ||
244 | void http_file_rewind(struct http_file *); | |
245 | ssize_t http_file_read(struct http_file *, void *, size_t); | |
246 | struct http_file *http_file_lookup(struct http_request *, const char *); | |
249 | 247 | |
250 | 248 | enum http_status_code { |
251 | 249 | HTTP_STATUS_CONTINUE = 100, |
293 | 291 | } |
294 | 292 | #endif |
295 | 293 | #endif /* !__H_HTTP_H */ |
294 | ||
295 | #endif /* ! KORE_NO_HTTP */ |
0 | /* | |
1 | * Copyright (c) 2016 Raphaël Monrouzeau <raphael.monrouzeau@gmail.com> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | #if !defined(KORE_NO_HTTP) | |
17 | ||
18 | #ifndef __H_JSONRPC_H | |
19 | #define __H_JSONRPC_H | |
20 | ||
21 | #if defined(__cplusplus) | |
22 | extern "C" { | |
23 | #endif | |
24 | ||
25 | /* JSON RPC request handling log entry. */ | |
26 | struct jsonrpc_log | |
27 | { | |
28 | char *msg; | |
29 | struct jsonrpc_log *next, *prev; | |
30 | int lvl; | |
31 | }; | |
32 | ||
33 | /* JSON RPC request. */ | |
34 | struct jsonrpc_request | |
35 | { | |
36 | struct jsonrpc_log log; | |
37 | struct kore_buf buf; | |
38 | struct http_request *http; | |
39 | yajl_gen gen; | |
40 | yajl_val json; | |
41 | yajl_val id; | |
42 | char *method; | |
43 | yajl_val params; | |
44 | unsigned int flags; | |
45 | int log_levels; | |
46 | }; | |
47 | ||
48 | #define YAJL_GEN_CONST_STRING(CTX, STR) \ | |
49 | yajl_gen_string((CTX), (unsigned char *)(STR), sizeof (STR) - 1) | |
50 | ||
51 | #define YAJL_GEN_CONST_NUMBER(CTX, STR) \ | |
52 | yajl_gen_number((CTX), (unsigned char *)(STR), sizeof (STR) - 1) | |
53 | ||
54 | #define YAJL_GEN_KO(OPERATION) \ | |
55 | ((OPERATION) != yajl_gen_status_ok) | |
56 | ||
57 | enum jsonrpc_error_code | |
58 | { | |
59 | #define JSONRPC_PARSE_ERROR_MSG "Parse error" | |
60 | JSONRPC_PARSE_ERROR = -32700, | |
61 | #define JSONRPC_INVALID_REQUEST_MSG "Invalid Request" | |
62 | JSONRPC_INVALID_REQUEST = -32600, | |
63 | #define JSONRPC_METHOD_NOT_FOUND_MSG "Method not found" | |
64 | JSONRPC_METHOD_NOT_FOUND = -32601, | |
65 | #define JSONRPC_INVALID_PARAMS_MSG "Invalid params" | |
66 | JSONRPC_INVALID_PARAMS = -32602, | |
67 | #define JSONRPC_INTERNAL_ERROR_MSG "Internal error" | |
68 | JSONRPC_INTERNAL_ERROR = -32603, | |
69 | #define JSONRPC_SERVER_ERROR_MSG "Server error" | |
70 | JSONRPC_SERVER_ERROR = -32000, | |
71 | #define JSONRPC_LIMIT_REACHED_MSG "Limit reached" | |
72 | JSONRPC_LIMIT_REACHED = -31997 | |
73 | }; | |
74 | ||
75 | void jsonrpc_log(struct jsonrpc_request *, int, const char *, ...); | |
76 | int jsonrpc_read_request(struct http_request *, struct jsonrpc_request *); | |
77 | void jsonrpc_destroy_request(struct jsonrpc_request *); | |
78 | int jsonrpc_error(struct jsonrpc_request *, int, const char *); | |
79 | int jsonrpc_result(struct jsonrpc_request *, | |
80 | int (*)(struct jsonrpc_request *, void *), void *); | |
81 | #if defined(__cplusplus) | |
82 | } | |
83 | #endif | |
84 | #endif /* !__H_JSONRPC_H */ | |
85 | ||
86 | #endif /* ! KORE_NO_HTTP */ |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
21 | 21 | #endif |
22 | 22 | |
23 | 23 | #include <sys/types.h> |
24 | #include <sys/time.h> | |
24 | 25 | #include <sys/queue.h> |
25 | 26 | |
26 | 27 | #include <netinet/in.h> |
27 | 28 | #include <arpa/inet.h> |
28 | 29 | |
30 | #if !defined(KORE_NO_TLS) | |
29 | 31 | #include <openssl/err.h> |
30 | 32 | #include <openssl/dh.h> |
31 | 33 | #include <openssl/ssl.h> |
34 | #endif | |
32 | 35 | |
33 | 36 | #include <errno.h> |
34 | 37 | #include <regex.h> |
38 | #include <stdarg.h> | |
39 | #include <stdlib.h> | |
40 | #include <stdio.h> | |
41 | #include <string.h> | |
35 | 42 | #include <syslog.h> |
36 | 43 | #include <unistd.h> |
37 | #include <zlib.h> | |
44 | #include <stdarg.h> | |
38 | 45 | |
39 | 46 | #if defined(__cplusplus) |
40 | 47 | extern "C" { |
45 | 52 | extern int daemon(int, int); |
46 | 53 | #endif |
47 | 54 | |
48 | #include "spdy.h" | |
49 | ||
50 | 55 | #define KORE_RESULT_ERROR 0 |
51 | 56 | #define KORE_RESULT_OK 1 |
52 | 57 | #define KORE_RESULT_RETRY 2 |
53 | 58 | |
54 | #define KORE_VERSION_MAJOR 1 | |
55 | #define KORE_VERSION_MINOR 2 | |
56 | #define KORE_VERSION_PATCH 3 | |
59 | #define KORE_VERSION_MAJOR 2 | |
60 | #define KORE_VERSION_MINOR 0 | |
61 | #define KORE_VERSION_PATCH 0 | |
57 | 62 | #define KORE_VERSION_STATE "release" |
58 | 63 | |
59 | 64 | #define KORE_TLS_VERSION_1_2 0 |
63 | 68 | #define errno_s strerror(errno) |
64 | 69 | #define ssl_errno_s ERR_error_string(ERR_get_error(), NULL) |
65 | 70 | |
66 | #define KORE_DOMAINNAME_LEN 254 | |
71 | #define KORE_DOMAINNAME_LEN 255 | |
67 | 72 | #define KORE_PIDFILE_DEFAULT "kore.pid" |
68 | 73 | #define KORE_DEFAULT_CIPHER_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!kRSA:!kDSA" |
69 | 74 | |
70 | 75 | #if defined(KORE_DEBUG) |
71 | #define kore_debug(fmt, ...) \ | |
76 | #define kore_debug(...) \ | |
72 | 77 | if (kore_debug) \ |
73 | kore_debug_internal(__FILE__, __LINE__, fmt, ##__VA_ARGS__) | |
78 | kore_debug_internal(__FILE__, __LINE__, __VA_ARGS__) | |
74 | 79 | #else |
75 | #define kore_debug(fmt, ...) | |
80 | #define kore_debug(...) | |
76 | 81 | #endif |
77 | 82 | |
78 | 83 | #define NETBUF_RECV 0 |
94 | 99 | #define X509_CN_LENGTH (ub_common_name + 1) |
95 | 100 | |
96 | 101 | /* XXX hackish. */ |
102 | #if !defined(KORE_NO_HTTP) | |
97 | 103 | struct http_request; |
98 | struct spdy_stream; | |
104 | #endif | |
99 | 105 | |
100 | 106 | struct netbuf { |
101 | 107 | u_int8_t *buf; |
102 | u_int32_t s_off; | |
103 | u_int32_t b_len; | |
104 | u_int32_t m_len; | |
108 | size_t s_off; | |
109 | size_t b_len; | |
110 | size_t m_len; | |
105 | 111 | u_int8_t type; |
106 | 112 | u_int8_t flags; |
107 | 113 | |
108 | 114 | void *owner; |
109 | struct spdy_stream *stream; | |
110 | 115 | |
111 | 116 | void *extra; |
112 | 117 | int (*cb)(struct netbuf *); |
121 | 126 | #define KORE_TYPE_PGSQL_CONN 3 |
122 | 127 | #define KORE_TYPE_TASK 4 |
123 | 128 | |
124 | struct listener { | |
125 | u_int8_t type; | |
126 | ||
127 | int fd; | |
128 | u_int8_t addrtype; | |
129 | ||
130 | union { | |
131 | struct sockaddr_in ipv4; | |
132 | struct sockaddr_in6 ipv6; | |
133 | } addr; | |
134 | ||
135 | LIST_ENTRY(listener) list; | |
136 | }; | |
137 | ||
138 | LIST_HEAD(listener_head, listener); | |
139 | ||
140 | 129 | #define CONN_STATE_UNKNOWN 0 |
141 | 130 | #define CONN_STATE_SSL_SHAKE 1 |
142 | 131 | #define CONN_STATE_ESTABLISHED 2 |
143 | 132 | #define CONN_STATE_DISCONNECTING 3 |
144 | 133 | |
145 | 134 | #define CONN_PROTO_UNKNOWN 0 |
146 | #define CONN_PROTO_SPDY 1 | |
147 | #define CONN_PROTO_HTTP 2 | |
148 | #define CONN_PROTO_WEBSOCKET 3 | |
135 | #define CONN_PROTO_HTTP 1 | |
136 | #define CONN_PROTO_WEBSOCKET 2 | |
137 | #define CONN_PROTO_MSG 3 | |
149 | 138 | |
150 | 139 | #define CONN_READ_POSSIBLE 0x01 |
151 | 140 | #define CONN_WRITE_POSSIBLE 0x02 |
153 | 142 | #define CONN_IDLE_TIMER_ACT 0x10 |
154 | 143 | #define CONN_READ_BLOCK 0x20 |
155 | 144 | #define CONN_CLOSE_EMPTY 0x40 |
156 | #define SPDY_CONN_GOAWAY 0x80 | |
157 | 145 | |
158 | 146 | #define KORE_IDLE_TIMER_MAX 20000 |
159 | 147 | |
169 | 157 | |
170 | 158 | #define KORE_TIMER_ONESHOT 0x01 |
171 | 159 | |
160 | #define KORE_CONNECTION_PRUNE_DISCONNECT 0 | |
161 | #define KORE_CONNECTION_PRUNE_ALL 1 | |
162 | ||
172 | 163 | struct connection { |
173 | 164 | u_int8_t type; |
174 | 165 | int fd; |
175 | 166 | u_int8_t state; |
176 | 167 | u_int8_t proto; |
177 | 168 | void *owner; |
169 | #if !defined(KORE_NO_TLS) | |
170 | X509 *cert; | |
178 | 171 | SSL *ssl; |
172 | int tls_reneg; | |
173 | #endif | |
179 | 174 | u_int8_t flags; |
180 | 175 | void *hdlr_extra; |
181 | X509 *cert; | |
182 | void *wscbs; | |
183 | int tls_reneg; | |
184 | ||
176 | ||
177 | int (*handle)(struct connection *); | |
185 | 178 | void (*disconnect)(struct connection *); |
186 | 179 | int (*read)(struct connection *, int *); |
187 | 180 | int (*write)(struct connection *, int, int *); |
197 | 190 | u_int64_t start; |
198 | 191 | } idle_timer; |
199 | 192 | |
200 | u_int8_t inflate_started; | |
201 | z_stream z_inflate; | |
202 | u_int8_t deflate_started; | |
203 | z_stream z_deflate; | |
204 | ||
205 | u_int32_t wsize_initial; | |
206 | u_int32_t spdy_send_wsize; | |
207 | u_int32_t spdy_recv_wsize; | |
208 | ||
209 | 193 | struct netbuf_head send_queue; |
210 | 194 | struct netbuf *snb; |
211 | 195 | struct netbuf *rnb; |
212 | 196 | |
213 | u_int32_t client_stream_id; | |
214 | TAILQ_HEAD(, spdy_stream) spdy_streams; | |
197 | #if !defined(KORE_NO_HTTP) | |
198 | void *wscbs; | |
215 | 199 | TAILQ_HEAD(, http_request) http_requests; |
200 | #endif | |
216 | 201 | |
217 | 202 | TAILQ_ENTRY(connection) list; |
218 | TAILQ_ENTRY(connection) flush_list; | |
219 | 203 | }; |
220 | 204 | |
221 | 205 | TAILQ_HEAD(connection_list, connection); |
222 | extern struct connection_list worker_clients; | |
206 | extern struct connection_list connections; | |
207 | extern struct connection_list disconnected; | |
208 | ||
209 | struct listener { | |
210 | u_int8_t type; | |
211 | u_int8_t addrtype; | |
212 | int fd; | |
213 | void (*connect)(struct connection *); | |
214 | ||
215 | union { | |
216 | struct sockaddr_in ipv4; | |
217 | struct sockaddr_in6 ipv6; | |
218 | } addr; | |
219 | ||
220 | LIST_ENTRY(listener) list; | |
221 | }; | |
222 | ||
223 | LIST_HEAD(listener_head, listener); | |
224 | ||
225 | #if !defined(KORE_NO_HTTP) | |
223 | 226 | |
224 | 227 | struct kore_handler_params { |
225 | 228 | char *name; |
243 | 246 | TAILQ_ENTRY(kore_auth) list; |
244 | 247 | }; |
245 | 248 | |
249 | #define HANDLER_TYPE_STATIC 1 | |
250 | #define HANDLER_TYPE_DYNAMIC 2 | |
251 | ||
252 | #endif | |
253 | ||
246 | 254 | #define KORE_MODULE_LOAD 1 |
247 | 255 | #define KORE_MODULE_UNLOAD 2 |
248 | ||
249 | #define HANDLER_TYPE_STATIC 1 | |
250 | #define HANDLER_TYPE_DYNAMIC 2 | |
251 | 256 | |
252 | 257 | struct kore_module { |
253 | 258 | void *handle; |
268 | 273 | int errors; |
269 | 274 | regex_t rctx; |
270 | 275 | struct kore_domain *dom; |
276 | #if !defined(KORE_NO_HTTP) | |
271 | 277 | struct kore_auth *auth; |
272 | ||
273 | 278 | TAILQ_HEAD(, kore_handler_params) params; |
279 | #endif | |
274 | 280 | TAILQ_ENTRY(kore_module_handle) list; |
275 | 281 | }; |
276 | 282 | |
278 | 284 | u_int8_t id; |
279 | 285 | u_int8_t cpu; |
280 | 286 | pid_t pid; |
287 | int pipe[2]; | |
288 | struct connection *msg[2]; | |
281 | 289 | u_int8_t has_lock; |
282 | 290 | struct kore_module_handle *active_hdlr; |
283 | 291 | }; |
284 | 292 | |
285 | 293 | struct kore_domain { |
286 | 294 | char *domain; |
295 | int accesslog; | |
296 | #if !defined(KORE_NO_TLS) | |
297 | char *cafile; | |
298 | char *crlfile; | |
287 | 299 | char *certfile; |
288 | 300 | char *certkey; |
289 | char *cafile; | |
290 | char *crlfile; | |
291 | int accesslog; | |
292 | 301 | SSL_CTX *ssl_ctx; |
302 | #endif | |
293 | 303 | TAILQ_HEAD(, kore_module_handle) handlers; |
294 | 304 | TAILQ_ENTRY(kore_domain) list; |
295 | 305 | }; |
296 | 306 | |
297 | 307 | TAILQ_HEAD(kore_domain_h, kore_domain); |
308 | ||
309 | #if !defined(KORE_NO_HTTP) | |
298 | 310 | |
299 | 311 | #define KORE_VALIDATOR_TYPE_REGEX 1 |
300 | 312 | #define KORE_VALIDATOR_TYPE_FUNCTION 2 |
308 | 320 | |
309 | 321 | TAILQ_ENTRY(kore_validator) list; |
310 | 322 | }; |
311 | ||
312 | #define KORE_BUF_INITIAL 128 | |
313 | #define KORE_BUF_INCREMENT KORE_BUF_INITIAL | |
323 | #endif | |
324 | ||
325 | #define KORE_BUF_OWNER_API 0x0001 | |
314 | 326 | |
315 | 327 | struct kore_buf { |
316 | 328 | u_int8_t *data; |
317 | u_int64_t length; | |
318 | u_int64_t offset; | |
329 | int flags; | |
330 | size_t length; | |
331 | size_t offset; | |
319 | 332 | }; |
320 | 333 | |
321 | 334 | struct kore_pool_region { |
322 | void *start; | |
335 | void *start; | |
336 | size_t length; | |
323 | 337 | LIST_ENTRY(kore_pool_region) list; |
324 | 338 | }; |
325 | 339 | |
330 | 344 | }; |
331 | 345 | |
332 | 346 | struct kore_pool { |
333 | u_int32_t elen; | |
334 | u_int32_t slen; | |
335 | u_int32_t elms; | |
336 | u_int32_t inuse; | |
347 | size_t elen; | |
348 | size_t slen; | |
349 | size_t elms; | |
350 | size_t inuse; | |
351 | volatile int lock; | |
337 | 352 | char *name; |
338 | 353 | |
339 | 354 | LIST_HEAD(, kore_pool_region) regions; |
352 | 367 | u_int64_t interval; |
353 | 368 | int flags; |
354 | 369 | void *arg; |
355 | void (*cb)(void *, u_int64_t, u_int64_t); | |
370 | void (*cb)(void *, u_int64_t); | |
356 | 371 | |
357 | 372 | TAILQ_ENTRY(kore_timer) list; |
358 | 373 | }; |
374 | ||
375 | #define KORE_WORKER_KEYMGR 0 | |
376 | ||
377 | /* Reserved message ids, registered on workers. */ | |
378 | #define KORE_MSG_ACCESSLOG 1 | |
379 | #define KORE_MSG_WEBSOCKET 2 | |
380 | #define KORE_MSG_KEYMGR_REQ 3 | |
381 | #define KORE_MSG_KEYMGR_RESP 4 | |
382 | ||
383 | /* Predefined message targets. */ | |
384 | #define KORE_MSG_PARENT 1000 | |
385 | #define KORE_MSG_WORKER_ALL 1001 | |
386 | ||
387 | struct kore_msg { | |
388 | u_int8_t id; | |
389 | u_int16_t src; | |
390 | u_int16_t dst; | |
391 | u_int32_t length; | |
392 | }; | |
393 | ||
394 | #if !defined(KORE_NO_TLS) | |
395 | struct kore_keyreq { | |
396 | int padding; | |
397 | char domain[KORE_DOMAINNAME_LEN]; | |
398 | u_int8_t domain_len; | |
399 | u_int16_t data_len; | |
400 | u_int8_t data[]; | |
401 | }; | |
402 | #endif | |
403 | ||
404 | #if !defined(KORE_SINGLE_BINARY) | |
405 | extern char *config_file; | |
406 | #endif | |
359 | 407 | |
360 | 408 | extern pid_t kore_pid; |
361 | 409 | extern int foreground; |
365 | 413 | extern int skip_runas; |
366 | 414 | extern char *runas_user; |
367 | 415 | extern char *kore_pidfile; |
368 | extern char *config_file; | |
369 | 416 | extern char *kore_tls_cipher_list; |
370 | 417 | extern int tls_version; |
418 | ||
419 | #if !defined(KORE_NO_TLS) | |
371 | 420 | extern DH *tls_dhparam; |
421 | #endif | |
372 | 422 | |
373 | 423 | extern u_int8_t nlisteners; |
374 | extern u_int64_t spdy_idle_time; | |
375 | 424 | extern u_int16_t cpu_count; |
376 | 425 | extern u_int8_t worker_count; |
377 | 426 | extern u_int8_t worker_set_affinity; |
396 | 445 | void kore_worker_wait(int); |
397 | 446 | void kore_worker_init(void); |
398 | 447 | void kore_worker_shutdown(void); |
448 | void kore_worker_privdrop(void); | |
399 | 449 | void kore_worker_dispatch_signal(int); |
400 | 450 | void kore_worker_spawn(u_int16_t, u_int16_t); |
401 | 451 | void kore_worker_entry(struct kore_worker *); |
402 | void kore_worker_connection_add(struct connection *); | |
403 | void kore_worker_connection_move(struct connection *); | |
404 | void kore_worker_connection_remove(struct connection *); | |
405 | void kore_worker_websocket_broadcast(struct connection *, | |
406 | void (*cb)(struct connection *, void *), void *); | |
452 | ||
453 | struct kore_worker *kore_worker_data(u_int8_t); | |
407 | 454 | |
408 | 455 | void kore_platform_init(void); |
409 | 456 | void kore_platform_event_init(void); |
457 | void kore_platform_event_cleanup(void); | |
410 | 458 | void kore_platform_proctitle(char *); |
411 | 459 | void kore_platform_disable_read(int); |
412 | 460 | void kore_platform_enable_accept(void); |
414 | 462 | int kore_platform_event_wait(u_int64_t); |
415 | 463 | void kore_platform_event_all(int, void *); |
416 | 464 | void kore_platform_schedule_read(int, void *); |
465 | void kore_platform_schedule_write(int, void *); | |
417 | 466 | void kore_platform_event_schedule(int, int, int, void *); |
418 | 467 | void kore_platform_worker_setcpu(struct kore_worker *); |
419 | 468 | |
420 | 469 | void kore_accesslog_init(void); |
421 | int kore_accesslog_wait(void); | |
422 | 470 | void kore_accesslog_worker_init(void); |
423 | ||
471 | int kore_accesslog_write(const void *, u_int32_t); | |
472 | ||
473 | #if !defined(KORE_NO_HTTP) | |
424 | 474 | int kore_auth_run(struct http_request *, struct kore_auth *); |
425 | 475 | void kore_auth_init(void); |
426 | 476 | int kore_auth_new(const char *); |
427 | 477 | struct kore_auth *kore_auth_lookup(const char *); |
478 | #endif | |
428 | 479 | |
429 | 480 | void kore_timer_init(void); |
430 | 481 | u_int64_t kore_timer_run(u_int64_t); |
431 | 482 | void kore_timer_remove(struct kore_timer *); |
432 | struct kore_timer *kore_timer_add(void (*cb)(void *, u_int64_t, | |
433 | u_int64_t), u_int64_t, void *, int); | |
434 | ||
483 | struct kore_timer *kore_timer_add(void (*cb)(void *, u_int64_t), | |
484 | u_int64_t, void *, int); | |
485 | ||
486 | void kore_listener_cleanup(void); | |
487 | int kore_server_bind(const char *, const char *, const char *); | |
488 | #if !defined(KORE_NO_TLS) | |
435 | 489 | int kore_tls_sni_cb(SSL *, int *, void *); |
436 | int kore_server_bind(const char *, const char *); | |
437 | int kore_tls_npn_cb(SSL *, const u_char **, unsigned int *, void *); | |
438 | 490 | void kore_tls_info_callback(const SSL *, int, int); |
491 | #endif | |
439 | 492 | |
440 | 493 | void kore_connection_init(void); |
494 | void kore_connection_cleanup(void); | |
495 | void kore_connection_prune(int); | |
441 | 496 | struct connection *kore_connection_new(void *); |
442 | int kore_connection_nonblock(int); | |
497 | void kore_connection_check_timeout(void); | |
498 | int kore_connection_nonblock(int, int); | |
443 | 499 | int kore_connection_handle(struct connection *); |
444 | 500 | void kore_connection_remove(struct connection *); |
445 | 501 | void kore_connection_disconnect(struct connection *); |
457 | 513 | void kore_parse_config(void); |
458 | 514 | void *kore_calloc(size_t, size_t); |
459 | 515 | void *kore_realloc(void *, size_t); |
460 | void kore_mem_free(void *); | |
516 | void kore_free(void *); | |
461 | 517 | void kore_mem_init(void); |
462 | ||
463 | #if defined(KORE_PEDANTIC_MALLOC) | |
464 | void explicit_bzero(void *, size_t); | |
465 | #endif | |
466 | 518 | |
467 | 519 | void *kore_pool_get(struct kore_pool *); |
468 | 520 | void kore_pool_put(struct kore_pool *, void *); |
469 | 521 | void kore_pool_init(struct kore_pool *, const char *, |
470 | u_int32_t, u_int32_t); | |
522 | size_t, size_t); | |
523 | void kore_pool_cleanup(struct kore_pool *); | |
471 | 524 | |
472 | 525 | time_t kore_date_to_time(char *); |
473 | 526 | char *kore_time_to_date(time_t); |
474 | 527 | char *kore_strdup(const char *); |
475 | 528 | void kore_log(int, const char *, ...); |
476 | 529 | u_int64_t kore_strtonum64(const char *, int, int *); |
477 | void kore_strlcpy(char *, const char *, size_t); | |
530 | size_t kore_strlcpy(char *, const char *, const size_t); | |
478 | 531 | void kore_server_disconnect(struct connection *); |
479 | 532 | int kore_split_string(char *, char *, char **, size_t); |
480 | void kore_strip_chars(char *, char, char **); | |
533 | void kore_strip_chars(char *, const char, char **); | |
481 | 534 | int kore_snprintf(char *, size_t, int *, const char *, ...); |
482 | 535 | long long kore_strtonum(const char *, int, long long, long long, int *); |
483 | int kore_base64_encode(u_int8_t *, u_int32_t, char **); | |
484 | int kore_base64_decode(char *, u_int8_t **, u_int32_t *); | |
485 | void *kore_mem_find(void *, size_t, void *, u_int32_t); | |
486 | ||
536 | int kore_base64_encode(u_int8_t *, size_t, char **); | |
537 | int kore_base64_decode(char *, u_int8_t **, size_t *); | |
538 | void *kore_mem_find(void *, size_t, void *, size_t); | |
539 | char *kore_text_trim(char *, size_t); | |
540 | char *kore_read_line(FILE *, char *, size_t); | |
541 | ||
542 | #if !defined(KORE_NO_HTTP) | |
487 | 543 | void kore_websocket_handshake(struct http_request *, |
488 | 544 | struct kore_wscbs *); |
489 | 545 | void kore_websocket_send(struct connection *, |
490 | u_int8_t, void *, size_t); | |
546 | u_int8_t, const void *, size_t); | |
491 | 547 | void kore_websocket_broadcast(struct connection *, |
492 | u_int8_t, void *, size_t, int); | |
548 | u_int8_t, const void *, size_t, int); | |
549 | #endif | |
550 | ||
551 | void kore_msg_init(void); | |
552 | void kore_msg_worker_init(void); | |
553 | void kore_msg_parent_init(void); | |
554 | void kore_msg_parent_add(struct kore_worker *); | |
555 | void kore_msg_parent_remove(struct kore_worker *); | |
556 | void kore_msg_send(u_int16_t, u_int8_t, const void *, u_int32_t); | |
557 | int kore_msg_register(u_int8_t, | |
558 | void (*cb)(struct kore_msg *, const void *)); | |
493 | 559 | |
494 | 560 | void kore_domain_init(void); |
561 | void kore_domain_cleanup(void); | |
495 | 562 | int kore_domain_new(char *); |
563 | void kore_domain_free(struct kore_domain *); | |
496 | 564 | void kore_module_init(void); |
565 | void kore_module_cleanup(void); | |
497 | 566 | void kore_module_reload(int); |
498 | 567 | void kore_module_onload(void); |
499 | 568 | int kore_module_loaded(void); |
500 | 569 | void kore_domain_closelogs(void); |
501 | 570 | void *kore_module_getsym(const char *); |
502 | 571 | void kore_domain_load_crl(void); |
572 | void kore_domain_keymgr_init(void); | |
503 | 573 | void kore_module_load(const char *, const char *); |
504 | 574 | void kore_domain_sslstart(struct kore_domain *); |
575 | void kore_domain_callback(void (*cb)(struct kore_domain *)); | |
505 | 576 | int kore_module_handler_new(const char *, const char *, |
506 | 577 | const char *, const char *, int); |
578 | void kore_module_handler_free(struct kore_module_handle *); | |
507 | 579 | |
508 | 580 | struct kore_domain *kore_domain_lookup(const char *); |
509 | 581 | struct kore_module_handle *kore_module_handler_find(const char *, |
510 | 582 | const char *); |
511 | 583 | |
584 | #if !defined(KORE_NO_HTTP) | |
512 | 585 | void kore_validator_init(void); |
513 | 586 | void kore_validator_reload(void); |
514 | 587 | int kore_validator_add(const char *, u_int8_t, const char *); |
516 | 589 | int kore_validator_check(struct http_request *, |
517 | 590 | struct kore_validator *, void *); |
518 | 591 | struct kore_validator *kore_validator_lookup(const char *); |
592 | #endif | |
519 | 593 | |
520 | 594 | void fatal(const char *, ...) __attribute__((noreturn)); |
521 | 595 | void kore_debug_internal(char *, int, const char *, ...); |
528 | 602 | void net_write64(u_int8_t *, u_int64_t); |
529 | 603 | |
530 | 604 | void net_init(void); |
605 | void net_cleanup(void); | |
531 | 606 | int net_send(struct connection *); |
532 | 607 | int net_send_flush(struct connection *); |
533 | 608 | int net_recv_flush(struct connection *); |
535 | 610 | int net_read_ssl(struct connection *, int *); |
536 | 611 | int net_write(struct connection *, int, int *); |
537 | 612 | int net_write_ssl(struct connection *, int, int *); |
538 | void net_recv_reset(struct connection *, u_int32_t, | |
613 | void net_recv_reset(struct connection *, size_t, | |
539 | 614 | int (*cb)(struct netbuf *)); |
540 | 615 | void net_remove_netbuf(struct netbuf_head *, struct netbuf *); |
541 | void net_recv_queue(struct connection *, u_int32_t, int, | |
616 | void net_recv_queue(struct connection *, size_t, int, | |
542 | 617 | int (*cb)(struct netbuf *)); |
543 | void net_recv_expand(struct connection *c, u_int32_t, | |
618 | void net_recv_expand(struct connection *c, size_t, | |
544 | 619 | int (*cb)(struct netbuf *)); |
545 | void net_send_queue(struct connection *, void *, | |
546 | u_int32_t, struct spdy_stream *, int); | |
620 | void net_send_queue(struct connection *, const void *, size_t); | |
547 | 621 | void net_send_stream(struct connection *, void *, |
548 | u_int32_t, struct spdy_stream *, | |
549 | int (*cb)(struct netbuf *), struct netbuf **); | |
622 | size_t, int (*cb)(struct netbuf *), struct netbuf **); | |
550 | 623 | |
551 | 624 | void kore_buf_free(struct kore_buf *); |
552 | struct kore_buf *kore_buf_create(u_int32_t); | |
553 | void kore_buf_append(struct kore_buf *, void *, u_int32_t); | |
554 | u_int8_t *kore_buf_release(struct kore_buf *, u_int32_t *); | |
625 | struct kore_buf *kore_buf_alloc(size_t); | |
626 | void kore_buf_init(struct kore_buf *, size_t); | |
627 | void kore_buf_append(struct kore_buf *, const void *, size_t); | |
628 | u_int8_t *kore_buf_release(struct kore_buf *, size_t *); | |
629 | void kore_buf_reset(struct kore_buf *); | |
630 | void kore_buf_cleanup(struct kore_buf *); | |
631 | ||
632 | char *kore_buf_stringify(struct kore_buf *, size_t *); | |
555 | 633 | void kore_buf_appendf(struct kore_buf *, const char *, ...); |
556 | 634 | void kore_buf_appendv(struct kore_buf *, const char *, va_list); |
557 | void kore_buf_appendb(struct kore_buf *, struct kore_buf *); | |
558 | 635 | void kore_buf_replace_string(struct kore_buf *, char *, void *, size_t); |
559 | 636 | |
560 | struct spdy_stream *spdy_stream_lookup(struct connection *, u_int32_t); | |
561 | int spdy_stream_get_header(struct spdy_header_block *, | |
562 | const char *, char **); | |
563 | void spdy_update_wsize(struct connection *, | |
564 | struct spdy_stream *, u_int32_t); | |
565 | ||
566 | int spdy_frame_recv(struct netbuf *); | |
567 | int spdy_dataframe_begin(struct connection *); | |
568 | void spdy_session_teardown(struct connection *c, u_int8_t); | |
569 | void spdy_frame_send(struct connection *, u_int16_t, | |
570 | u_int8_t, u_int32_t, struct spdy_stream *, u_int32_t); | |
571 | void spdy_header_block_add(struct spdy_header_block *, | |
572 | char *, char *); | |
573 | u_int8_t *spdy_header_block_release(struct connection *, | |
574 | struct spdy_header_block *, u_int32_t *); | |
575 | void spdy_stream_close(struct connection *, | |
576 | struct spdy_stream *, int); | |
577 | ||
578 | struct spdy_header_block *spdy_header_block_create(int); | |
637 | void kore_keymgr_run(void); | |
638 | void kore_keymgr_cleanup(void); | |
639 | ||
640 | #if defined(KORE_SINGLE_BINARY) | |
641 | void kore_onload(void); | |
642 | #endif | |
579 | 643 | |
580 | 644 | #if defined(__cplusplus) |
581 | 645 | } |
21 | 21 | #define KORE_PGSQL_FORMAT_TEXT 0 |
22 | 22 | #define KORE_PGSQL_FORMAT_BINARY 1 |
23 | 23 | |
24 | #define KORE_PGSQL_SYNC 0x0001 | |
25 | #define KORE_PGSQL_ASYNC 0x0002 | |
26 | ||
24 | 27 | #if defined(__cplusplus) |
25 | 28 | extern "C" { |
26 | 29 | #endif |
28 | 31 | struct pgsql_conn { |
29 | 32 | u_int8_t type; |
30 | 33 | u_int8_t flags; |
34 | char *name; | |
31 | 35 | |
32 | 36 | PGconn *db; |
33 | 37 | struct pgsql_job *job; |
34 | 38 | TAILQ_ENTRY(pgsql_conn) list; |
35 | 39 | }; |
36 | 40 | |
41 | struct pgsql_db { | |
42 | char *name; | |
43 | char *conn_string; | |
44 | ||
45 | LIST_ENTRY(pgsql_db) rlist; | |
46 | }; | |
47 | ||
37 | 48 | struct kore_pgsql { |
38 | 49 | u_int8_t state; |
50 | int flags; | |
39 | 51 | char *error; |
40 | 52 | PGresult *result; |
41 | 53 | struct pgsql_conn *conn; |
44 | 56 | }; |
45 | 57 | |
46 | 58 | extern u_int16_t pgsql_conn_max; |
47 | extern char *pgsql_conn_string; | |
48 | 59 | |
49 | 60 | void kore_pgsql_init(void); |
61 | int kore_pgsql_query_init(struct kore_pgsql *, struct http_request *, | |
62 | const char *, int); | |
50 | 63 | void kore_pgsql_handle(void *, int); |
51 | 64 | void kore_pgsql_cleanup(struct kore_pgsql *); |
52 | 65 | void kore_pgsql_continue(struct http_request *, struct kore_pgsql *); |
53 | int kore_pgsql_query(struct kore_pgsql *, struct http_request *, | |
54 | const char *); | |
55 | int kore_pgsql_query_params(struct kore_pgsql *, struct http_request *, | |
66 | int kore_pgsql_query(struct kore_pgsql *, const char *); | |
67 | int kore_pgsql_query_params(struct kore_pgsql *, | |
56 | 68 | const char *, int, u_int8_t, ...); |
57 | ||
69 | int kore_pgsql_v_query_params(struct kore_pgsql *, | |
70 | const char *, int, u_int8_t, va_list); | |
71 | int kore_pgsql_register(const char *, const char *); | |
58 | 72 | int kore_pgsql_ntuples(struct kore_pgsql *); |
59 | 73 | void kore_pgsql_logerror(struct kore_pgsql *); |
60 | 74 | void kore_pgsql_queue_remove(struct http_request *); |
0 | /* | |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | #ifndef __H_SPDY_H | |
17 | #define __H_SPDY_H | |
18 | ||
19 | #include <sys/types.h> | |
20 | #include <sys/queue.h> | |
21 | ||
22 | #if defined(__cplusplus) | |
23 | extern "C" { | |
24 | #endif | |
25 | ||
26 | /* XXX */ | |
27 | struct connection; | |
28 | struct http_request; | |
29 | ||
30 | struct spdy_ctrl_frame { | |
31 | u_int16_t version; | |
32 | u_int16_t type; | |
33 | u_int8_t flags; | |
34 | u_int32_t length; | |
35 | }; | |
36 | ||
37 | struct spdy_data_frame { | |
38 | u_int32_t stream_id; | |
39 | u_int8_t flags; | |
40 | u_int32_t length; | |
41 | }; | |
42 | ||
43 | struct spdy_syn_stream { | |
44 | u_int32_t stream_id; | |
45 | u_int32_t assoc_stream_id; | |
46 | u_int8_t slot; | |
47 | u_int8_t reserved; | |
48 | u_int8_t prio; | |
49 | }; | |
50 | ||
51 | struct spdy_header_block { | |
52 | u_int8_t *header_block; | |
53 | u_int32_t header_block_len; | |
54 | u_int32_t header_offset; | |
55 | u_int32_t header_pairs; | |
56 | }; | |
57 | ||
58 | struct spdy_stream { | |
59 | u_int32_t stream_id; | |
60 | u_int8_t flags; | |
61 | u_int8_t prio; | |
62 | u_int64_t post_size; | |
63 | u_int64_t send_size; | |
64 | u_int32_t frame_size; | |
65 | u_int32_t recv_wsize; | |
66 | u_int32_t send_wsize; | |
67 | void (*onclose)(struct connection *, struct spdy_stream *); | |
68 | ||
69 | struct http_request *httpreq; | |
70 | struct spdy_header_block *hblock; | |
71 | TAILQ_ENTRY(spdy_stream) list; | |
72 | }; | |
73 | ||
74 | extern const unsigned char SPDY_dictionary_txt[]; | |
75 | ||
76 | #if defined(__cplusplus) | |
77 | } | |
78 | #endif | |
79 | ||
80 | #define KORE_SSL_PROTO_STRING "\x08spdy/3.1\x08http/1.1" | |
81 | #define SPDY_CONTROL_FRAME(x) ((x & (1 << 31))) | |
82 | ||
83 | #define SPDY_FRAME_SIZE 8 | |
84 | #define SPDY_SYNFRAME_SIZE 10 | |
85 | #define SPDY_ZLIB_DICT_SIZE 1423 | |
86 | #define SPDY_ZLIB_CHUNK 16348 | |
87 | #define SPDY_INIT_WSIZE 65536 | |
88 | ||
89 | /* control frames */ | |
90 | #define SPDY_CTRL_FRAME_SYN_STREAM 1 | |
91 | #define SPDY_CTRL_FRAME_SYN_REPLY 2 | |
92 | #define SPDY_CTRL_FRAME_RST_STREAM 3 | |
93 | #define SPDY_CTRL_FRAME_SETTINGS 4 | |
94 | #define SPDY_CTRL_FRAME_PING 6 | |
95 | #define SPDY_CTRL_FRAME_GOAWAY 7 | |
96 | #define SPDY_CTRL_FRAME_WINDOW 9 | |
97 | #define SPDY_DATA_FRAME 99 | |
98 | ||
99 | /* session error codes */ | |
100 | #define SPDY_SESSION_ERROR_OK 0 | |
101 | #define SPDY_SESSION_ERROR_PROTOCOL 1 | |
102 | #define SPDY_SESSION_ERROR_INTERNAL 2 | |
103 | ||
104 | /* flags */ | |
105 | #define FLAG_FIN 0x01 | |
106 | #define FLAG_UNIDIRECTIONAL 0x02 | |
107 | ||
108 | /* settings */ | |
109 | #define SETTINGS_UPLOAD_BANDWIDTH 1 | |
110 | #define SETTINGS_DOWNLOAD_BANDWIDTH 2 | |
111 | #define SETTINGS_ROUND_TRIP_TIME 3 | |
112 | #define SETTINGS_MAX_CONCURRENT_STREAMS 4 | |
113 | #define SETTINGS_CURRENT_CWND 5 | |
114 | #define SETTINGS_DOWNLOAD_RETRANS_RATE 6 | |
115 | #define SETTINGS_INITIAL_WINDOW_SIZE 7 | |
116 | #define SETTINGS_CLIENT_CERTIFICATE_VECTOR_SIZE 8 | |
117 | ||
118 | #define SPDY_HBLOCK_NORMAL 0 | |
119 | #define SPDY_HBLOCK_DELAYED_ALLOC 1 | |
120 | ||
121 | #define SPDY_FLOW_WINDOW_MAX 2147483647 | |
122 | ||
123 | /* internal flags (make sure they don't clash with SPDY stream flags) */ | |
124 | #define SPDY_KORE_FIN 0x10 | |
125 | #define SPDY_DATAFRAME_PRELUDE 0x20 | |
126 | #define SPDY_NO_CLOSE 0x40 | |
127 | ||
128 | #define SPDY_KEEP_NETBUFS 0 | |
129 | #define SPDY_REMOVE_NETBUFS 1 | |
130 | ||
131 | #endif /* !__H_SPDY_H */ |
23 | 23 | #define KORE_TASK_STATE_FINISHED 3 |
24 | 24 | #define KORE_TASK_STATE_ABORT 4 |
25 | 25 | |
26 | #define KORE_TASK_THREADS 2 | |
27 | ||
26 | 28 | #if defined(__cplusplus) |
27 | 29 | extern "C" { |
28 | 30 | #endif |
29 | 31 | |
32 | #if !defined(KORE_NO_HTTP) | |
30 | 33 | struct http_request; |
34 | #endif | |
31 | 35 | |
32 | 36 | struct kore_task { |
33 | 37 | u_int8_t type; |
35 | 39 | int result; |
36 | 40 | pthread_rwlock_t lock; |
37 | 41 | |
42 | #if !defined(KORE_NO_HTTP) | |
38 | 43 | struct http_request *req; |
44 | #endif | |
45 | ||
39 | 46 | int fds[2]; |
40 | 47 | int (*entry)(struct kore_task *); |
48 | void (*cb)(struct kore_task *); | |
41 | 49 | |
42 | 50 | struct kore_task_thread *thread; |
43 | 51 | |
62 | 70 | int kore_task_finished(struct kore_task *); |
63 | 71 | void kore_task_handle(struct kore_task *, int); |
64 | 72 | |
73 | #if !defined(KORE_NO_HTTP) | |
65 | 74 | void kore_task_bind_request(struct kore_task *, |
66 | 75 | struct http_request *); |
76 | #endif | |
77 | void kore_task_bind_callback(struct kore_task *, | |
78 | void (*cb)(struct kore_task *)); | |
67 | 79 | void kore_task_create(struct kore_task *, |
68 | 80 | int (*entry)(struct kore_task *)); |
69 | 81 | |
75 | 87 | |
76 | 88 | int kore_task_state(struct kore_task *); |
77 | 89 | int kore_task_result(struct kore_task *); |
78 | ||
90 | ||
91 | extern u_int16_t kore_task_threads; | |
92 | ||
79 | 93 | #if defined(__cplusplus) |
80 | 94 | } |
81 | 95 | #endif |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
16 | 16 | #include <sys/socket.h> |
17 | 17 | |
18 | 18 | #include <poll.h> |
19 | #include <time.h> | |
19 | 20 | |
20 | 21 | #include "kore.h" |
21 | 22 | #include "http.h" |
22 | ||
23 | static int accesslog_fd[2]; | |
24 | 23 | |
25 | 24 | struct kore_log_packet { |
26 | 25 | u_int8_t method; |
33 | 32 | char host[KORE_DOMAINNAME_LEN]; |
34 | 33 | char path[HTTP_URI_LEN]; |
35 | 34 | char agent[HTTP_USERAGENT_LEN]; |
35 | #if !defined(KORE_NO_TLS) | |
36 | 36 | char cn[X509_CN_LENGTH]; |
37 | #endif | |
37 | 38 | }; |
38 | 39 | |
39 | 40 | void |
40 | 41 | kore_accesslog_init(void) |
41 | 42 | { |
42 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, accesslog_fd) == -1) | |
43 | fatal("kore_accesslog_init(): socketpair() %s", errno_s); | |
44 | 43 | } |
45 | 44 | |
46 | 45 | void |
47 | 46 | kore_accesslog_worker_init(void) |
48 | 47 | { |
49 | close(accesslog_fd[0]); | |
50 | 48 | kore_domain_closelogs(); |
51 | 49 | } |
52 | 50 | |
53 | 51 | int |
54 | kore_accesslog_wait(void) | |
52 | kore_accesslog_write(const void *data, u_int32_t len) | |
55 | 53 | { |
56 | ssize_t len; | |
54 | int l; | |
57 | 55 | time_t now; |
56 | ssize_t sent; | |
58 | 57 | struct kore_domain *dom; |
59 | struct pollfd pfd[1]; | |
60 | int nfds, l; | |
61 | 58 | struct kore_log_packet logpacket; |
62 | 59 | char addr[INET6_ADDRSTRLEN]; |
63 | 60 | char *method, *buf, *tbuf, *cn; |
64 | 61 | |
65 | pfd[0].fd = accesslog_fd[0]; | |
66 | pfd[0].events = POLLIN; | |
67 | pfd[0].revents = 0; | |
62 | if (len != sizeof(struct kore_log_packet)) | |
63 | return (KORE_RESULT_ERROR); | |
68 | 64 | |
69 | nfds = poll(pfd, 1, 1000); | |
70 | if (nfds == -1 || (pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL))) { | |
71 | if (nfds == -1 && errno == EINTR) | |
72 | return (KORE_RESULT_OK); | |
73 | kore_log(LOG_WARNING, "poll(): %s", errno_s); | |
74 | return (KORE_RESULT_ERROR); | |
75 | } | |
76 | ||
77 | if (nfds == 0) | |
78 | return (KORE_RESULT_OK); | |
79 | ||
80 | len = recv(accesslog_fd[0], &logpacket, sizeof(logpacket), 0); | |
81 | if (len == -1) { | |
82 | kore_log(LOG_WARNING, "recv(): %s", errno_s); | |
83 | return (KORE_RESULT_ERROR); | |
84 | } | |
85 | ||
86 | if (len != sizeof(logpacket)) | |
87 | return (KORE_RESULT_ERROR); | |
65 | (void)memcpy(&logpacket, data, sizeof(logpacket)); | |
88 | 66 | |
89 | 67 | if ((dom = kore_domain_lookup(logpacket.host)) == NULL) { |
90 | 68 | kore_log(LOG_WARNING, |
114 | 92 | break; |
115 | 93 | } |
116 | 94 | |
95 | cn = "none"; | |
96 | #if !defined(KORE_NO_TLS) | |
117 | 97 | if (logpacket.cn[0] != '\0') |
118 | 98 | cn = logpacket.cn; |
119 | else | |
120 | cn = "none"; | |
99 | #endif | |
121 | 100 | |
122 | 101 | if (inet_ntop(logpacket.addrtype, &(logpacket.addr), |
123 | 102 | addr, sizeof(addr)) == NULL) |
124 | kore_strlcpy(addr, "unknown", sizeof(addr)); | |
103 | (void)kore_strlcpy(addr, "unknown", sizeof(addr)); | |
125 | 104 | |
126 | 105 | time(&now); |
127 | 106 | tbuf = kore_time_to_date(now); |
130 | 109 | logpacket.worker_id, logpacket.time_req, cn, logpacket.agent); |
131 | 110 | if (l == -1) { |
132 | 111 | kore_log(LOG_WARNING, |
133 | "kore_accesslog_wait(): asprintf() == -1"); | |
112 | "kore_accesslog_write(): asprintf() == -1"); | |
134 | 113 | return (KORE_RESULT_ERROR); |
135 | 114 | } |
136 | 115 | |
137 | len = write(dom->accesslog, buf, l); | |
138 | if (len == -1) { | |
116 | sent = write(dom->accesslog, buf, l); | |
117 | if (sent == -1) { | |
139 | 118 | free(buf); |
140 | 119 | kore_log(LOG_WARNING, |
141 | "kore_accesslog_wait(): write(): %s", errno_s); | |
120 | "kore_accesslog_write(): write(): %s", errno_s); | |
142 | 121 | return (KORE_RESULT_ERROR); |
143 | 122 | } |
144 | 123 | |
145 | if (len != l) | |
124 | if (sent != l) | |
146 | 125 | kore_log(LOG_NOTICE, "accesslog: %s", buf); |
147 | 126 | |
148 | 127 | free(buf); |
152 | 131 | void |
153 | 132 | kore_accesslog(struct http_request *req) |
154 | 133 | { |
155 | ssize_t len; | |
156 | 134 | struct kore_log_packet logpacket; |
157 | 135 | |
158 | 136 | logpacket.addrtype = req->owner->addrtype; |
171 | 149 | logpacket.worker_id = worker->id; |
172 | 150 | logpacket.worker_cpu = worker->cpu; |
173 | 151 | logpacket.time_req = req->total; |
174 | kore_strlcpy(logpacket.host, req->host, sizeof(logpacket.host)); | |
175 | kore_strlcpy(logpacket.path, req->path, sizeof(logpacket.path)); | |
152 | ||
153 | if (kore_strlcpy(logpacket.host, | |
154 | req->host, sizeof(logpacket.host)) >= sizeof(logpacket.host)) | |
155 | kore_log(LOG_NOTICE, "kore_accesslog: host truncated"); | |
156 | ||
157 | if (kore_strlcpy(logpacket.path, | |
158 | req->path, sizeof(logpacket.path)) >= sizeof(logpacket.path)) | |
159 | kore_log(LOG_NOTICE, "kore_accesslog: path truncated"); | |
176 | 160 | |
177 | 161 | if (req->agent != NULL) { |
178 | kore_strlcpy(logpacket.agent, | |
179 | req->agent, sizeof(logpacket.agent)); | |
162 | if (kore_strlcpy(logpacket.agent, req->agent, | |
163 | sizeof(logpacket.agent)) >= sizeof(logpacket.agent)) | |
164 | kore_log(LOG_NOTICE, "kore_accesslog: agent truncated"); | |
180 | 165 | } else { |
181 | kore_strlcpy(logpacket.agent, "unknown", | |
166 | (void)kore_strlcpy(logpacket.agent, "unknown", | |
182 | 167 | sizeof(logpacket.agent)); |
183 | 168 | } |
184 | 169 | |
170 | #if !defined(KORE_NO_TLS) | |
185 | 171 | memset(logpacket.cn, '\0', sizeof(logpacket.cn)); |
186 | #if !defined(KORE_BENCHMARK) | |
187 | 172 | if (req->owner->cert != NULL) { |
188 | 173 | if (X509_GET_CN(req->owner->cert, |
189 | 174 | logpacket.cn, sizeof(logpacket.cn)) == -1) { |
192 | 177 | } |
193 | 178 | #endif |
194 | 179 | |
195 | len = send(accesslog_fd[1], &logpacket, sizeof(logpacket), 0); | |
196 | if (len == -1) { | |
197 | kore_log(LOG_WARNING, "kore_accesslog(): send(): %s", errno_s); | |
198 | } else if (len != sizeof(logpacket)) { | |
199 | kore_log(LOG_WARNING, "short accesslog packet sent"); | |
200 | } | |
180 | kore_msg_send(KORE_MSG_PARENT, | |
181 | KORE_MSG_ACCESSLOG, &logpacket, sizeof(logpacket)); | |
201 | 182 | } |
76 | 76 | |
77 | 77 | switch (r) { |
78 | 78 | case KORE_RESULT_OK: |
79 | req->flags |= HTTP_REQUEST_AUTHED; | |
79 | 80 | kore_debug("kore_auth_run() for %s successful", req->path); |
80 | 81 | /* FALLTHROUGH */ |
81 | 82 | case KORE_RESULT_RETRY: |
108 | 109 | size_t len, slen; |
109 | 110 | char *value, *c, *cookie, *cookies[HTTP_MAX_COOKIES]; |
110 | 111 | |
111 | if (!http_request_header(req, "cookie", &cookie)) | |
112 | if (!http_request_header(req, "cookie", &c)) | |
112 | 113 | return (KORE_RESULT_ERROR); |
114 | ||
115 | cookie = kore_strdup(c); | |
113 | 116 | |
114 | 117 | slen = strlen(auth->value); |
115 | 118 | v = kore_split_string(cookie, ";", cookies, HTTP_MAX_COOKIES); |
123 | 126 | } |
124 | 127 | |
125 | 128 | if (i == v) { |
126 | kore_mem_free(cookie); | |
129 | kore_free(cookie); | |
127 | 130 | return (KORE_RESULT_ERROR); |
128 | 131 | } |
129 | 132 | |
130 | 133 | c = cookies[i]; |
131 | 134 | if ((value = strchr(c, '=')) == NULL) { |
132 | kore_mem_free(cookie); | |
135 | kore_free(cookie); | |
133 | 136 | return (KORE_RESULT_ERROR); |
134 | 137 | } |
135 | 138 | |
136 | 139 | i = kore_validator_check(req, auth->validator, ++value); |
137 | kore_mem_free(cookie); | |
140 | kore_free(cookie); | |
138 | 141 | |
139 | 142 | return (i); |
140 | 143 | } |
142 | 145 | static int |
143 | 146 | kore_auth_header(struct http_request *req, struct kore_auth *auth) |
144 | 147 | { |
145 | int r; | |
146 | 148 | char *header; |
147 | 149 | |
148 | 150 | if (!http_request_header(req, auth->value, &header)) |
149 | 151 | return (KORE_RESULT_ERROR); |
150 | 152 | |
151 | r = kore_validator_check(req, auth->validator, header); | |
152 | kore_mem_free(header); | |
153 | ||
154 | return (r); | |
153 | return (kore_validator_check(req, auth->validator, header)); | |
155 | 154 | } |
156 | 155 | |
157 | 156 | static int |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
21 | 21 | #include <sys/cpuset.h> |
22 | 22 | #endif |
23 | 23 | |
24 | #include <errno.h> | |
25 | #include <string.h> | |
26 | ||
24 | 27 | #include "kore.h" |
25 | 28 | |
26 | 29 | #if defined(KORE_USE_PGSQL) |
81 | 84 | event_count = (worker_max_connections * 2) + nlisteners; |
82 | 85 | events = kore_calloc(event_count, sizeof(struct kevent)); |
83 | 86 | |
84 | LIST_FOREACH(l, &listeners, list) { | |
85 | kore_platform_event_schedule(l->fd, | |
86 | EVFILT_READ, EV_ADD | EV_DISABLE, l); | |
87 | /* Hack to check if we're running under the parent or not. */ | |
88 | if (worker != NULL) { | |
89 | LIST_FOREACH(l, &listeners, list) { | |
90 | kore_platform_event_schedule(l->fd, | |
91 | EVFILT_READ, EV_ADD | EV_DISABLE, l); | |
92 | } | |
93 | } | |
94 | } | |
95 | ||
96 | void | |
97 | kore_platform_event_cleanup(void) | |
98 | { | |
99 | if (kfd != -1) { | |
100 | close(kfd); | |
101 | kfd = -1; | |
102 | } | |
103 | ||
104 | if (events != NULL) { | |
105 | kore_free(events); | |
106 | events = NULL; | |
87 | 107 | } |
88 | 108 | } |
89 | 109 | |
172 | 192 | !(c->flags & CONN_WRITE_BLOCK)) |
173 | 193 | c->flags |= CONN_WRITE_POSSIBLE; |
174 | 194 | |
175 | if (!kore_connection_handle(c)) { | |
195 | if (c->handle != NULL && !c->handle(c)) | |
176 | 196 | kore_connection_disconnect(c); |
177 | } else { | |
178 | if (!TAILQ_EMPTY(&(c->send_queue))) { | |
179 | kore_platform_event_schedule(c->fd, | |
180 | EVFILT_WRITE, EV_ADD | EV_ONESHOT, | |
181 | c); | |
182 | } | |
183 | } | |
184 | 197 | break; |
185 | 198 | #if defined(KORE_USE_PGSQL) |
186 | 199 | case KORE_TYPE_PGSQL_CONN: |
203 | 216 | void |
204 | 217 | kore_platform_event_all(int fd, void *c) |
205 | 218 | { |
206 | kore_platform_event_schedule(fd, EVFILT_READ, EV_ADD, c); | |
207 | kore_platform_event_schedule(fd, EVFILT_WRITE, EV_ADD | EV_ONESHOT, c); | |
219 | kore_platform_event_schedule(fd, EVFILT_READ, EV_ADD | EV_CLEAR, c); | |
220 | kore_platform_event_schedule(fd, EVFILT_WRITE, EV_ADD | EV_CLEAR, c); | |
208 | 221 | } |
209 | 222 | |
210 | 223 | void |
242 | 255 | } |
243 | 256 | |
244 | 257 | void |
258 | kore_platform_schedule_write(int fd, void *data) | |
259 | { | |
260 | kore_platform_event_schedule(fd, EVFILT_WRITE, EV_ADD, data); | |
261 | } | |
262 | ||
263 | void | |
245 | 264 | kore_platform_disable_read(int fd) |
246 | 265 | { |
247 | 266 | kore_platform_event_schedule(fd, EVFILT_READ, EV_DELETE, NULL); |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
13 | 13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 14 | */ |
15 | 15 | |
16 | #include <string.h> | |
17 | #include <stdio.h> | |
18 | #include <stdarg.h> | |
19 | #include <stdlib.h> | |
20 | ||
16 | 21 | #include "kore.h" |
17 | 22 | |
18 | 23 | struct kore_buf * |
19 | kore_buf_create(u_int32_t initial) | |
24 | kore_buf_alloc(size_t initial) | |
20 | 25 | { |
21 | 26 | struct kore_buf *buf; |
22 | 27 | |
23 | 28 | buf = kore_malloc(sizeof(*buf)); |
24 | buf->data = kore_malloc(initial); | |
25 | buf->length = initial; | |
26 | buf->offset = 0; | |
29 | kore_buf_init(buf, initial); | |
30 | buf->flags = KORE_BUF_OWNER_API; | |
27 | 31 | |
28 | 32 | return (buf); |
29 | 33 | } |
30 | 34 | |
31 | 35 | void |
32 | kore_buf_append(struct kore_buf *buf, void *d, u_int32_t len) | |
36 | kore_buf_init(struct kore_buf *buf, size_t initial) | |
33 | 37 | { |
34 | if ((buf->offset + len) >= buf->length) { | |
35 | buf->length += len + KORE_BUF_INCREMENT; | |
38 | if (initial > 0) | |
39 | buf->data = kore_malloc(initial); | |
40 | else | |
41 | buf->data = NULL; | |
42 | ||
43 | buf->length = initial; | |
44 | buf->offset = 0; | |
45 | buf->flags = 0; | |
46 | } | |
47 | ||
48 | void | |
49 | kore_buf_cleanup(struct kore_buf *buf) | |
50 | { | |
51 | kore_free(buf->data); | |
52 | buf->data = NULL; | |
53 | buf->offset = 0; | |
54 | buf->length = 0; | |
55 | } | |
56 | ||
57 | void | |
58 | kore_buf_free(struct kore_buf *buf) | |
59 | { | |
60 | kore_buf_cleanup(buf); | |
61 | if (buf->flags & KORE_BUF_OWNER_API) | |
62 | kore_free(buf); | |
63 | } | |
64 | ||
65 | void | |
66 | kore_buf_append(struct kore_buf *buf, const void *d, size_t len) | |
67 | { | |
68 | if ((buf->offset + len) < len) | |
69 | fatal("overflow in kore_buf_append"); | |
70 | ||
71 | if ((buf->offset + len) > buf->length) { | |
72 | buf->length += len; | |
36 | 73 | buf->data = kore_realloc(buf->data, buf->length); |
37 | 74 | } |
38 | 75 | |
39 | 76 | memcpy((buf->data + buf->offset), d, len); |
40 | 77 | buf->offset += len; |
41 | } | |
42 | ||
43 | void | |
44 | kore_buf_appendb(struct kore_buf *buf, struct kore_buf *src) | |
45 | { | |
46 | u_int8_t *d; | |
47 | u_int32_t len; | |
48 | ||
49 | d = kore_buf_release(src, &len); | |
50 | kore_buf_append(buf, d, len); | |
51 | kore_mem_free(d); | |
52 | 78 | } |
53 | 79 | |
54 | 80 | void |
69 | 95 | b = sb; |
70 | 96 | } |
71 | 97 | |
72 | kore_buf_append(buf, (u_int8_t *)b, l); | |
98 | kore_buf_append(buf, b, l); | |
73 | 99 | if (b != sb) |
74 | 100 | free(b); |
75 | 101 | } |
84 | 110 | va_end(args); |
85 | 111 | } |
86 | 112 | |
113 | char * | |
114 | kore_buf_stringify(struct kore_buf *buf, size_t *len) | |
115 | { | |
116 | char c; | |
117 | ||
118 | if (len != NULL) | |
119 | *len = buf->offset; | |
120 | ||
121 | c = '\0'; | |
122 | kore_buf_append(buf, &c, sizeof(c)); | |
123 | ||
124 | return ((char *)buf->data); | |
125 | } | |
126 | ||
87 | 127 | u_int8_t * |
88 | kore_buf_release(struct kore_buf *buf, u_int32_t *len) | |
128 | kore_buf_release(struct kore_buf *buf, size_t *len) | |
89 | 129 | { |
90 | 130 | u_int8_t *p; |
91 | 131 | |
92 | 132 | p = buf->data; |
93 | 133 | *len = buf->offset; |
94 | kore_mem_free(buf); | |
134 | ||
135 | buf->data = NULL; | |
136 | kore_buf_free(buf); | |
95 | 137 | |
96 | 138 | return (p); |
97 | 139 | } |
98 | 140 | |
99 | 141 | void |
100 | kore_buf_free(struct kore_buf *buf) | |
101 | { | |
102 | kore_mem_free(buf->data); | |
103 | kore_mem_free(buf); | |
104 | } | |
105 | ||
106 | void | |
107 | 142 | kore_buf_replace_string(struct kore_buf *b, char *src, void *dst, size_t len) |
108 | 143 | { |
109 | u_int32_t blen, off, off2; | |
110 | size_t nlen, klen; | |
111 | 144 | char *key, *end, *tmp, *p; |
145 | size_t blen, off, off2, nlen, klen; | |
112 | 146 | |
113 | 147 | off = 0; |
114 | 148 | klen = strlen(src); |
131 | 165 | memcpy((tmp + off), dst, len); |
132 | 166 | memcpy((tmp + off + len), end, off2); |
133 | 167 | |
134 | kore_mem_free(b->data); | |
168 | kore_free(b->data); | |
135 | 169 | b->data = (u_int8_t *)tmp; |
136 | 170 | b->offset = off + len + off2; |
137 | 171 | b->length = nlen; |
139 | 173 | off = off + len; |
140 | 174 | } |
141 | 175 | } |
176 | ||
177 | void | |
178 | kore_buf_reset(struct kore_buf *buf) | |
179 | { | |
180 | buf->offset = 0; | |
181 | } |
19 | 19 | #include <sys/queue.h> |
20 | 20 | #include <sys/wait.h> |
21 | 21 | #include <sys/mman.h> |
22 | #include <sys/time.h> | |
22 | 23 | |
23 | 24 | #include <openssl/pem.h> |
24 | 25 | #include <openssl/x509v3.h> |
39 | 40 | |
40 | 41 | #include "kore.h" |
41 | 42 | |
42 | #if defined(OpenBSD) || defined(__FreeBSD_version) | |
43 | #if defined(OpenBSD) || defined(__FreeBSD_version) || \ | |
44 | defined(NetBSD) || defined(__DragonFly_version) | |
43 | 45 | #define PRI_TIME_T "d" |
44 | 46 | #endif |
45 | 47 | |
46 | #if defined(linux) | |
48 | #if defined(__linux__) | |
47 | 49 | #if defined(__x86_64__) |
48 | 50 | #define PRI_TIME_T PRIu64 |
49 | 51 | #else |
55 | 57 | #define PRI_TIME_T "ld" |
56 | 58 | #endif |
57 | 59 | |
58 | #define LD_FLAGS_MAX 10 | |
60 | #define LD_FLAGS_MAX 30 | |
61 | #define CFLAGS_MAX 30 | |
62 | #define CXXFLAGS_MAX CFLAGS_MAX | |
63 | ||
64 | #define BUILD_NOBUILD 0 | |
65 | #define BUILD_C 1 | |
66 | #define BUILD_CXX 2 | |
67 | ||
68 | struct buildopt { | |
69 | char *name; | |
70 | char *kore_source; | |
71 | char *kore_flavor; | |
72 | int single_binary; | |
73 | struct kore_buf *cflags; | |
74 | struct kore_buf *cxxflags; | |
75 | struct kore_buf *ldflags; | |
76 | TAILQ_ENTRY(buildopt) list; | |
77 | }; | |
78 | ||
79 | TAILQ_HEAD(buildopt_list, buildopt); | |
59 | 80 | |
60 | 81 | struct cmd { |
61 | 82 | const char *name; |
70 | 91 | struct cfile { |
71 | 92 | struct stat st; |
72 | 93 | int build; |
73 | int cpp; | |
74 | 94 | char *name; |
75 | 95 | char *fpath; |
76 | 96 | char *opath; |
80 | 100 | TAILQ_HEAD(cfile_list, cfile); |
81 | 101 | |
82 | 102 | static void cli_fatal(const char *, ...) __attribute__((noreturn)); |
83 | ||
84 | 103 | static void cli_file_close(int); |
85 | static void cli_run_kore(void *); | |
104 | static void cli_run_kore(void); | |
86 | 105 | static void cli_generate_certs(void); |
87 | 106 | static void cli_link_library(void *); |
88 | static void cli_compile_cfile(void *); | |
107 | static void cli_compile_kore(void *); | |
108 | static void cli_compile_source_file(void *); | |
89 | 109 | static void cli_mkdir(const char *, int); |
90 | 110 | static int cli_dir_exists(const char *); |
91 | 111 | static int cli_file_exists(const char *); |
92 | 112 | static void cli_cleanup_files(const char *); |
113 | static void cli_build_cflags(struct buildopt *); | |
114 | static void cli_build_cxxflags(struct buildopt *); | |
115 | static void cli_build_ldflags(struct buildopt *); | |
93 | 116 | static void cli_file_writef(int, const char *, ...); |
94 | 117 | static void cli_file_open(const char *, int, int *); |
95 | 118 | static void cli_file_remove(char *, struct dirent *); |
98 | 121 | static int cli_vasprintf(char **, const char *, ...); |
99 | 122 | static void cli_spawn_proc(void (*cb)(void *), void *); |
100 | 123 | static void cli_write_asset(const char *, const char *); |
101 | static void cli_register_cfile(char *, struct dirent *); | |
124 | static void cli_register_kore_file(char *, struct dirent *); | |
125 | static void cli_register_source_file(char *, struct dirent *); | |
102 | 126 | static void cli_file_create(const char *, const char *, size_t); |
103 | 127 | static int cli_file_requires_build(struct stat *, const char *); |
104 | 128 | static void cli_find_files(const char *, |
105 | 129 | void (*cb)(char *, struct dirent *)); |
106 | static void cli_add_cfile(char *, char *, char *, | |
107 | struct stat *, int, int); | |
130 | static void cli_add_source_file(char *, char *, char *, | |
131 | struct stat *, int); | |
132 | ||
133 | static struct buildopt *cli_buildopt_default(void); | |
134 | static struct buildopt *cli_buildopt_new(const char *); | |
135 | static struct buildopt *cli_buildopt_find(const char *); | |
136 | static void cli_buildopt_cleanup(void); | |
137 | static void cli_buildopt_parse(const char *); | |
138 | static void cli_buildopt_cflags(struct buildopt *, const char *); | |
139 | static void cli_buildopt_cxxflags(struct buildopt *, const char *); | |
140 | static void cli_buildopt_ldflags(struct buildopt *, const char *); | |
141 | static void cli_buildopt_single_binary(struct buildopt *, | |
142 | const char *); | |
143 | static void cli_buildopt_kore_source(struct buildopt *, | |
144 | const char *); | |
145 | static void cli_buildopt_kore_flavor(struct buildopt *, | |
146 | const char *); | |
147 | ||
148 | static void cli_flavor_load(void); | |
149 | static void cli_flavor_change(const char *); | |
108 | 150 | |
109 | 151 | static void cli_run(int, char **); |
110 | 152 | static void cli_help(int, char **); |
111 | 153 | static void cli_build(int, char **); |
112 | 154 | static void cli_clean(int, char **); |
113 | 155 | static void cli_create(int, char **); |
156 | static void cli_flavor(int, char **); | |
114 | 157 | |
115 | 158 | static void file_create_src(void); |
116 | 159 | static void file_create_config(void); |
122 | 165 | { "build", "build an application", cli_build }, |
123 | 166 | { "clean", "cleanup the build files", cli_clean }, |
124 | 167 | { "create", "create a new application skeleton", cli_create }, |
168 | { "flavor", "switch build flavor", cli_flavor }, | |
125 | 169 | { NULL, NULL, NULL } |
126 | 170 | }; |
127 | 171 | |
134 | 178 | |
135 | 179 | static const char *gen_dirs[] = { |
136 | 180 | "src", |
137 | #if !defined(KORE_BENCHMARK) | |
181 | #if !defined(KORE_NO_TLS) | |
138 | 182 | "cert", |
139 | 183 | #endif |
140 | 184 | "conf", |
156 | 200 | "}\n"; |
157 | 201 | |
158 | 202 | static const char *config_data = |
159 | "# Placeholder configuration\n" | |
203 | "# %s configuration\n" | |
160 | 204 | "\n" |
161 | 205 | "bind\t\t127.0.0.1 8888\n" |
162 | 206 | "load\t\t./%s.so\n" |
163 | #if !defined(KORE_BENCHMARK) | |
207 | #if !defined(KORE_NO_TLS) | |
164 | 208 | "tls_dhparam\tdh2048.pem\n" |
165 | 209 | #endif |
166 | 210 | "\n" |
167 | "domain 127.0.0.1 {\n" | |
168 | #if !defined(KORE_BENCHMARK) | |
211 | "domain * {\n" | |
212 | #if !defined(KORE_NO_TLS) | |
169 | 213 | "\tcertfile\tcert/server.crt\n" |
170 | 214 | "\tcertkey\t\tcert/server.key\n" |
171 | 215 | #endif |
172 | 216 | "\tstatic\t/\tpage\n" |
173 | 217 | "}\n"; |
174 | 218 | |
175 | #if !defined(KORE_BENCHMARK) | |
219 | static const char *build_data = | |
220 | "# %s build config\n" | |
221 | "# You can switch flavors using: kore flavor [newflavor]\n" | |
222 | "\n" | |
223 | "# Set to yes if you wish to produce a single binary instead\n" | |
224 | "# of a dynamic library. If you set this to yes you must also\n" | |
225 | "# set kore_source together with kore_flavor and update ldflags\n" | |
226 | "# to include the appropriate libraries you will be linking with.\n" | |
227 | "#single_binary=no\n" | |
228 | "#kore_source=/home/joris/src/kore\n" | |
229 | "#kore_flavor=\n" | |
230 | "\n" | |
231 | "# The flags below are shared between flavors\n" | |
232 | "cflags=-Wall -Wmissing-declarations -Wshadow\n" | |
233 | "cflags=-Wstrict-prototypes -Wmissing-prototypes\n" | |
234 | "cflags=-Wpointer-arith -Wcast-qual -Wsign-compare\n" | |
235 | "\n" | |
236 | "cxxflags=-Wall -Wmissing-declarations -Wshadow\n" | |
237 | "cxxflags=-Wpointer-arith -Wcast-qual -Wsign-compare\n" | |
238 | "\n" | |
239 | "dev {\n" | |
240 | " # These flags are added to the shared ones when\n" | |
241 | " # you build the \"dev\" flavor.\n" | |
242 | " cflags=-g\n" | |
243 | " cxxflags=-g\n" | |
244 | "}\n" | |
245 | "\n" | |
246 | "#prod {\n" | |
247 | "# You can specify additional flags here which are only\n" | |
248 | "# included if you build with the \"prod\" flavor.\n" | |
249 | "#}\n"; | |
250 | ||
251 | #if !defined(KORE_NO_TLS) | |
176 | 252 | static const char *dh2048_data = |
177 | 253 | "-----BEGIN DH PARAMETERS-----\n" |
178 | 254 | "MIIBCAKCAQEAn4f4Qn5SudFjEYPWTbUaOTLUH85YWmmPFW1+b5bRa9ygr+1wfamv\n" |
184 | 260 | "-----END DH PARAMETERS-----"; |
185 | 261 | #endif |
186 | 262 | |
187 | static const char *gitignore_data = "*.o\n.objs\n%s.so\nassets.h\ncert\n"; | |
263 | static const char *gitignore = "*.o\n.flavor\n.objs\n%s.so\nassets.h\ncert\n"; | |
188 | 264 | |
189 | 265 | static int s_fd = -1; |
190 | 266 | static char *appl = NULL; |
267 | static int run_after = 0; | |
191 | 268 | static char *rootdir = NULL; |
192 | static char *compiler = "gcc"; | |
269 | static char *compiler_c = "gcc"; | |
270 | static char *compiler_cpp = "g++"; | |
271 | static char *compiler_ld = "gcc"; | |
193 | 272 | static struct cfile_list source_files; |
194 | static int cfiles_count; | |
273 | static struct buildopt_list build_options; | |
274 | static int source_files_count; | |
275 | static int cxx_files_count; | |
195 | 276 | static struct cmd *command = NULL; |
277 | static int cflags_count = 0; | |
278 | static int cxxflags_count = 0; | |
279 | static int ldflags_count = 0; | |
280 | static char *flavor = NULL; | |
281 | static char *cflags[CFLAGS_MAX]; | |
282 | static char *cxxflags[CXXFLAGS_MAX]; | |
283 | static char *ldflags[LD_FLAGS_MAX]; | |
196 | 284 | |
197 | 285 | void |
198 | 286 | kore_cli_usage(int local) |
225 | 313 | kore_cli_usage(1); |
226 | 314 | |
227 | 315 | (void)umask(S_IWGRP|S_IWOTH); |
316 | ||
317 | if ((flavor = strchr(argv[0], ':')) != NULL) | |
318 | *(flavor)++ = '\0'; | |
228 | 319 | |
229 | 320 | for (i = 0; cmds[i].name != NULL; i++) { |
230 | 321 | if (!strcmp(argv[0], cmds[i].name)) { |
274 | 365 | |
275 | 366 | cli_generate_certs(); |
276 | 367 | |
277 | printf("%s created succesfully!\n", appl); | |
278 | ||
279 | #if !defined(KORE_BENCHMARK) | |
368 | printf("%s created successfully!\n", appl); | |
369 | ||
370 | #if !defined(KORE_NO_TLS) | |
280 | 371 | printf("note: do NOT use the created DH parameters/certificates in production\n"); |
281 | 372 | #endif |
282 | 373 | } |
283 | 374 | |
284 | 375 | static void |
376 | cli_flavor(int argc, char **argv) | |
377 | { | |
378 | struct buildopt *bopt; | |
379 | char pwd[MAXPATHLEN], *conf; | |
380 | ||
381 | if (getcwd(pwd, sizeof(pwd)) == NULL) | |
382 | cli_fatal("could not get cwd: %s", errno_s); | |
383 | ||
384 | appl = basename(pwd); | |
385 | (void)cli_vasprintf(&conf, "conf/%s.conf", appl); | |
386 | if (!cli_dir_exists("conf") || !cli_file_exists(conf)) | |
387 | cli_fatal("%s doesn't appear to be a kore app", appl); | |
388 | free(conf); | |
389 | ||
390 | TAILQ_INIT(&build_options); | |
391 | (void)cli_buildopt_new("_default"); | |
392 | cli_buildopt_parse("conf/build.conf"); | |
393 | ||
394 | if (argc == 0) { | |
395 | cli_flavor_load(); | |
396 | TAILQ_FOREACH(bopt, &build_options, list) { | |
397 | if (!strcmp(bopt->name, "_default")) | |
398 | continue; | |
399 | if (!strcmp(bopt->name, flavor)) { | |
400 | printf("* %s\n", bopt->name); | |
401 | } else { | |
402 | printf(" %s\n", bopt->name); | |
403 | } | |
404 | } | |
405 | } else { | |
406 | cli_flavor_change(argv[0]); | |
407 | printf("changed build flavor to: %s\n", argv[0]); | |
408 | } | |
409 | ||
410 | cli_buildopt_cleanup(); | |
411 | } | |
412 | ||
413 | static void | |
285 | 414 | cli_build(int argc, char **argv) |
286 | 415 | { |
287 | struct cfile *cf; | |
288 | struct timeval times[2]; | |
289 | int requires_relink; | |
290 | char pwd[PATH_MAX], *src_path, *assets_header; | |
291 | char *assets_path, *p, *obj_path, *cpath, *config; | |
292 | ||
293 | if (argc == 0) { | |
294 | if (getcwd(pwd, sizeof(pwd)) == NULL) | |
295 | cli_fatal("could not get cwd: %s", errno_s); | |
296 | ||
297 | rootdir = "."; | |
298 | appl = basename(pwd); | |
299 | } else { | |
300 | appl = argv[0]; | |
301 | rootdir = appl; | |
302 | } | |
416 | struct dirent dp; | |
417 | struct cfile *cf; | |
418 | struct buildopt *bopt; | |
419 | struct timeval times[2]; | |
420 | char *build_path; | |
421 | int requires_relink, l; | |
422 | char *sofile, *config, *data; | |
423 | char *assets_path, *p, *obj_path, *cpath; | |
424 | char pwd[PATH_MAX], *src_path, *assets_header; | |
425 | ||
426 | if (getcwd(pwd, sizeof(pwd)) == NULL) | |
427 | cli_fatal("could not get cwd: %s", errno_s); | |
428 | ||
429 | rootdir = "."; | |
430 | appl = basename(pwd); | |
303 | 431 | |
304 | 432 | if ((p = getenv("CC")) != NULL) |
305 | compiler = p; | |
306 | ||
307 | cfiles_count = 0; | |
433 | compiler_c = p; | |
434 | ||
435 | if ((p = getenv("CXX")) != NULL) | |
436 | compiler_cpp = p; | |
437 | ||
438 | source_files_count = 0; | |
439 | cxx_files_count = 0; | |
308 | 440 | TAILQ_INIT(&source_files); |
441 | TAILQ_INIT(&build_options); | |
309 | 442 | |
310 | 443 | (void)cli_vasprintf(&src_path, "%s/src", rootdir); |
311 | 444 | (void)cli_vasprintf(&assets_path, "%s/assets", rootdir); |
312 | 445 | (void)cli_vasprintf(&config, "%s/conf/%s.conf", rootdir, appl); |
313 | 446 | (void)cli_vasprintf(&assets_header, "%s/src/assets.h", rootdir); |
447 | (void)cli_vasprintf(&build_path, "%s/conf/build.conf", rootdir); | |
448 | ||
314 | 449 | if (!cli_dir_exists(src_path) || !cli_file_exists(config)) |
315 | 450 | cli_fatal("%s doesn't appear to be a kore app", appl); |
316 | 451 | |
317 | free(config); | |
452 | cli_flavor_load(); | |
453 | bopt = cli_buildopt_new("_default"); | |
454 | if (!cli_file_exists(build_path)) { | |
455 | l = cli_vasprintf(&data, build_data, appl); | |
456 | cli_file_create("conf/build.conf", data, l); | |
457 | free(data); | |
458 | } | |
459 | ||
460 | cli_find_files(src_path, cli_register_source_file); | |
461 | free(src_path); | |
462 | ||
463 | cli_buildopt_parse(build_path); | |
464 | free(build_path); | |
318 | 465 | |
319 | 466 | (void)cli_vasprintf(&obj_path, "%s/.objs", rootdir); |
320 | 467 | if (!cli_dir_exists(obj_path)) |
321 | 468 | cli_mkdir(obj_path, 0755); |
322 | 469 | free(obj_path); |
323 | 470 | |
471 | if (bopt->single_binary) { | |
472 | if (bopt->kore_source == NULL) | |
473 | cli_fatal("single_binary set but not kore_source"); | |
474 | ||
475 | printf("building kore (%s)\n", bopt->kore_source); | |
476 | cli_spawn_proc(cli_compile_kore, bopt); | |
477 | ||
478 | (void)cli_vasprintf(&src_path, "%s/src", bopt->kore_source); | |
479 | cli_find_files(src_path, cli_register_kore_file); | |
480 | free(src_path); | |
481 | } | |
482 | ||
483 | printf("building %s (%s)\n", appl, flavor); | |
484 | ||
485 | cli_build_cflags(bopt); | |
486 | cli_build_cxxflags(bopt); | |
487 | cli_build_ldflags(bopt); | |
488 | ||
324 | 489 | (void)unlink(assets_header); |
325 | 490 | |
326 | 491 | /* Generate the assets. */ |
327 | if (cli_dir_exists(assets_path)) { | |
328 | cli_file_open(assets_header, | |
329 | O_CREAT | O_TRUNC | O_WRONLY, &s_fd); | |
330 | ||
331 | cli_file_writef(s_fd, "#ifndef __H_KORE_ASSETS_H\n"); | |
332 | cli_file_writef(s_fd, "#define __H_KORE_ASSETS_H\n"); | |
492 | cli_file_open(assets_header, O_CREAT | O_TRUNC | O_WRONLY, &s_fd); | |
493 | cli_file_writef(s_fd, "#ifndef __H_KORE_ASSETS_H\n"); | |
494 | cli_file_writef(s_fd, "#define __H_KORE_ASSETS_H\n"); | |
495 | ||
496 | if (cli_dir_exists(assets_path)) | |
333 | 497 | cli_find_files(assets_path, cli_build_asset); |
334 | cli_file_writef(s_fd, "\n#endif\n"); | |
335 | cli_file_close(s_fd); | |
336 | } | |
498 | ||
499 | if (bopt->single_binary) { | |
500 | memset(&dp, 0, sizeof(dp)); | |
501 | dp.d_type = DT_REG; | |
502 | printf("adding config %s\n", config); | |
503 | (void)snprintf(dp.d_name, | |
504 | sizeof(dp.d_name), "builtin_kore.conf"); | |
505 | cli_build_asset(config, &dp); | |
506 | } | |
507 | ||
508 | cli_file_writef(s_fd, "\n#endif\n"); | |
509 | cli_file_close(s_fd); | |
337 | 510 | |
338 | 511 | free(assets_path); |
339 | ||
340 | /* Build all source files. */ | |
341 | cli_find_files(src_path, cli_register_cfile); | |
342 | ||
343 | free(src_path); | |
512 | free(config); | |
513 | ||
514 | if (cxx_files_count > 0) | |
515 | compiler_ld = compiler_cpp; | |
516 | ||
344 | 517 | requires_relink = 0; |
345 | ||
346 | 518 | TAILQ_FOREACH(cf, &source_files, list) { |
347 | if (cf->build == 0) | |
519 | if (cf->build == BUILD_NOBUILD) | |
348 | 520 | continue; |
349 | 521 | |
350 | 522 | printf("compiling %s\n", cf->name); |
351 | cli_spawn_proc(cli_compile_cfile, cf); | |
523 | cli_spawn_proc(cli_compile_source_file, cf); | |
352 | 524 | |
353 | 525 | times[0].tv_usec = 0; |
354 | 526 | times[0].tv_sec = cf->st.st_mtime; |
370 | 542 | } |
371 | 543 | free(cpath); |
372 | 544 | |
545 | if (bopt->single_binary) { | |
546 | requires_relink++; | |
547 | (void)cli_vasprintf(&sofile, "%s", appl); | |
548 | } else { | |
549 | (void)cli_vasprintf(&sofile, "%s.so", appl); | |
550 | } | |
551 | ||
552 | if (!cli_file_exists(sofile)) | |
553 | requires_relink++; | |
554 | free(sofile); | |
555 | ||
373 | 556 | if (requires_relink) { |
374 | cli_spawn_proc(cli_link_library, NULL); | |
375 | printf("%s built succesfully!\n", appl); | |
557 | cli_spawn_proc(cli_link_library, bopt); | |
558 | printf("%s built successfully!\n", appl); | |
376 | 559 | } else { |
377 | printf("nothing to be done\n"); | |
378 | } | |
560 | printf("nothing to be done!\n"); | |
561 | } | |
562 | ||
563 | if (run_after == 0) | |
564 | cli_buildopt_cleanup(); | |
379 | 565 | } |
380 | 566 | |
381 | 567 | static void |
400 | 586 | static void |
401 | 587 | cli_run(int argc, char **argv) |
402 | 588 | { |
589 | run_after = 1; | |
403 | 590 | cli_build(argc, argv); |
404 | 591 | |
405 | 592 | if (chdir(rootdir) == -1) |
409 | 596 | * We are exec()'ing kore again, while we could technically set |
410 | 597 | * the right cli options manually and just continue running. |
411 | 598 | */ |
412 | cli_run_kore(NULL); | |
599 | cli_run_kore(); | |
413 | 600 | } |
414 | 601 | |
415 | 602 | static void |
429 | 616 | char *name, *data; |
430 | 617 | |
431 | 618 | (void)cli_vasprintf(&name, "conf/%s.conf", appl); |
432 | l = cli_vasprintf(&data, config_data, appl); | |
619 | l = cli_vasprintf(&data, config_data, appl, appl); | |
433 | 620 | cli_file_create(name, data, l); |
434 | ||
435 | 621 | free(name); |
436 | 622 | free(data); |
623 | ||
624 | l = cli_vasprintf(&data, build_data, appl); | |
625 | cli_file_create("conf/build.conf", data, l); | |
626 | free(data); | |
437 | 627 | } |
438 | 628 | |
439 | 629 | static void |
442 | 632 | int l; |
443 | 633 | char *data; |
444 | 634 | |
445 | l = cli_vasprintf(&data, gitignore_data, appl); | |
635 | l = cli_vasprintf(&data, gitignore, appl); | |
446 | 636 | cli_file_create(".gitignore", data, l); |
447 | 637 | free(data); |
448 | 638 | } |
599 | 789 | if (stat(fpath, &st) == -1) |
600 | 790 | cli_fatal("stat: %s %s", fpath, errno_s); |
601 | 791 | |
792 | /* If this file was empty, skip it. */ | |
793 | if (st.st_size == 0) { | |
794 | printf("skipping empty asset %s\n", name); | |
795 | return; | |
796 | } | |
797 | ||
602 | 798 | (void)cli_vasprintf(&opath, "%s/.objs/%s.o", rootdir, name); |
603 | 799 | (void)cli_vasprintf(&cpath, "%s/.objs/%s.c", rootdir, name); |
604 | 800 | |
607 | 803 | *(ext)++ = '\0'; |
608 | 804 | cli_write_asset(name, ext); |
609 | 805 | *ext = '_'; |
610 | ||
611 | cli_add_cfile(name, cpath, opath, &st, 0, 0); | |
612 | kore_mem_free(name); | |
806 | ||
807 | cli_add_source_file(name, cpath, opath, &st, BUILD_NOBUILD); | |
808 | kore_free(name); | |
613 | 809 | return; |
614 | 810 | } |
615 | 811 | |
630 | 826 | |
631 | 827 | /* Start generating the file. */ |
632 | 828 | cli_file_writef(out, "/* Auto generated */\n"); |
633 | cli_file_writef(out, "#include <sys/param.h>\n\n"); | |
829 | cli_file_writef(out, "#include <sys/types.h>\n\n"); | |
634 | 830 | |
635 | 831 | /* Write the file data as a byte array. */ |
636 | 832 | cli_file_writef(out, "u_int8_t asset_%s_%s[] = {\n", name, ext); |
667 | 863 | *--ext = '.'; |
668 | 864 | |
669 | 865 | /* Register the .c file now (cpath is free'd later). */ |
670 | cli_add_cfile(name, cpath, opath, &st, 1, 0); | |
671 | kore_mem_free(name); | |
672 | } | |
673 | ||
674 | static void | |
675 | cli_add_cfile(char *name, char *fpath, char *opath, struct stat *st, | |
676 | int build, int cpp) | |
866 | cli_add_source_file(name, cpath, opath, &st, BUILD_C); | |
867 | kore_free(name); | |
868 | } | |
869 | ||
870 | static void | |
871 | cli_add_source_file(char *name, char *fpath, char *opath, struct stat *st, | |
872 | int build) | |
677 | 873 | { |
678 | 874 | struct cfile *cf; |
679 | 875 | |
680 | cfiles_count++; | |
876 | source_files_count++; | |
681 | 877 | cf = kore_malloc(sizeof(*cf)); |
682 | 878 | |
683 | 879 | cf->st = *st; |
684 | 880 | cf->build = build; |
685 | cf->cpp = cpp; | |
686 | 881 | cf->fpath = fpath; |
687 | 882 | cf->opath = opath; |
688 | 883 | cf->name = kore_strdup(name); |
691 | 886 | } |
692 | 887 | |
693 | 888 | static void |
694 | cli_register_cfile(char *fpath, struct dirent *dp) | |
889 | cli_register_source_file(char *fpath, struct dirent *dp) | |
695 | 890 | { |
696 | 891 | struct stat st; |
697 | 892 | char *ext, *opath; |
698 | int cpp; | |
893 | int build; | |
699 | 894 | |
700 | 895 | if ((ext = strrchr(fpath, '.')) == NULL || |
701 | 896 | (strcmp(ext, ".c") && strcmp(ext, ".cpp"))) |
702 | 897 | return; |
703 | 898 | |
704 | if (!strcmp(ext, ".cpp")) | |
705 | cpp = 1; | |
706 | else | |
707 | cpp = 0; | |
708 | ||
709 | 899 | if (stat(fpath, &st) == -1) |
710 | 900 | cli_fatal("stat(%s): %s", fpath, errno_s); |
711 | 901 | |
902 | if (!strcmp(ext, ".cpp")) | |
903 | cxx_files_count++; | |
904 | ||
712 | 905 | (void)cli_vasprintf(&opath, "%s/.objs/%s.o", rootdir, dp->d_name); |
713 | 906 | if (!cli_file_requires_build(&st, opath)) { |
714 | cli_add_cfile(dp->d_name, fpath, opath, &st, 0, cpp); | |
907 | build = BUILD_NOBUILD; | |
908 | } else if (!strcmp(ext, ".cpp")) { | |
909 | build = BUILD_CXX; | |
910 | } else { | |
911 | build = BUILD_C; | |
912 | } | |
913 | ||
914 | cli_add_source_file(dp->d_name, fpath, opath, &st, build); | |
915 | } | |
916 | ||
917 | static void | |
918 | cli_register_kore_file(char *fpath, struct dirent *dp) | |
919 | { | |
920 | struct stat st, ost; | |
921 | char *opath, *ext, *fname; | |
922 | ||
923 | if ((ext = strrchr(fpath, '.')) == NULL || strcmp(ext, ".c")) | |
715 | 924 | return; |
716 | } | |
717 | ||
718 | cli_add_cfile(dp->d_name, fpath, opath, &st, 1, cpp); | |
925 | ||
926 | if (stat(fpath, &st) == -1) | |
927 | cli_fatal("stat(%s): %s", fpath, errno_s); | |
928 | ||
929 | *ext = '\0'; | |
930 | if ((fname = basename(fpath)) == NULL) | |
931 | cli_fatal("basename failed"); | |
932 | ||
933 | (void)cli_vasprintf(&opath, "%s/.objs/%s.o", rootdir, fname); | |
934 | ||
935 | /* Silently ignore non existing object files for kore source files. */ | |
936 | if (stat(opath, &ost) == -1) { | |
937 | free(opath); | |
938 | return; | |
939 | } | |
940 | ||
941 | cli_add_source_file(dp->d_name, fpath, opath, &st, BUILD_NOBUILD); | |
719 | 942 | } |
720 | 943 | |
721 | 944 | static void |
765 | 988 | static void |
766 | 989 | cli_generate_certs(void) |
767 | 990 | { |
768 | #if !defined(KORE_BENCHMARK) | |
991 | #if !defined(KORE_NO_TLS) | |
769 | 992 | BIGNUM *e; |
770 | 993 | FILE *fp; |
771 | 994 | time_t now; |
865 | 1088 | } |
866 | 1089 | |
867 | 1090 | static void |
868 | cli_compile_cfile(void *arg) | |
869 | { | |
870 | int idx; | |
1091 | cli_compile_source_file(void *arg) | |
1092 | { | |
1093 | int idx, i; | |
871 | 1094 | struct cfile *cf = arg; |
872 | char *args[24], *ipath[2], *p, *cppstandard; | |
873 | #if defined(KORE_USE_PGSQL) | |
874 | char *ppath; | |
875 | #endif | |
876 | ||
877 | (void)cli_vasprintf(&ipath[0], "-I%s/src", rootdir); | |
878 | (void)cli_vasprintf(&ipath[1], "-I%s/src/includes", rootdir); | |
879 | ||
880 | /* | |
881 | * These compiler options should be settable | |
882 | * somehow by the user if they so choose. | |
883 | */ | |
1095 | char *args[32 + CFLAGS_MAX]; | |
1096 | char *compiler; | |
1097 | char **flags; | |
1098 | int flags_count; | |
1099 | ||
1100 | switch (cf->build) { | |
1101 | case BUILD_C: | |
1102 | compiler = compiler_c; | |
1103 | flags = cflags; | |
1104 | flags_count = cflags_count; | |
1105 | break; | |
1106 | case BUILD_CXX: | |
1107 | compiler = compiler_cpp; | |
1108 | flags = cxxflags; | |
1109 | flags_count = cxxflags_count; | |
1110 | break; | |
1111 | default: | |
1112 | cli_fatal("cli_compile_file: unexpected file type: %d", | |
1113 | cf->build); | |
1114 | break; | |
1115 | } | |
1116 | ||
884 | 1117 | idx = 0; |
885 | 1118 | args[idx++] = compiler; |
886 | args[idx++] = ipath[0]; | |
887 | args[idx++] = ipath[1]; | |
888 | #if defined(PREFIX) | |
889 | (void)cli_vasprintf(&args[idx++], "-I%s/include", PREFIX); | |
890 | #else | |
891 | args[idx++] = "-I/usr/local/include"; | |
892 | #endif | |
893 | ||
894 | #if defined(KORE_USE_PGSQL) | |
895 | (void)cli_vasprintf(&ppath, "-I%s", PGSQL_INCLUDE_PATH); | |
896 | args[idx++] = ppath; | |
897 | #endif | |
898 | ||
899 | args[idx++] = "-Wall"; | |
900 | args[idx++] = "-Wmissing-declarations"; | |
901 | args[idx++] = "-Wshadow"; | |
902 | args[idx++] = "-Wpointer-arith"; | |
903 | args[idx++] = "-Wcast-qual"; | |
904 | args[idx++] = "-Wsign-compare"; | |
905 | args[idx++] = "-fPIC"; | |
906 | args[idx++] = "-g"; | |
907 | ||
908 | if (cf->cpp) { | |
909 | args[idx++] = "-Woverloaded-virtual"; | |
910 | args[idx++] = "-Wold-style-cast"; | |
911 | args[idx++] = "-Wnon-virtual-dtor"; | |
912 | ||
913 | if ((p = getenv("CXXSTD")) != NULL) { | |
914 | (void)cli_vasprintf(&cppstandard, "-std=%s", p); | |
915 | args[idx++] = cppstandard; | |
916 | } | |
917 | } else { | |
918 | args[idx++] = "-Wstrict-prototypes"; | |
919 | args[idx++] = "-Wmissing-prototypes"; | |
920 | } | |
1119 | ||
1120 | for (i = 0; i < flags_count; i++) | |
1121 | args[idx++] = flags[i]; | |
921 | 1122 | |
922 | 1123 | args[idx++] = "-c"; |
923 | 1124 | args[idx++] = cf->fpath; |
926 | 1127 | args[idx] = NULL; |
927 | 1128 | |
928 | 1129 | execvp(compiler, args); |
1130 | cli_fatal("failed to start '%s': %s", compiler, errno_s); | |
929 | 1131 | } |
930 | 1132 | |
931 | 1133 | static void |
932 | 1134 | cli_link_library(void *arg) |
933 | 1135 | { |
934 | 1136 | struct cfile *cf; |
935 | int idx, f, i, has_cpp; | |
936 | char *args[cfiles_count + 11 + LD_FLAGS_MAX]; | |
937 | char *p, *libname, *flags[LD_FLAGS_MAX], *cpplib; | |
938 | ||
939 | if ((p = getenv("LDFLAGS")) != NULL) | |
940 | f = kore_split_string(p, " ", flags, LD_FLAGS_MAX); | |
1137 | struct buildopt *bopt; | |
1138 | int idx, i; | |
1139 | char *output; | |
1140 | char *args[source_files_count + 11 + LD_FLAGS_MAX]; | |
1141 | ||
1142 | bopt = arg; | |
1143 | ||
1144 | if (bopt->single_binary) | |
1145 | (void)cli_vasprintf(&output, "%s/%s", rootdir, appl); | |
941 | 1146 | else |
942 | f = 0; | |
943 | ||
944 | (void)cli_vasprintf(&libname, "%s/%s.so", rootdir, appl); | |
1147 | (void)cli_vasprintf(&output, "%s/%s.so", rootdir, appl); | |
945 | 1148 | |
946 | 1149 | idx = 0; |
947 | args[idx++] = compiler; | |
948 | ||
1150 | args[idx++] = compiler_ld; | |
1151 | ||
1152 | TAILQ_FOREACH(cf, &source_files, list) | |
1153 | args[idx++] = cf->opath; | |
1154 | ||
1155 | for (i = 0; i < ldflags_count; i++) | |
1156 | args[idx++] = ldflags[i]; | |
1157 | ||
1158 | if (bopt->single_binary) { | |
1159 | args[idx++] = "-rdynamic"; | |
1160 | #if defined(__linux__) | |
1161 | args[idx++] = "-ldl"; | |
1162 | #endif | |
1163 | } | |
1164 | ||
1165 | args[idx++] = "-o"; | |
1166 | args[idx++] = output; | |
1167 | args[idx] = NULL; | |
1168 | ||
1169 | execvp(compiler_ld, args); | |
1170 | cli_fatal("failed to start '%s': %s", compiler_ld, errno_s); | |
1171 | } | |
1172 | ||
1173 | static void | |
1174 | cli_compile_kore(void *arg) | |
1175 | { | |
1176 | struct buildopt *bopt = arg; | |
1177 | int idx, i, fcnt; | |
1178 | char *obj, *args[16], pwd[MAXPATHLEN], *flavors[7]; | |
1179 | ||
1180 | if (getcwd(pwd, sizeof(pwd)) == NULL) | |
1181 | cli_fatal("could not get cwd: %s", errno_s); | |
1182 | ||
1183 | (void)cli_vasprintf(&obj, "OBJDIR=%s/.objs", pwd); | |
1184 | ||
1185 | if (putenv(obj) != 0) | |
1186 | cli_fatal("cannot set OBJDIR for building kore"); | |
1187 | ||
1188 | if (putenv("CFLAGS=-DKORE_SINGLE_BINARY") != 0) | |
1189 | cli_fatal("cannot set CFLAGS for building kore"); | |
1190 | ||
1191 | fcnt = kore_split_string(bopt->kore_flavor, " ", flavors, 7); | |
1192 | ||
1193 | #if defined(OpenBSD) || defined(__FreeBSD_version) || \ | |
1194 | defined(NetBSD) || defined(__DragonFly_version) | |
1195 | args[0] = "gmake"; | |
1196 | #else | |
1197 | args[0] = "make"; | |
1198 | #endif | |
1199 | ||
1200 | args[1] = "-s"; | |
1201 | args[2] = "-C"; | |
1202 | args[3] = bopt->kore_source; | |
1203 | args[4] = "objects"; | |
1204 | ||
1205 | idx = 5; | |
1206 | for (i = 0; i < fcnt; i++) { | |
1207 | printf("using flavor %s\n", flavors[i]); | |
1208 | args[idx++] = flavors[i]; | |
1209 | } | |
1210 | ||
1211 | args[idx] = NULL; | |
1212 | ||
1213 | execvp(args[0], args); | |
1214 | cli_fatal("failed to start '%s': %s", args[0], errno_s); | |
1215 | } | |
1216 | ||
1217 | static void | |
1218 | cli_run_kore(void) | |
1219 | { | |
1220 | struct buildopt *bopt; | |
1221 | char *args[4], *cpath, *cmd, *flags; | |
1222 | ||
1223 | bopt = cli_buildopt_default(); | |
1224 | ||
1225 | if (bopt->single_binary) { | |
1226 | cpath = NULL; | |
1227 | flags = "-fnr"; | |
1228 | (void)cli_vasprintf(&cmd, "./%s", appl); | |
1229 | } else { | |
1230 | cmd = "kore"; | |
1231 | flags = "-fnrc"; | |
1232 | (void)cli_vasprintf(&cpath, "conf/%s.conf", appl); | |
1233 | } | |
1234 | ||
1235 | args[0] = cmd; | |
1236 | args[1] = flags; | |
1237 | ||
1238 | if (cpath != NULL) { | |
1239 | args[2] = cpath; | |
1240 | args[3] = NULL; | |
1241 | } else { | |
1242 | args[2] = NULL; | |
1243 | } | |
1244 | ||
1245 | execvp(args[0], args); | |
1246 | cli_fatal("failed to start '%s': %s", args[0], errno_s); | |
1247 | } | |
1248 | ||
1249 | static void | |
1250 | cli_buildopt_parse(const char *path) | |
1251 | { | |
1252 | FILE *fp; | |
1253 | struct buildopt *bopt; | |
1254 | char buf[BUFSIZ], *p, *t; | |
1255 | ||
1256 | if ((fp = fopen(path, "r")) == NULL) | |
1257 | cli_fatal("cli_buildopt_parse: fopen(%s): %s", path, errno_s); | |
1258 | ||
1259 | bopt = NULL; | |
1260 | ||
1261 | while ((p = kore_read_line(fp, buf, sizeof(buf))) != NULL) { | |
1262 | if (strlen(p) == 0) | |
1263 | continue; | |
1264 | ||
1265 | if (bopt != NULL && !strcmp(p, "}")) { | |
1266 | bopt = NULL; | |
1267 | continue; | |
1268 | } | |
1269 | ||
1270 | if (bopt == NULL) { | |
1271 | if ((t = strchr(p, '=')) != NULL) | |
1272 | goto parse_option; | |
1273 | if ((t = strchr(p, ' ')) == NULL) | |
1274 | cli_fatal("unexpected '%s'", p); | |
1275 | *(t)++ = '\0'; | |
1276 | if (strcmp(t, "{")) | |
1277 | cli_fatal("expected '{', got '%s'", t); | |
1278 | bopt = cli_buildopt_new(p); | |
1279 | continue; | |
1280 | } | |
1281 | ||
1282 | if ((t = strchr(p, '=')) == NULL) { | |
1283 | printf("bad buildopt line: '%s'\n", p); | |
1284 | continue; | |
1285 | } | |
1286 | ||
1287 | parse_option: | |
1288 | *(t)++ = '\0'; | |
1289 | ||
1290 | p = kore_text_trim(p, strlen(p)); | |
1291 | t = kore_text_trim(t, strlen(t)); | |
1292 | ||
1293 | if (!strcasecmp(p, "cflags")) { | |
1294 | cli_buildopt_cflags(bopt, t); | |
1295 | } else if (!strcasecmp(p, "cxxflags")) { | |
1296 | cli_buildopt_cxxflags(bopt, t); | |
1297 | } else if (!strcasecmp(p, "ldflags")) { | |
1298 | cli_buildopt_ldflags(bopt, t); | |
1299 | } else if (!strcasecmp(p, "single_binary")) { | |
1300 | cli_buildopt_single_binary(bopt, t); | |
1301 | } else if (!strcasecmp(p, "kore_source")) { | |
1302 | cli_buildopt_kore_source(bopt, t); | |
1303 | } else if (!strcasecmp(p, "kore_flavor")) { | |
1304 | cli_buildopt_kore_flavor(bopt, t); | |
1305 | } else { | |
1306 | printf("ignoring unknown option '%s'\n", p); | |
1307 | } | |
1308 | } | |
1309 | } | |
1310 | ||
1311 | static struct buildopt * | |
1312 | cli_buildopt_new(const char *name) | |
1313 | { | |
1314 | struct buildopt *bopt; | |
1315 | ||
1316 | bopt = kore_malloc(sizeof(*bopt)); | |
1317 | bopt->cflags = NULL; | |
1318 | bopt->cxxflags = NULL; | |
1319 | bopt->ldflags = NULL; | |
1320 | bopt->single_binary = 0; | |
1321 | bopt->kore_source = NULL; | |
1322 | bopt->kore_flavor = NULL; | |
1323 | bopt->name = kore_strdup(name); | |
1324 | ||
1325 | TAILQ_INSERT_TAIL(&build_options, bopt, list); | |
1326 | return (bopt); | |
1327 | } | |
1328 | ||
1329 | static struct buildopt * | |
1330 | cli_buildopt_find(const char *name) | |
1331 | { | |
1332 | struct buildopt *bopt; | |
1333 | ||
1334 | TAILQ_FOREACH(bopt, &build_options, list) { | |
1335 | if (!strcmp(bopt->name, name)) | |
1336 | return (bopt); | |
1337 | } | |
1338 | ||
1339 | return (NULL); | |
1340 | } | |
1341 | ||
1342 | static struct buildopt * | |
1343 | cli_buildopt_default(void) | |
1344 | { | |
1345 | struct buildopt *bopt; | |
1346 | ||
1347 | if ((bopt = cli_buildopt_find("_default")) == NULL) | |
1348 | fatal("no _default buildopt options"); | |
1349 | ||
1350 | return (bopt); | |
1351 | } | |
1352 | ||
1353 | static void | |
1354 | cli_buildopt_cleanup(void) | |
1355 | { | |
1356 | struct buildopt *bopt, *next; | |
1357 | ||
1358 | for (bopt = TAILQ_FIRST(&build_options); bopt != NULL; bopt = next) { | |
1359 | next = TAILQ_NEXT(bopt, list); | |
1360 | TAILQ_REMOVE(&build_options, bopt, list); | |
1361 | ||
1362 | if (bopt->cflags != NULL) | |
1363 | kore_buf_free(bopt->cflags); | |
1364 | if (bopt->cxxflags != NULL) | |
1365 | kore_buf_free(bopt->cxxflags); | |
1366 | if (bopt->ldflags != NULL) | |
1367 | kore_buf_free(bopt->ldflags); | |
1368 | if (bopt->kore_source != NULL) | |
1369 | kore_free(bopt->kore_source); | |
1370 | if (bopt->kore_flavor != NULL) | |
1371 | kore_free(bopt->kore_flavor); | |
1372 | kore_free(bopt); | |
1373 | } | |
1374 | } | |
1375 | ||
1376 | static void | |
1377 | cli_buildopt_cflags(struct buildopt *bopt, const char *string) | |
1378 | { | |
1379 | if (bopt == NULL) | |
1380 | bopt = cli_buildopt_default(); | |
1381 | ||
1382 | if (bopt->cflags == NULL) | |
1383 | bopt->cflags = kore_buf_alloc(128); | |
1384 | ||
1385 | kore_buf_appendf(bopt->cflags, "%s ", string); | |
1386 | } | |
1387 | ||
1388 | static void | |
1389 | cli_buildopt_cxxflags(struct buildopt *bopt, const char *string) | |
1390 | { | |
1391 | if (bopt == NULL) | |
1392 | bopt = cli_buildopt_default(); | |
1393 | ||
1394 | if (bopt->cxxflags == NULL) | |
1395 | bopt->cxxflags = kore_buf_alloc(128); | |
1396 | ||
1397 | kore_buf_appendf(bopt->cxxflags, "%s ", string); | |
1398 | } | |
1399 | ||
1400 | static void | |
1401 | cli_buildopt_ldflags(struct buildopt *bopt, const char *string) | |
1402 | { | |
1403 | if (bopt == NULL) | |
1404 | bopt = cli_buildopt_default(); | |
1405 | ||
1406 | if (bopt->ldflags == NULL) | |
1407 | bopt->ldflags = kore_buf_alloc(128); | |
1408 | ||
1409 | kore_buf_appendf(bopt->ldflags, "%s ", string); | |
1410 | } | |
1411 | ||
1412 | static void | |
1413 | cli_buildopt_single_binary(struct buildopt *bopt, const char *string) | |
1414 | { | |
1415 | if (bopt == NULL) | |
1416 | bopt = cli_buildopt_default(); | |
1417 | else | |
1418 | cli_fatal("single_binary only supported in global context"); | |
1419 | ||
1420 | if (!strcmp(string, "yes")) | |
1421 | bopt->single_binary = 1; | |
1422 | else | |
1423 | bopt->single_binary = 0; | |
1424 | } | |
1425 | ||
1426 | static void | |
1427 | cli_buildopt_kore_source(struct buildopt *bopt, const char *string) | |
1428 | { | |
1429 | if (bopt == NULL) | |
1430 | bopt = cli_buildopt_default(); | |
1431 | else | |
1432 | cli_fatal("kore_source only supported in global context"); | |
1433 | ||
1434 | if (bopt->kore_source != NULL) | |
1435 | kore_free(bopt->kore_source); | |
1436 | ||
1437 | bopt->kore_source = kore_strdup(string); | |
1438 | } | |
1439 | ||
1440 | static void | |
1441 | cli_buildopt_kore_flavor(struct buildopt *bopt, const char *string) | |
1442 | { | |
1443 | if (bopt == NULL) | |
1444 | bopt = cli_buildopt_default(); | |
1445 | else | |
1446 | cli_fatal("kore_flavor only supported in global context"); | |
1447 | ||
1448 | if (bopt->kore_flavor != NULL) | |
1449 | kore_free(bopt->kore_flavor); | |
1450 | ||
1451 | bopt->kore_flavor = kore_strdup(string); | |
1452 | } | |
1453 | ||
1454 | static void | |
1455 | cli_build_flags_common(struct kore_buf* buf) | |
1456 | { | |
1457 | kore_buf_appendf(buf, | |
1458 | "-fPIC -I%s/src -I%s/src/includes ", rootdir, rootdir); | |
1459 | #if defined(PREFIX) | |
1460 | kore_buf_appendf(buf, "-I%s/include ", PREFIX); | |
1461 | #else | |
1462 | kore_buf_appendf(buf, "-I/usr/local/include "); | |
1463 | #endif | |
949 | 1464 | #if defined(__MACH__) |
950 | args[idx++] = "-dynamiclib"; | |
951 | args[idx++] = "-undefined"; | |
952 | args[idx++] = "suppress"; | |
953 | args[idx++] = "-flat_namespace"; | |
1465 | /* Add default openssl include path from homebrew / ports under OSX. */ | |
1466 | kore_buf_appendf(buf, "-I/opt/local/include "); | |
1467 | kore_buf_appendf(buf, "-I/usr/local/opt/openssl/include "); | |
1468 | #endif | |
1469 | #if defined(KORE_USE_PGSQL) | |
1470 | kore_buf_appendf(buf, "-I%s ", PGSQL_INCLUDE_PATH); | |
1471 | #endif | |
1472 | #if defined(KORE_NO_HTTP) | |
1473 | kore_buf_appendf(buf, "-DKORE_NO_HTTP "); | |
1474 | #endif | |
1475 | #if defined(KORE_NO_TLS) | |
1476 | kore_buf_appendf(buf, "-DKORE_NO_TLS "); | |
1477 | #endif | |
1478 | } | |
1479 | ||
1480 | static void | |
1481 | cli_build_cflags(struct buildopt *bopt) | |
1482 | { | |
1483 | struct buildopt *obopt; | |
1484 | char *string; | |
1485 | ||
1486 | if ((obopt = cli_buildopt_find(flavor)) == NULL) | |
1487 | cli_fatal("no such build flavor: %s", flavor); | |
1488 | ||
1489 | if (bopt->cflags == NULL) | |
1490 | bopt->cflags = kore_buf_alloc(128); | |
1491 | ||
1492 | cli_build_flags_common(bopt->cflags); | |
1493 | ||
1494 | if (obopt != NULL && obopt->cflags != NULL) { | |
1495 | kore_buf_append(bopt->cflags, obopt->cflags->data, | |
1496 | obopt->cflags->offset); | |
1497 | } | |
1498 | ||
1499 | if (bopt->single_binary) | |
1500 | kore_buf_appendf(bopt->cflags, "-DKORE_SINGLE_BINARY"); | |
1501 | ||
1502 | string = kore_buf_stringify(bopt->cflags, NULL); | |
1503 | printf("CFLAGS=%s\n", string); | |
1504 | cflags_count = kore_split_string(string, " ", cflags, CFLAGS_MAX); | |
1505 | } | |
1506 | ||
1507 | static void | |
1508 | cli_build_cxxflags(struct buildopt *bopt) | |
1509 | { | |
1510 | struct buildopt *obopt; | |
1511 | char *string; | |
1512 | ||
1513 | if ((obopt = cli_buildopt_find(flavor)) == NULL) | |
1514 | cli_fatal("no such build flavor: %s", flavor); | |
1515 | ||
1516 | if (bopt->cxxflags == NULL) | |
1517 | bopt->cxxflags = kore_buf_alloc(128); | |
1518 | ||
1519 | cli_build_flags_common(bopt->cxxflags); | |
1520 | ||
1521 | if (obopt != NULL && obopt->cxxflags != NULL) { | |
1522 | kore_buf_append(bopt->cxxflags, obopt->cxxflags->data, | |
1523 | obopt->cxxflags->offset); | |
1524 | } | |
1525 | ||
1526 | string = kore_buf_stringify(bopt->cxxflags, NULL); | |
1527 | if (cxx_files_count > 0) | |
1528 | printf("CXXFLAGS=%s\n", string); | |
1529 | cxxflags_count = kore_split_string(string, " ", cxxflags, CXXFLAGS_MAX); | |
1530 | } | |
1531 | ||
1532 | static void | |
1533 | cli_build_ldflags(struct buildopt *bopt) | |
1534 | { | |
1535 | struct buildopt *obopt; | |
1536 | char *string; | |
1537 | ||
1538 | if ((obopt = cli_buildopt_find(flavor)) == NULL) | |
1539 | cli_fatal("no such build flavor: %s", flavor); | |
1540 | ||
1541 | if (bopt->ldflags == NULL) | |
1542 | bopt->ldflags = kore_buf_alloc(128); | |
1543 | ||
1544 | if (bopt->single_binary == 0) { | |
1545 | #if defined(__MACH__) | |
1546 | kore_buf_appendf(bopt->ldflags, | |
1547 | "-dynamiclib -undefined suppress -flat_namespace "); | |
954 | 1548 | #else |
955 | args[idx++] = "-shared"; | |
956 | #endif | |
957 | ||
958 | has_cpp = 0; | |
959 | TAILQ_FOREACH(cf, &source_files, list) { | |
960 | if (cf->cpp) | |
961 | has_cpp = 1; | |
962 | args[idx++] = cf->opath; | |
963 | } | |
964 | ||
965 | if (has_cpp) { | |
966 | if ((p = getenv("CXXLIB")) != NULL) { | |
967 | (void)cli_vasprintf(&cpplib, "-l%s", p); | |
968 | args[idx++] = cpplib; | |
969 | } else { | |
970 | args[idx++] = "-lstdc++"; | |
971 | } | |
972 | } | |
973 | ||
974 | for (i = 0; i < f; i++) | |
975 | args[idx++] = flags[i]; | |
976 | ||
977 | args[idx++] = "-o"; | |
978 | args[idx++] = libname; | |
979 | args[idx] = NULL; | |
980 | ||
981 | execvp(compiler, args); | |
982 | } | |
983 | ||
984 | static void | |
985 | cli_run_kore(void *arg) | |
986 | { | |
987 | char *args[4], *cpath; | |
988 | ||
989 | (void)cli_vasprintf(&cpath, "conf/%s.conf", appl); | |
990 | ||
991 | args[0] = "kore"; | |
992 | args[1] = "-fnrc"; | |
993 | args[2] = cpath; | |
994 | args[3] = NULL; | |
995 | ||
996 | execvp("kore", args); | |
1549 | kore_buf_appendf(bopt->ldflags, "-shared "); | |
1550 | #endif | |
1551 | } | |
1552 | ||
1553 | if (obopt != NULL && obopt->ldflags != NULL) { | |
1554 | kore_buf_append(bopt->ldflags, obopt->ldflags->data, | |
1555 | obopt->ldflags->offset); | |
1556 | } | |
1557 | ||
1558 | string = kore_buf_stringify(bopt->ldflags, NULL); | |
1559 | printf("LDFLAGS=%s\n", string); | |
1560 | ldflags_count = kore_split_string(string, " ", ldflags, LD_FLAGS_MAX); | |
1561 | } | |
1562 | ||
1563 | static void | |
1564 | cli_flavor_load(void) | |
1565 | { | |
1566 | FILE *fp; | |
1567 | char buf[BUFSIZ], pwd[MAXPATHLEN], *p, *conf; | |
1568 | ||
1569 | if (getcwd(pwd, sizeof(pwd)) == NULL) | |
1570 | cli_fatal("could not get cwd: %s", errno_s); | |
1571 | ||
1572 | appl = basename(pwd); | |
1573 | if (appl == NULL) | |
1574 | cli_fatal("basename: %s", errno_s); | |
1575 | appl = kore_strdup(appl); | |
1576 | (void)cli_vasprintf(&conf, "conf/%s.conf", appl); | |
1577 | ||
1578 | if (!cli_dir_exists("conf") || !cli_file_exists(conf)) | |
1579 | cli_fatal("%s doesn't appear to be a kore app", appl); | |
1580 | free(conf); | |
1581 | ||
1582 | if ((fp = fopen(".flavor", "r")) == NULL) { | |
1583 | flavor = kore_strdup("dev"); | |
1584 | return; | |
1585 | } | |
1586 | ||
1587 | if (fgets(buf, sizeof(buf), fp) == NULL) | |
1588 | cli_fatal("failed to read flavor from file"); | |
1589 | ||
1590 | if ((p = strchr(buf, '\n')) != NULL) | |
1591 | *p = '\0'; | |
1592 | ||
1593 | flavor = kore_strdup(buf); | |
1594 | (void)fclose(fp); | |
1595 | } | |
1596 | ||
1597 | static void | |
1598 | cli_flavor_change(const char *name) | |
1599 | { | |
1600 | FILE *fp; | |
1601 | int ret; | |
1602 | struct buildopt *bopt; | |
1603 | ||
1604 | if ((bopt = cli_buildopt_find(name)) == NULL) | |
1605 | cli_fatal("no such flavor: %s", name); | |
1606 | ||
1607 | if ((fp = fopen(".flavor.tmp", "w")) == NULL) | |
1608 | cli_fatal("failed to open temporary file to save flavor"); | |
1609 | ||
1610 | ret = fprintf(fp, "%s\n", name); | |
1611 | if (ret == -1 || (size_t)ret != (strlen(name) + 1)) | |
1612 | cli_fatal("failed to write new build flavor"); | |
1613 | ||
1614 | (void)fclose(fp); | |
1615 | ||
1616 | if (rename(".flavor.tmp", ".flavor") == -1) | |
1617 | cli_fatal("failed to replace build flavor"); | |
1618 | ||
1619 | cli_clean(0, NULL); | |
997 | 1620 | } |
998 | 1621 | |
999 | 1622 | static void |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
13 | 13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 14 | */ |
15 | 15 | |
16 | #include <sys/param.h> | |
16 | 17 | #include <sys/stat.h> |
17 | 18 | |
19 | #include <stdio.h> | |
20 | #include <string.h> | |
18 | 21 | #include <ctype.h> |
19 | 22 | #include <limits.h> |
20 | 23 | #include <fcntl.h> |
27 | 30 | #include "pgsql.h" |
28 | 31 | #endif |
29 | 32 | |
33 | #if defined(KORE_USE_TASKS) | |
34 | #include "tasks.h" | |
35 | #endif | |
36 | ||
30 | 37 | /* XXX - This is becoming a clusterfuck. Fix it. */ |
31 | 38 | |
32 | static int configure_include(char **); | |
33 | static int configure_bind(char **); | |
34 | static int configure_load(char **); | |
35 | static int configure_handler(char **); | |
36 | static int configure_domain(char **); | |
37 | static int configure_chroot(char **); | |
38 | static int configure_runas(char **); | |
39 | static int configure_workers(char **); | |
40 | static int configure_pidfile(char **); | |
41 | static int configure_accesslog(char **); | |
42 | static int configure_certfile(char **); | |
43 | static int configure_certkey(char **); | |
44 | static int configure_rlimit_nofiles(char **); | |
45 | static int configure_max_connections(char **); | |
46 | static int configure_accept_threshold(char **); | |
47 | static int configure_set_affinity(char **); | |
48 | static int configure_tls_version(char **); | |
49 | static int configure_tls_cipher(char **); | |
50 | static int configure_tls_dhparam(char **); | |
51 | static int configure_spdy_idle_time(char **); | |
52 | static int configure_http_header_max(char **); | |
53 | static int configure_http_body_max(char **); | |
54 | static int configure_http_hsts_enable(char **); | |
55 | static int configure_http_keepalive_time(char **); | |
56 | static int configure_http_request_limit(char **); | |
57 | static int configure_validator(char **); | |
58 | static int configure_params(char **); | |
59 | static int configure_validate(char **); | |
60 | static int configure_client_certificates(char **); | |
61 | static int configure_authentication(char **); | |
62 | static int configure_authentication_uri(char **); | |
63 | static int configure_authentication_type(char **); | |
64 | static int configure_authentication_value(char **); | |
65 | static int configure_authentication_validator(char **); | |
66 | static int configure_websocket_maxframe(char **); | |
67 | static int configure_websocket_timeout(char **); | |
68 | static int configure_socket_backlog(char **); | |
39 | #if !defined(KORE_SINGLE_BINARY) | |
40 | static int configure_load(char *); | |
41 | #else | |
42 | static FILE *config_file_write(void); | |
43 | extern u_int8_t asset_builtin_kore_conf[]; | |
44 | extern u_int32_t asset_len_builtin_kore_conf; | |
45 | #endif | |
46 | ||
47 | static int configure_include(char *); | |
48 | static int configure_bind(char *); | |
49 | static int configure_domain(char *); | |
50 | static int configure_chroot(char *); | |
51 | static int configure_runas(char *); | |
52 | static int configure_workers(char *); | |
53 | static int configure_pidfile(char *); | |
54 | static int configure_rlimit_nofiles(char *); | |
55 | static int configure_max_connections(char *); | |
56 | static int configure_accept_threshold(char *); | |
57 | static int configure_set_affinity(char *); | |
58 | static int configure_socket_backlog(char *); | |
59 | ||
60 | #if !defined(KORE_NO_TLS) | |
61 | static int configure_certfile(char *); | |
62 | static int configure_certkey(char *); | |
63 | static int configure_tls_version(char *); | |
64 | static int configure_tls_cipher(char *); | |
65 | static int configure_tls_dhparam(char *); | |
66 | static int configure_client_certificates(char *); | |
67 | #endif | |
68 | ||
69 | #if !defined(KORE_NO_HTTP) | |
70 | static int configure_handler(int, char *); | |
71 | static int configure_static_handler(char *); | |
72 | static int configure_dynamic_handler(char *); | |
73 | static int configure_accesslog(char *); | |
74 | static int configure_http_header_max(char *); | |
75 | static int configure_http_body_max(char *); | |
76 | static int configure_http_hsts_enable(char *); | |
77 | static int configure_http_keepalive_time(char *); | |
78 | static int configure_http_request_limit(char *); | |
79 | static int configure_http_body_disk_offload(char *); | |
80 | static int configure_http_body_disk_path(char *); | |
81 | static int configure_validator(char *); | |
82 | static int configure_params(char *); | |
83 | static int configure_validate(char *); | |
84 | static int configure_authentication(char *); | |
85 | static int configure_authentication_uri(char *); | |
86 | static int configure_authentication_type(char *); | |
87 | static int configure_authentication_value(char *); | |
88 | static int configure_authentication_validator(char *); | |
89 | static int configure_websocket_maxframe(char *); | |
90 | static int configure_websocket_timeout(char *); | |
91 | #endif | |
69 | 92 | |
70 | 93 | #if defined(KORE_USE_PGSQL) |
71 | static int configure_pgsql_conn_max(char **); | |
94 | static int configure_pgsql_conn_max(char *); | |
95 | #endif | |
96 | ||
97 | #if defined(KORE_USE_TASKS) | |
98 | static int configure_task_threads(char *); | |
72 | 99 | #endif |
73 | 100 | |
74 | 101 | static void domain_sslstart(void); |
75 | static void kore_parse_config_file(char *); | |
102 | static void kore_parse_config_file(const char *); | |
76 | 103 | |
77 | 104 | static struct { |
78 | 105 | const char *name; |
79 | int (*configure)(char **); | |
106 | int (*configure)(char *); | |
80 | 107 | } config_names[] = { |
81 | 108 | { "include", configure_include }, |
82 | 109 | { "bind", configure_bind }, |
110 | #if !defined(KORE_SINGLE_BINARY) | |
83 | 111 | { "load", configure_load }, |
84 | { "static", configure_handler }, | |
85 | { "dynamic", configure_handler }, | |
86 | { "tls_version", configure_tls_version }, | |
87 | { "tls_cipher", configure_tls_cipher }, | |
88 | { "tls_dhparam", configure_tls_dhparam }, | |
89 | { "spdy_idle_time", configure_spdy_idle_time }, | |
112 | #endif | |
90 | 113 | { "domain", configure_domain }, |
91 | 114 | { "chroot", configure_chroot }, |
92 | 115 | { "runas", configure_runas }, |
96 | 119 | { "worker_accept_threshold", configure_accept_threshold }, |
97 | 120 | { "worker_set_affinity", configure_set_affinity }, |
98 | 121 | { "pidfile", configure_pidfile }, |
99 | { "accesslog", configure_accesslog }, | |
122 | { "socket_backlog", configure_socket_backlog }, | |
123 | #if !defined(KORE_NO_TLS) | |
124 | { "tls_version", configure_tls_version }, | |
125 | { "tls_cipher", configure_tls_cipher }, | |
126 | { "tls_dhparam", configure_tls_dhparam }, | |
100 | 127 | { "certfile", configure_certfile }, |
101 | 128 | { "certkey", configure_certkey }, |
102 | 129 | { "client_certificates", configure_client_certificates }, |
130 | #endif | |
131 | #if !defined(KORE_NO_HTTP) | |
132 | { "static", configure_static_handler }, | |
133 | { "dynamic", configure_dynamic_handler }, | |
134 | { "accesslog", configure_accesslog }, | |
103 | 135 | { "http_header_max", configure_http_header_max }, |
104 | 136 | { "http_body_max", configure_http_body_max }, |
105 | 137 | { "http_hsts_enable", configure_http_hsts_enable }, |
106 | 138 | { "http_keepalive_time", configure_http_keepalive_time }, |
107 | 139 | { "http_request_limit", configure_http_request_limit }, |
140 | { "http_body_disk_offload", configure_http_body_disk_offload }, | |
141 | { "http_body_disk_path", configure_http_body_disk_path }, | |
108 | 142 | { "validator", configure_validator }, |
109 | 143 | { "params", configure_params }, |
110 | 144 | { "validate", configure_validate }, |
115 | 149 | { "authentication_validator", configure_authentication_validator }, |
116 | 150 | { "websocket_maxframe", configure_websocket_maxframe }, |
117 | 151 | { "websocket_timeout", configure_websocket_timeout }, |
118 | { "socket_backlog", configure_socket_backlog }, | |
152 | #endif | |
119 | 153 | #if defined(KORE_USE_PGSQL) |
120 | 154 | { "pgsql_conn_max", configure_pgsql_conn_max }, |
121 | 155 | #endif |
156 | #if defined(KORE_USE_TASKS) | |
157 | { "task_threads", configure_task_threads }, | |
158 | #endif | |
122 | 159 | { NULL, NULL }, |
123 | 160 | }; |
124 | 161 | |
162 | #if !defined(KORE_SINGLE_BINARY) | |
125 | 163 | char *config_file = NULL; |
164 | #endif | |
165 | ||
166 | #if !defined(KORE_NO_HTTP) | |
126 | 167 | static u_int8_t current_method = 0; |
127 | 168 | static struct kore_auth *current_auth = NULL; |
169 | static struct kore_module_handle *current_handler = NULL; | |
170 | #endif | |
171 | ||
172 | extern const char *__progname; | |
128 | 173 | static struct kore_domain *current_domain = NULL; |
129 | static struct kore_module_handle *current_handler = NULL; | |
130 | 174 | |
131 | 175 | void |
132 | 176 | kore_parse_config(void) |
133 | 177 | { |
178 | #if !defined(KORE_SINGLE_BINARY) | |
134 | 179 | kore_parse_config_file(config_file); |
180 | #else | |
181 | kore_parse_config_file(NULL); | |
182 | #endif | |
135 | 183 | |
136 | 184 | if (!kore_module_loaded()) |
137 | fatal("no site module was loaded"); | |
138 | ||
139 | if (LIST_EMPTY(&listeners)) | |
140 | fatal("no listeners defined"); | |
185 | fatal("no application module was loaded"); | |
141 | 186 | |
142 | 187 | if (skip_chroot != 1 && chroot_path == NULL) { |
143 | 188 | fatal("missing a chroot path"); |
148 | 193 | } |
149 | 194 | |
150 | 195 | if (skip_runas != 1 && runas_user == NULL) { |
151 | fatal("missing runas user"); | |
196 | fatal("missing runas user, use -r to skip it"); | |
152 | 197 | } |
153 | 198 | |
154 | 199 | if (getuid() != 0 && skip_runas == 0) { |
155 | fatal("cannot drop privileges, use -p to skip it"); | |
200 | fatal("cannot drop privileges, use -r to skip it"); | |
156 | 201 | } |
157 | 202 | } |
158 | 203 | |
159 | 204 | static void |
160 | kore_parse_config_file(char *fpath) | |
205 | kore_parse_config_file(const char *fpath) | |
161 | 206 | { |
162 | 207 | FILE *fp; |
163 | 208 | int i, lineno; |
164 | char buf[BUFSIZ], *p, *t, *argv[5]; | |
165 | ||
209 | char buf[BUFSIZ], *p, *t; | |
210 | ||
211 | #if !defined(KORE_SINGLE_BINARY) | |
166 | 212 | if ((fp = fopen(fpath, "r")) == NULL) |
167 | 213 | fatal("configuration given cannot be opened: %s", fpath); |
214 | #else | |
215 | fp = config_file_write(); | |
216 | #endif | |
168 | 217 | |
169 | 218 | kore_debug("parsing configuration file '%s'", fpath); |
170 | 219 | |
171 | 220 | lineno = 1; |
172 | while (fgets(buf, sizeof(buf), fp) != NULL) { | |
173 | p = buf; | |
174 | buf[strcspn(buf, "\n")] = '\0'; | |
175 | ||
176 | while (isspace(*p)) | |
177 | p++; | |
178 | if (p[0] == '#' || p[0] == '\0') { | |
221 | while ((p = kore_read_line(fp, buf, sizeof(buf))) != NULL) { | |
222 | if (strlen(p) == 0) { | |
179 | 223 | lineno++; |
180 | 224 | continue; |
181 | 225 | } |
182 | 226 | |
183 | for (t = p; *t != '\0'; t++) { | |
184 | if (*t == '\t') | |
185 | *t = ' '; | |
186 | } | |
187 | ||
227 | #if !defined(KORE_NO_HTTP) | |
188 | 228 | if (!strcmp(p, "}") && current_handler != NULL) { |
189 | 229 | lineno++; |
190 | 230 | current_handler = NULL; |
201 | 241 | current_auth = NULL; |
202 | 242 | continue; |
203 | 243 | } |
244 | #endif | |
204 | 245 | |
205 | 246 | if (!strcmp(p, "}") && current_domain != NULL) |
206 | 247 | domain_sslstart(); |
210 | 251 | continue; |
211 | 252 | } |
212 | 253 | |
213 | kore_split_string(p, " ", argv, 5); | |
254 | if ((t = strchr(p, ' ')) == NULL) { | |
255 | printf("ignoring \"%s\" on line %d\n", p, lineno++); | |
256 | continue; | |
257 | } | |
258 | ||
259 | *(t)++ = '\0'; | |
260 | ||
261 | p = kore_text_trim(p, strlen(p)); | |
262 | t = kore_text_trim(t, strlen(t)); | |
263 | ||
264 | if (strlen(p) == 0 || strlen(t) == 0) { | |
265 | printf("ignoring \"%s\" on line %d\n", p, lineno++); | |
266 | continue; | |
267 | } | |
268 | ||
214 | 269 | for (i = 0; config_names[i].name != NULL; i++) { |
215 | if (!strcmp(config_names[i].name, argv[0])) { | |
216 | if (!config_names[i].configure(argv)) { | |
217 | fatal("configuration error on line %d", | |
218 | lineno); | |
219 | } | |
220 | break; | |
270 | if (!strcmp(config_names[i].name, p)) { | |
271 | if (config_names[i].configure(t)) | |
272 | break; | |
273 | fatal("configuration error on line %d", lineno); | |
274 | /* NOTREACHED */ | |
221 | 275 | } |
222 | 276 | } |
223 | 277 | |
224 | if (config_names[i].name == NULL) { | |
225 | printf("unknown configuration option \"%s\" on line %d\n", p, | |
226 | lineno); | |
278 | if (config_names[i].name == NULL) | |
279 | printf("ignoring \"%s\" on line %d\n", p, lineno); | |
280 | lineno++; | |
281 | } | |
282 | ||
283 | fclose(fp); | |
284 | } | |
285 | ||
286 | static int | |
287 | configure_include(char *path) | |
288 | { | |
289 | kore_parse_config_file(path); | |
290 | return (KORE_RESULT_OK); | |
291 | } | |
292 | ||
293 | static int | |
294 | configure_bind(char *options) | |
295 | { | |
296 | char *argv[4]; | |
297 | ||
298 | kore_split_string(options, " ", argv, 4); | |
299 | if (argv[0] == NULL || argv[1] == NULL) | |
300 | return (KORE_RESULT_ERROR); | |
301 | ||
302 | return (kore_server_bind(argv[0], argv[1], argv[2])); | |
303 | } | |
304 | ||
305 | #if !defined(KORE_SINGLE_BINARY) | |
306 | static int | |
307 | configure_load(char *options) | |
308 | { | |
309 | char *argv[3]; | |
310 | ||
311 | kore_split_string(options, " ", argv, 3); | |
312 | if (argv[0] == NULL) | |
313 | return (KORE_RESULT_ERROR); | |
314 | ||
315 | kore_module_load(argv[0], argv[1]); | |
316 | return (KORE_RESULT_OK); | |
317 | } | |
318 | #else | |
319 | static FILE * | |
320 | config_file_write(void) | |
321 | { | |
322 | FILE *fp; | |
323 | ssize_t ret; | |
324 | int fd, len; | |
325 | char fpath[MAXPATHLEN]; | |
326 | ||
327 | len = snprintf(fpath, sizeof(fpath), "/tmp/%s.XXXXXX", __progname); | |
328 | if (len == -1 || (size_t)len >= sizeof(fpath)) | |
329 | fatal("failed to create temporary path"); | |
330 | ||
331 | if ((fd = mkstemp(fpath)) == -1) | |
332 | fatal("mkstemp(%s): %s", fpath, errno_s); | |
333 | ||
334 | (void)unlink(fpath); | |
335 | ||
336 | for (;;) { | |
337 | ret = write(fd, asset_builtin_kore_conf, | |
338 | asset_len_builtin_kore_conf); | |
339 | if (ret == -1) { | |
340 | if (errno == EINTR) | |
341 | continue; | |
342 | fatal("failed to write temporary config: %s", errno_s); | |
227 | 343 | } |
228 | 344 | |
229 | lineno++; | |
230 | } | |
231 | ||
232 | fclose(fp); | |
233 | } | |
234 | ||
235 | static int | |
236 | configure_include(char **argv) | |
237 | { | |
238 | if (argv[1] == NULL) { | |
239 | printf("No file given in include directive\n"); | |
240 | return (KORE_RESULT_ERROR); | |
241 | } | |
242 | ||
243 | kore_parse_config_file(argv[1]); | |
244 | return (KORE_RESULT_OK); | |
245 | } | |
246 | ||
247 | static int | |
248 | configure_bind(char **argv) | |
249 | { | |
250 | if (argv[1] == NULL || argv[2] == NULL) | |
251 | return (KORE_RESULT_ERROR); | |
252 | ||
253 | return (kore_server_bind(argv[1], argv[2])); | |
254 | } | |
255 | ||
256 | static int | |
257 | configure_load(char **argv) | |
258 | { | |
259 | if (argv[1] == NULL) | |
260 | return (KORE_RESULT_ERROR); | |
261 | ||
262 | kore_module_load(argv[1], argv[2]); | |
263 | return (KORE_RESULT_OK); | |
264 | } | |
265 | ||
266 | static int | |
267 | configure_tls_version(char **argv) | |
268 | { | |
269 | if (argv[1] == NULL) | |
270 | return (KORE_RESULT_ERROR); | |
271 | ||
272 | if (!strcmp(argv[1], "1.2")) { | |
345 | if ((size_t)ret != asset_len_builtin_kore_conf) | |
346 | fatal("failed to write temporary config"); | |
347 | break; | |
348 | } | |
349 | ||
350 | if ((fp = fdopen(fd, "w+")) == NULL) | |
351 | fatal("fdopen(): %s", errno_s); | |
352 | ||
353 | rewind(fp); | |
354 | ||
355 | return (fp); | |
356 | } | |
357 | #endif | |
358 | ||
359 | #if !defined(KORE_NO_TLS) | |
360 | static int | |
361 | configure_tls_version(char *version) | |
362 | { | |
363 | if (!strcmp(version, "1.2")) { | |
273 | 364 | tls_version = KORE_TLS_VERSION_1_2; |
274 | } else if (!strcmp(argv[1], "1.0")) { | |
365 | } else if (!strcmp(version, "1.0")) { | |
275 | 366 | tls_version = KORE_TLS_VERSION_1_0; |
276 | } else if (!strcmp(argv[1], "both")) { | |
367 | } else if (!strcmp(version, "both")) { | |
277 | 368 | tls_version = KORE_TLS_VERSION_BOTH; |
278 | 369 | } else { |
279 | printf("unknown value for tls_version: %s\n", argv[1]); | |
280 | return (KORE_RESULT_ERROR); | |
281 | } | |
282 | ||
283 | return (KORE_RESULT_OK); | |
284 | } | |
285 | ||
286 | static int | |
287 | configure_tls_cipher(char **argv) | |
288 | { | |
289 | if (argv[1] == NULL) | |
290 | return (KORE_RESULT_ERROR); | |
291 | ||
370 | printf("unknown value for tls_version: %s\n", version); | |
371 | return (KORE_RESULT_ERROR); | |
372 | } | |
373 | ||
374 | return (KORE_RESULT_OK); | |
375 | } | |
376 | ||
377 | static int | |
378 | configure_tls_cipher(char *cipherlist) | |
379 | { | |
292 | 380 | if (strcmp(kore_tls_cipher_list, KORE_DEFAULT_CIPHER_LIST)) { |
293 | kore_debug("duplicate tls_cipher directive specified"); | |
294 | return (KORE_RESULT_ERROR); | |
295 | } | |
296 | ||
297 | kore_tls_cipher_list = kore_strdup(argv[1]); | |
298 | return (KORE_RESULT_OK); | |
299 | } | |
300 | ||
301 | static int | |
302 | configure_tls_dhparam(char **argv) | |
303 | { | |
304 | #if !defined(KORE_BENCHMARK) | |
381 | printf("tls_cipher specified twice\n"); | |
382 | return (KORE_RESULT_ERROR); | |
383 | } | |
384 | ||
385 | kore_tls_cipher_list = kore_strdup(cipherlist); | |
386 | return (KORE_RESULT_OK); | |
387 | } | |
388 | ||
389 | static int | |
390 | configure_tls_dhparam(char *path) | |
391 | { | |
305 | 392 | BIO *bio; |
306 | 393 | |
307 | if (argv[1] == NULL) | |
308 | return (KORE_RESULT_ERROR); | |
309 | ||
310 | 394 | if (tls_dhparam != NULL) { |
311 | kore_debug("duplicate tls_dhparam directive specified"); | |
312 | return (KORE_RESULT_ERROR); | |
313 | } | |
314 | ||
315 | if ((bio = BIO_new_file(argv[1], "r")) == NULL) { | |
316 | printf("%s did not exist\n", argv[1]); | |
395 | printf("tls_dhparam specified twice\n"); | |
396 | return (KORE_RESULT_ERROR); | |
397 | } | |
398 | ||
399 | if ((bio = BIO_new_file(path, "r")) == NULL) { | |
400 | printf("%s did not exist\n", path); | |
317 | 401 | return (KORE_RESULT_ERROR); |
318 | 402 | } |
319 | 403 | |
324 | 408 | printf("PEM_read_bio_DHparams(): %s\n", ssl_errno_s); |
325 | 409 | return (KORE_RESULT_ERROR); |
326 | 410 | } |
327 | #endif | |
328 | return (KORE_RESULT_OK); | |
329 | } | |
330 | ||
331 | static int | |
332 | configure_spdy_idle_time(char **argv) | |
333 | { | |
334 | int err; | |
335 | ||
336 | if (argv[1] == NULL) | |
337 | return (KORE_RESULT_ERROR); | |
338 | ||
339 | spdy_idle_time = kore_strtonum(argv[1], 10, 0, 65535, &err); | |
340 | if (err != KORE_RESULT_OK) { | |
341 | printf("spdy_idle_time has invalid value: %s\n", argv[1]); | |
342 | return (KORE_RESULT_ERROR); | |
343 | } | |
344 | ||
345 | spdy_idle_time = spdy_idle_time * 1000; | |
346 | return (KORE_RESULT_OK); | |
347 | } | |
348 | ||
349 | static int | |
350 | configure_domain(char **argv) | |
351 | { | |
352 | if (argv[2] == NULL) | |
353 | return (KORE_RESULT_ERROR); | |
411 | ||
412 | return (KORE_RESULT_OK); | |
413 | } | |
414 | ||
415 | static int | |
416 | configure_client_certificates(char *options) | |
417 | { | |
418 | char *argv[3]; | |
419 | ||
420 | if (current_domain == NULL) { | |
421 | printf("client_certificates not specified in domain context\n"); | |
422 | return (KORE_RESULT_ERROR); | |
423 | } | |
424 | ||
425 | kore_split_string(options, " ", argv, 3); | |
426 | if (argv[0] == NULL) { | |
427 | printf("client_certificate is missing a parameter\n"); | |
428 | return (KORE_RESULT_ERROR); | |
429 | } | |
430 | ||
431 | if (current_domain->cafile != NULL) { | |
432 | printf("client_certificate already set for %s\n", | |
433 | current_domain->domain); | |
434 | return (KORE_RESULT_ERROR); | |
435 | } | |
436 | ||
437 | current_domain->cafile = kore_strdup(argv[0]); | |
438 | if (argv[1] != NULL) | |
439 | current_domain->crlfile = kore_strdup(argv[1]); | |
440 | ||
441 | return (KORE_RESULT_OK); | |
442 | } | |
443 | ||
444 | static int | |
445 | configure_certfile(char *path) | |
446 | { | |
447 | if (current_domain == NULL) { | |
448 | printf("certfile not specified in domain context\n"); | |
449 | return (KORE_RESULT_ERROR); | |
450 | } | |
451 | ||
452 | if (current_domain->certfile != NULL) { | |
453 | printf("certfile specified twice for %s\n", | |
454 | current_domain->domain); | |
455 | return (KORE_RESULT_ERROR); | |
456 | } | |
457 | ||
458 | current_domain->certfile = kore_strdup(path); | |
459 | return (KORE_RESULT_OK); | |
460 | } | |
461 | ||
462 | static int | |
463 | configure_certkey(char *path) | |
464 | { | |
465 | if (current_domain == NULL) { | |
466 | printf("certkey not specified in domain text\n"); | |
467 | return (KORE_RESULT_ERROR); | |
468 | } | |
469 | ||
470 | if (current_domain->certkey != NULL) { | |
471 | printf("certkey specified twice for %s\n", | |
472 | current_domain->domain); | |
473 | return (KORE_RESULT_ERROR); | |
474 | } | |
475 | ||
476 | current_domain->certkey = kore_strdup(path); | |
477 | return (KORE_RESULT_OK); | |
478 | } | |
479 | ||
480 | #endif /* !KORE_NO_TLS */ | |
481 | ||
482 | static int | |
483 | configure_domain(char *options) | |
484 | { | |
485 | char *argv[3]; | |
354 | 486 | |
355 | 487 | if (current_domain != NULL) { |
356 | printf("previous domain configuration not closed\n"); | |
357 | return (KORE_RESULT_ERROR); | |
358 | } | |
359 | ||
360 | if (strcmp(argv[2], "{")) { | |
361 | printf("missing { for domain directive\n"); | |
362 | return (KORE_RESULT_ERROR); | |
363 | } | |
364 | ||
365 | if (!kore_domain_new(argv[1])) { | |
366 | printf("could not create new domain %s\n", argv[1]); | |
367 | return (KORE_RESULT_ERROR); | |
368 | } | |
369 | ||
370 | current_domain = kore_domain_lookup(argv[1]); | |
371 | return (KORE_RESULT_OK); | |
372 | } | |
373 | ||
374 | static int | |
375 | configure_handler(char **argv) | |
376 | { | |
377 | int type; | |
488 | printf("nested domain contexts are not allowed\n"); | |
489 | return (KORE_RESULT_ERROR); | |
490 | } | |
491 | ||
492 | kore_split_string(options, " ", argv, 3); | |
493 | ||
494 | if (strcmp(argv[1], "{")) { | |
495 | printf("domain context not opened correctly\n"); | |
496 | return (KORE_RESULT_ERROR); | |
497 | } | |
498 | ||
499 | if (strlen(argv[0]) >= KORE_DOMAINNAME_LEN - 1) { | |
500 | printf("domain name '%s' too long\n", argv[0]); | |
501 | return (KORE_RESULT_ERROR); | |
502 | } | |
503 | ||
504 | if (!kore_domain_new(argv[0])) { | |
505 | printf("could not create new domain %s\n", argv[0]); | |
506 | return (KORE_RESULT_ERROR); | |
507 | } | |
508 | ||
509 | current_domain = kore_domain_lookup(argv[0]); | |
510 | return (KORE_RESULT_OK); | |
511 | } | |
512 | ||
513 | #if !defined(KORE_NO_HTTP) | |
514 | static int | |
515 | configure_static_handler(char *options) | |
516 | { | |
517 | return (configure_handler(HANDLER_TYPE_STATIC, options)); | |
518 | } | |
519 | ||
520 | static int | |
521 | configure_dynamic_handler(char *options) | |
522 | { | |
523 | return (configure_handler(HANDLER_TYPE_DYNAMIC, options)); | |
524 | } | |
525 | ||
526 | static int | |
527 | configure_handler(int type, char *options) | |
528 | { | |
529 | char *argv[4]; | |
378 | 530 | |
379 | 531 | if (current_domain == NULL) { |
380 | printf("missing domain for page handler\n"); | |
381 | return (KORE_RESULT_ERROR); | |
382 | } | |
383 | ||
384 | if (argv[1] == NULL || argv[2] == NULL) | |
385 | return (KORE_RESULT_ERROR); | |
386 | ||
387 | if (!strcmp(argv[0], "static")) | |
388 | type = HANDLER_TYPE_STATIC; | |
389 | else if (!strcmp(argv[0], "dynamic")) | |
390 | type = HANDLER_TYPE_DYNAMIC; | |
391 | else | |
392 | return (KORE_RESULT_ERROR); | |
393 | ||
394 | if (!kore_module_handler_new(argv[1], | |
395 | current_domain->domain, argv[2], argv[3], type)) { | |
396 | kore_debug("cannot create handler for %s", argv[1]); | |
397 | return (KORE_RESULT_ERROR); | |
398 | } | |
399 | ||
400 | return (KORE_RESULT_OK); | |
401 | } | |
402 | ||
403 | static int | |
404 | configure_client_certificates(char **argv) | |
532 | printf("page handler not specified in domain context\n"); | |
533 | return (KORE_RESULT_ERROR); | |
534 | } | |
535 | ||
536 | kore_split_string(options, " ", argv, 4); | |
537 | ||
538 | if (argv[0] == NULL || argv[1] == NULL) { | |
539 | printf("missing parameters for page handler\n"); | |
540 | return (KORE_RESULT_ERROR); | |
541 | } | |
542 | ||
543 | if (!kore_module_handler_new(argv[0], | |
544 | current_domain->domain, argv[1], argv[2], type)) { | |
545 | printf("cannot create handler for %s\n", argv[0]); | |
546 | return (KORE_RESULT_ERROR); | |
547 | } | |
548 | ||
549 | return (KORE_RESULT_OK); | |
550 | } | |
551 | ||
552 | static int | |
553 | configure_accesslog(char *path) | |
405 | 554 | { |
406 | 555 | if (current_domain == NULL) { |
407 | printf("missing domain for require_client_cert\n"); | |
408 | return (KORE_RESULT_ERROR); | |
409 | } | |
410 | ||
411 | if (argv[1] == NULL) { | |
412 | printf("missing argument for require_client_cert\n"); | |
413 | return (KORE_RESULT_ERROR); | |
414 | } | |
415 | ||
416 | if (current_domain->cafile != NULL) { | |
417 | printf("require_client_cert already set for %s\n", | |
556 | kore_debug("accesslog not specified in domain context\n"); | |
557 | return (KORE_RESULT_ERROR); | |
558 | } | |
559 | ||
560 | if (current_domain->accesslog != -1) { | |
561 | printf("domain %s already has an open accesslog\n", | |
418 | 562 | current_domain->domain); |
419 | 563 | return (KORE_RESULT_ERROR); |
420 | 564 | } |
421 | 565 | |
422 | current_domain->cafile = kore_strdup(argv[1]); | |
423 | if (argv[2] != NULL) | |
424 | current_domain->crlfile = kore_strdup(argv[2]); | |
425 | ||
426 | return (KORE_RESULT_OK); | |
427 | } | |
428 | ||
429 | static int | |
430 | configure_chroot(char **argv) | |
431 | { | |
432 | if (chroot_path != NULL) { | |
433 | kore_debug("duplicate chroot path specified"); | |
434 | return (KORE_RESULT_ERROR); | |
435 | } | |
436 | ||
437 | if (argv[1] == NULL) | |
438 | return (KORE_RESULT_ERROR); | |
439 | ||
440 | chroot_path = kore_strdup(argv[1]); | |
441 | return (KORE_RESULT_OK); | |
442 | } | |
443 | ||
444 | static int | |
445 | configure_runas(char **argv) | |
446 | { | |
447 | if (runas_user != NULL) { | |
448 | kore_debug("duplicate runas user specified"); | |
449 | return (KORE_RESULT_ERROR); | |
450 | } | |
451 | ||
452 | if (argv[1] == NULL) | |
453 | return (KORE_RESULT_ERROR); | |
454 | ||
455 | runas_user = kore_strdup(argv[1]); | |
456 | return (KORE_RESULT_OK); | |
457 | } | |
458 | ||
459 | static int | |
460 | configure_workers(char **argv) | |
461 | { | |
462 | int err; | |
463 | ||
464 | if (worker_count != 0) { | |
465 | kore_debug("duplicate worker directive specified"); | |
466 | return (KORE_RESULT_ERROR); | |
467 | } | |
468 | ||
469 | if (argv[1] == NULL) | |
470 | return (KORE_RESULT_ERROR); | |
471 | ||
472 | worker_count = kore_strtonum(argv[1], 10, 1, 255, &err); | |
473 | if (err != KORE_RESULT_OK) { | |
474 | printf("%s is not a correct worker number\n", argv[1]); | |
475 | return (KORE_RESULT_ERROR); | |
476 | } | |
477 | ||
478 | return (KORE_RESULT_OK); | |
479 | } | |
480 | ||
481 | static int | |
482 | configure_pidfile(char **argv) | |
483 | { | |
484 | if (argv[1] == NULL) | |
485 | return (KORE_RESULT_ERROR); | |
486 | ||
487 | if (strcmp(kore_pidfile, KORE_PIDFILE_DEFAULT)) { | |
488 | kore_debug("duplicate pidfile directive specified"); | |
489 | return (KORE_RESULT_ERROR); | |
490 | } | |
491 | ||
492 | kore_pidfile = kore_strdup(argv[1]); | |
493 | return (KORE_RESULT_OK); | |
494 | } | |
495 | ||
496 | static int | |
497 | configure_accesslog(char **argv) | |
498 | { | |
499 | if (argv[1] == NULL) | |
500 | return (KORE_RESULT_ERROR); | |
501 | ||
502 | if (current_domain == NULL) { | |
503 | kore_debug("missing domain for accesslog"); | |
504 | return (KORE_RESULT_ERROR); | |
505 | } | |
506 | ||
507 | if (current_domain->accesslog != -1) { | |
508 | kore_debug("domain %s already has an open accesslog", | |
509 | current_domain->domain); | |
510 | return (KORE_RESULT_ERROR); | |
511 | } | |
512 | ||
513 | current_domain->accesslog = open(argv[1], | |
566 | current_domain->accesslog = open(path, | |
514 | 567 | O_CREAT | O_APPEND | O_WRONLY, |
515 | 568 | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); |
516 | 569 | if (current_domain->accesslog == -1) { |
517 | kore_debug("open(%s): %s", argv[1], errno_s); | |
518 | return (KORE_RESULT_ERROR); | |
519 | } | |
520 | ||
521 | return (KORE_RESULT_OK); | |
522 | } | |
523 | ||
524 | static int | |
525 | configure_certfile(char **argv) | |
526 | { | |
527 | if (argv[1] == NULL) | |
528 | return (KORE_RESULT_ERROR); | |
529 | ||
530 | if (current_domain == NULL) { | |
531 | printf("missing domain for certfile\n"); | |
532 | return (KORE_RESULT_ERROR); | |
533 | } | |
534 | ||
535 | if (current_domain->certfile != NULL) { | |
536 | kore_debug("domain already has a certfile set"); | |
537 | return (KORE_RESULT_ERROR); | |
538 | } | |
539 | ||
540 | current_domain->certfile = kore_strdup(argv[1]); | |
541 | return (KORE_RESULT_OK); | |
542 | } | |
543 | ||
544 | static int | |
545 | configure_certkey(char **argv) | |
546 | { | |
547 | if (argv[1] == NULL) | |
548 | return (KORE_RESULT_ERROR); | |
549 | ||
550 | if (current_domain == NULL) { | |
551 | printf("missing domain for certkey\n"); | |
552 | return (KORE_RESULT_ERROR); | |
553 | } | |
554 | ||
555 | if (current_domain->certkey != NULL) { | |
556 | kore_debug("domain already has a certkey set"); | |
557 | return (KORE_RESULT_ERROR); | |
558 | } | |
559 | ||
560 | current_domain->certkey = kore_strdup(argv[1]); | |
561 | return (KORE_RESULT_OK); | |
562 | } | |
563 | ||
564 | static int | |
565 | configure_max_connections(char **argv) | |
566 | { | |
567 | int err; | |
568 | ||
569 | if (argv[1] == NULL) | |
570 | return (KORE_RESULT_ERROR); | |
571 | ||
572 | worker_max_connections = kore_strtonum(argv[1], 10, 1, UINT_MAX, &err); | |
573 | if (err != KORE_RESULT_OK) { | |
574 | printf("bad value for worker_max_connections: %s\n", argv[1]); | |
575 | return (KORE_RESULT_ERROR); | |
576 | } | |
577 | ||
578 | return (KORE_RESULT_OK); | |
579 | } | |
580 | ||
581 | static int | |
582 | configure_rlimit_nofiles(char **argv) | |
583 | { | |
584 | int err; | |
585 | ||
586 | if (argv[1] == NULL) | |
587 | return (KORE_RESULT_ERROR); | |
588 | ||
589 | worker_rlimit_nofiles = kore_strtonum(argv[1], 10, 1, UINT_MAX, &err); | |
590 | if (err != KORE_RESULT_OK) { | |
591 | printf("bad value for worker_rlimit_nofiles: %s\n", argv[1]); | |
592 | return (KORE_RESULT_ERROR); | |
593 | } | |
594 | ||
595 | return (KORE_RESULT_OK); | |
596 | } | |
597 | ||
598 | static int | |
599 | configure_accept_threshold(char **argv) | |
600 | { | |
601 | int err; | |
602 | ||
603 | if (argv[1] == NULL) | |
604 | return (KORE_RESULT_ERROR); | |
605 | ||
606 | worker_accept_threshold = kore_strtonum(argv[1], 0, 1, UINT_MAX, &err); | |
607 | if (err != KORE_RESULT_OK) { | |
608 | printf("bad value for worker_accept_threshold: %s\n", argv[1]); | |
609 | return (KORE_RESULT_ERROR); | |
610 | } | |
611 | ||
612 | return (KORE_RESULT_OK); | |
613 | } | |
614 | ||
615 | static int | |
616 | configure_set_affinity(char **argv) | |
617 | { | |
618 | int err; | |
619 | ||
620 | if (argv[1] == NULL) | |
621 | return (KORE_RESULT_ERROR); | |
622 | ||
623 | worker_set_affinity = kore_strtonum(argv[1], 10, 0, 1, &err); | |
624 | if (err != KORE_RESULT_OK) { | |
625 | printf("bad value for worker_set_affinity: %s\n", argv[1]); | |
626 | return (KORE_RESULT_ERROR); | |
627 | } | |
628 | ||
629 | return (KORE_RESULT_OK); | |
630 | } | |
631 | ||
632 | static int | |
633 | configure_http_header_max(char **argv) | |
634 | { | |
635 | int err; | |
636 | ||
637 | if (argv[1] == NULL) | |
638 | return (KORE_RESULT_ERROR); | |
639 | ||
640 | if (http_header_max != HTTP_HEADER_MAX_LEN) { | |
641 | kore_debug("http_header_max already set"); | |
642 | return (KORE_RESULT_ERROR); | |
643 | } | |
644 | ||
645 | http_header_max = kore_strtonum(argv[1], 10, 1, 65535, &err); | |
646 | if (err != KORE_RESULT_OK) { | |
647 | printf("bad http_header_max value: %s\n", argv[1]); | |
648 | return (KORE_RESULT_ERROR); | |
649 | } | |
650 | ||
651 | return (KORE_RESULT_OK); | |
652 | } | |
653 | ||
654 | static int | |
655 | configure_http_body_max(char **argv) | |
656 | { | |
657 | int err; | |
658 | ||
659 | if (argv[1] == NULL) | |
660 | return (KORE_RESULT_ERROR); | |
661 | ||
662 | if (http_body_max != HTTP_BODY_MAX_LEN) { | |
663 | kore_debug("http_body_max already set"); | |
664 | return (KORE_RESULT_ERROR); | |
665 | } | |
666 | ||
667 | http_body_max = kore_strtonum(argv[1], 10, 1, LONG_MAX, &err); | |
668 | if (err != KORE_RESULT_OK) { | |
669 | printf("bad http_body_max value: %s\n", argv[1]); | |
670 | return (KORE_RESULT_ERROR); | |
671 | } | |
672 | ||
673 | return (KORE_RESULT_OK); | |
674 | } | |
675 | ||
676 | static int | |
677 | configure_http_hsts_enable(char **argv) | |
678 | { | |
679 | int err; | |
680 | ||
681 | if (argv[1] == NULL) | |
682 | return (KORE_RESULT_ERROR); | |
683 | ||
684 | if (http_hsts_enable != HTTP_HSTS_ENABLE) { | |
685 | kore_debug("http_hsts_enable already set"); | |
686 | return (KORE_RESULT_ERROR); | |
687 | } | |
688 | ||
689 | http_hsts_enable = kore_strtonum(argv[1], 10, 0, LONG_MAX, &err); | |
690 | if (err != KORE_RESULT_OK) { | |
691 | printf("bad http_hsts_enable value: %s\n", argv[1]); | |
692 | return (KORE_RESULT_ERROR); | |
693 | } | |
694 | ||
695 | return (KORE_RESULT_OK); | |
696 | } | |
697 | ||
698 | static int | |
699 | configure_http_keepalive_time(char **argv) | |
700 | { | |
701 | int err; | |
702 | ||
703 | if (argv[1] == NULL) | |
704 | return (KORE_RESULT_ERROR); | |
705 | ||
706 | if (http_keepalive_time != HTTP_KEEPALIVE_TIME) { | |
707 | kore_debug("http_keepalive_time already set"); | |
708 | return (KORE_RESULT_ERROR); | |
709 | } | |
710 | ||
711 | http_keepalive_time = kore_strtonum(argv[1], 10, 0, USHRT_MAX, &err); | |
712 | if (err != KORE_RESULT_OK) { | |
713 | printf("bad http_keepalive_time value: %s\n", argv[1]); | |
714 | return (KORE_RESULT_ERROR); | |
715 | } | |
716 | ||
717 | return (KORE_RESULT_OK); | |
718 | } | |
719 | ||
720 | static int | |
721 | configure_http_request_limit(char **argv) | |
722 | { | |
723 | int err; | |
724 | ||
725 | if (argv[1] == NULL) | |
726 | return (KORE_RESULT_ERROR); | |
727 | ||
728 | http_request_limit = kore_strtonum(argv[1], 10, 0, UINT_MAX, &err); | |
729 | if (err != KORE_RESULT_OK) { | |
730 | printf("bad http_request_limit value: %s\n", argv[1]); | |
731 | return (KORE_RESULT_ERROR); | |
732 | } | |
733 | ||
734 | return (KORE_RESULT_OK); | |
735 | } | |
736 | ||
737 | static int | |
738 | configure_validator(char **argv) | |
570 | printf("accesslog open(%s): %s\n", path, errno_s); | |
571 | return (KORE_RESULT_ERROR); | |
572 | } | |
573 | ||
574 | return (KORE_RESULT_OK); | |
575 | } | |
576 | ||
577 | static int | |
578 | configure_http_header_max(char *option) | |
579 | { | |
580 | int err; | |
581 | ||
582 | http_header_max = kore_strtonum(option, 10, 1, 65535, &err); | |
583 | if (err != KORE_RESULT_OK) { | |
584 | printf("bad http_header_max value: %s\n", option); | |
585 | return (KORE_RESULT_ERROR); | |
586 | } | |
587 | ||
588 | return (KORE_RESULT_OK); | |
589 | } | |
590 | ||
591 | static int | |
592 | configure_http_body_max(char *option) | |
593 | { | |
594 | int err; | |
595 | ||
596 | http_body_max = kore_strtonum(option, 10, 0, LONG_MAX, &err); | |
597 | if (err != KORE_RESULT_OK) { | |
598 | printf("bad http_body_max value: %s\n", option); | |
599 | return (KORE_RESULT_ERROR); | |
600 | } | |
601 | ||
602 | return (KORE_RESULT_OK); | |
603 | } | |
604 | ||
605 | static int | |
606 | configure_http_body_disk_offload(char *option) | |
607 | { | |
608 | int err; | |
609 | ||
610 | http_body_disk_offload = kore_strtonum(option, 10, 0, LONG_MAX, &err); | |
611 | if (err != KORE_RESULT_OK) { | |
612 | printf("bad http_body_disk_offload value: %s\n", option); | |
613 | return (KORE_RESULT_ERROR); | |
614 | } | |
615 | ||
616 | return (KORE_RESULT_OK); | |
617 | } | |
618 | ||
619 | static int | |
620 | configure_http_body_disk_path(char *path) | |
621 | { | |
622 | if (strcmp(http_body_disk_path, HTTP_BODY_DISK_PATH)) | |
623 | kore_free(http_body_disk_path); | |
624 | ||
625 | http_body_disk_path = kore_strdup(path); | |
626 | return (KORE_RESULT_OK); | |
627 | } | |
628 | ||
629 | static int | |
630 | configure_http_hsts_enable(char *option) | |
631 | { | |
632 | int err; | |
633 | ||
634 | http_hsts_enable = kore_strtonum(option, 10, 0, LONG_MAX, &err); | |
635 | if (err != KORE_RESULT_OK) { | |
636 | printf("bad http_hsts_enable value: %s\n", option); | |
637 | return (KORE_RESULT_ERROR); | |
638 | } | |
639 | ||
640 | return (KORE_RESULT_OK); | |
641 | } | |
642 | ||
643 | static int | |
644 | configure_http_keepalive_time(char *option) | |
645 | { | |
646 | int err; | |
647 | ||
648 | http_keepalive_time = kore_strtonum(option, 10, 0, USHRT_MAX, &err); | |
649 | if (err != KORE_RESULT_OK) { | |
650 | printf("bad http_keepalive_time value: %s\n", option); | |
651 | return (KORE_RESULT_ERROR); | |
652 | } | |
653 | ||
654 | return (KORE_RESULT_OK); | |
655 | } | |
656 | ||
657 | static int | |
658 | configure_http_request_limit(char *option) | |
659 | { | |
660 | int err; | |
661 | ||
662 | http_request_limit = kore_strtonum(option, 10, 0, UINT_MAX, &err); | |
663 | if (err != KORE_RESULT_OK) { | |
664 | printf("bad http_request_limit value: %s\n", option); | |
665 | return (KORE_RESULT_ERROR); | |
666 | } | |
667 | ||
668 | return (KORE_RESULT_OK); | |
669 | } | |
670 | ||
671 | static int | |
672 | configure_validator(char *name) | |
739 | 673 | { |
740 | 674 | u_int8_t type; |
741 | ||
742 | if (argv[3] == NULL) | |
743 | return (KORE_RESULT_ERROR); | |
744 | ||
745 | if (!strcmp(argv[2], "regex")) { | |
675 | char *tname, *value; | |
676 | ||
677 | if ((tname = strchr(name, ' ')) == NULL) { | |
678 | printf("missing validator name\n"); | |
679 | return (KORE_RESULT_ERROR); | |
680 | } | |
681 | ||
682 | *(tname)++ = '\0'; | |
683 | tname = kore_text_trim(tname, strlen(tname)); | |
684 | if ((value = strchr(tname, ' ')) == NULL) { | |
685 | printf("missing validator value\n"); | |
686 | return (KORE_RESULT_ERROR); | |
687 | } | |
688 | ||
689 | *(value)++ = '\0'; | |
690 | value = kore_text_trim(value, strlen(value)); | |
691 | ||
692 | if (!strcmp(tname, "regex")) { | |
746 | 693 | type = KORE_VALIDATOR_TYPE_REGEX; |
747 | } else if (!strcmp(argv[2], "function")) { | |
694 | } else if (!strcmp(tname, "function")) { | |
748 | 695 | type = KORE_VALIDATOR_TYPE_FUNCTION; |
749 | 696 | } else { |
750 | printf("bad type for validator %s\n", argv[1]); | |
751 | return (KORE_RESULT_ERROR); | |
752 | } | |
753 | ||
754 | if (!kore_validator_add(argv[1], type, argv[3])) { | |
755 | printf("bad validator specified: %s\n", argv[1]); | |
756 | return (KORE_RESULT_ERROR); | |
757 | } | |
758 | ||
759 | return (KORE_RESULT_OK); | |
760 | } | |
761 | ||
762 | static int | |
763 | configure_params(char **argv) | |
697 | printf("bad type for validator %s\n", tname); | |
698 | return (KORE_RESULT_ERROR); | |
699 | } | |
700 | ||
701 | if (!kore_validator_add(name, type, value)) { | |
702 | printf("bad validator specified: %s\n", tname); | |
703 | return (KORE_RESULT_ERROR); | |
704 | } | |
705 | ||
706 | return (KORE_RESULT_OK); | |
707 | } | |
708 | ||
709 | static int | |
710 | configure_params(char *options) | |
764 | 711 | { |
765 | 712 | struct kore_module_handle *hdlr; |
713 | char *argv[3]; | |
766 | 714 | |
767 | 715 | if (current_domain == NULL) { |
768 | printf("params keyword used in wrong context\n"); | |
716 | printf("params not used in domain context\n"); | |
769 | 717 | return (KORE_RESULT_ERROR); |
770 | 718 | } |
771 | 719 | |
774 | 722 | return (KORE_RESULT_ERROR); |
775 | 723 | } |
776 | 724 | |
777 | if (argv[2] == NULL) | |
778 | return (KORE_RESULT_ERROR); | |
779 | ||
780 | if (!strcasecmp(argv[1], "post")) { | |
725 | kore_split_string(options, " ", argv, 3); | |
726 | if (argv[1] == NULL) | |
727 | return (KORE_RESULT_ERROR); | |
728 | ||
729 | if (!strcasecmp(argv[0], "post")) { | |
781 | 730 | current_method = HTTP_METHOD_POST; |
782 | } else if (!strcasecmp(argv[1], "get")) { | |
731 | } else if (!strcasecmp(argv[0], "get")) { | |
783 | 732 | current_method = HTTP_METHOD_GET; |
784 | } else if (!strcasecmp(argv[1], "put")) { | |
733 | } else if (!strcasecmp(argv[0], "put")) { | |
785 | 734 | current_method = HTTP_METHOD_PUT; |
786 | } else if (!strcasecmp(argv[1], "delete")) { | |
735 | } else if (!strcasecmp(argv[0], "delete")) { | |
787 | 736 | current_method = HTTP_METHOD_DELETE; |
788 | } else if (!strcasecmp(argv[1], "head")) { | |
737 | } else if (!strcasecmp(argv[0], "head")) { | |
789 | 738 | current_method = HTTP_METHOD_HEAD; |
790 | 739 | } else { |
791 | 740 | printf("unknown method: %s in params block for %s\n", |
792 | argv[1], argv[2]); | |
741 | argv[0], argv[1]); | |
793 | 742 | return (KORE_RESULT_ERROR); |
794 | 743 | } |
795 | 744 | |
798 | 747 | * in case of a dynamic page. |
799 | 748 | */ |
800 | 749 | TAILQ_FOREACH(hdlr, &(current_domain->handlers), list) { |
801 | if (!strcmp(hdlr->path, argv[2])) { | |
750 | if (!strcmp(hdlr->path, argv[1])) { | |
802 | 751 | current_handler = hdlr; |
803 | 752 | return (KORE_RESULT_OK); |
804 | 753 | } |
805 | 754 | } |
806 | 755 | |
807 | printf("params for unknown page handler: %s\n", argv[2]); | |
756 | printf("params for unknown page handler: %s\n", argv[1]); | |
808 | 757 | return (KORE_RESULT_ERROR); |
809 | 758 | } |
810 | 759 | |
811 | 760 | static int |
812 | configure_validate(char **argv) | |
761 | configure_validate(char *options) | |
813 | 762 | { |
814 | 763 | struct kore_handler_params *p; |
815 | 764 | struct kore_validator *val; |
765 | char *argv[3]; | |
816 | 766 | |
817 | 767 | if (current_handler == NULL) { |
818 | printf("validate keyword used in wrong context\n"); | |
819 | return (KORE_RESULT_ERROR); | |
820 | } | |
821 | ||
822 | if (argv[2] == NULL) | |
823 | return (KORE_RESULT_ERROR); | |
824 | ||
825 | if ((val = kore_validator_lookup(argv[2])) == NULL) { | |
826 | printf("unknown validator %s for %s\n", argv[2], argv[1]); | |
768 | printf("validate not used in domain context\n"); | |
769 | return (KORE_RESULT_ERROR); | |
770 | } | |
771 | ||
772 | kore_split_string(options, " ", argv, 3); | |
773 | if (argv[1] == NULL) | |
774 | return (KORE_RESULT_ERROR); | |
775 | ||
776 | if ((val = kore_validator_lookup(argv[1])) == NULL) { | |
777 | printf("unknown validator %s for %s\n", argv[1], argv[0]); | |
827 | 778 | return (KORE_RESULT_ERROR); |
828 | 779 | } |
829 | 780 | |
830 | 781 | p = kore_malloc(sizeof(*p)); |
831 | 782 | p->validator = val; |
832 | 783 | p->method = current_method; |
833 | p->name = kore_strdup(argv[1]); | |
784 | p->name = kore_strdup(argv[0]); | |
834 | 785 | |
835 | 786 | TAILQ_INSERT_TAIL(&(current_handler->params), p, list); |
836 | ||
837 | return (KORE_RESULT_OK); | |
838 | } | |
839 | ||
840 | static int | |
841 | configure_authentication(char **argv) | |
842 | { | |
843 | if (argv[2] == NULL) { | |
844 | printf("Missing name for authentication block\n"); | |
845 | return (KORE_RESULT_ERROR); | |
846 | } | |
787 | return (KORE_RESULT_OK); | |
788 | } | |
789 | ||
790 | static int | |
791 | configure_authentication(char *options) | |
792 | { | |
793 | char *argv[3]; | |
847 | 794 | |
848 | 795 | if (current_auth != NULL) { |
849 | printf("Previous authentication block not closed\n"); | |
850 | return (KORE_RESULT_ERROR); | |
851 | } | |
852 | ||
853 | if (strcmp(argv[2], "{")) { | |
796 | printf("previous authentication block not closed\n"); | |
797 | return (KORE_RESULT_ERROR); | |
798 | } | |
799 | ||
800 | kore_split_string(options, " ", argv, 3); | |
801 | if (argv[1] == NULL) { | |
802 | printf("missing name for authentication block\n"); | |
803 | return (KORE_RESULT_ERROR); | |
804 | } | |
805 | ||
806 | if (strcmp(argv[1], "{")) { | |
854 | 807 | printf("missing { for authentication block\n"); |
855 | 808 | return (KORE_RESULT_ERROR); |
856 | 809 | } |
857 | 810 | |
858 | if (!kore_auth_new(argv[1])) | |
859 | return (KORE_RESULT_ERROR); | |
860 | ||
861 | current_auth = kore_auth_lookup(argv[1]); | |
862 | ||
863 | return (KORE_RESULT_OK); | |
864 | } | |
865 | ||
866 | static int | |
867 | configure_authentication_type(char **argv) | |
811 | if (!kore_auth_new(argv[0])) | |
812 | return (KORE_RESULT_ERROR); | |
813 | ||
814 | current_auth = kore_auth_lookup(argv[0]); | |
815 | ||
816 | return (KORE_RESULT_OK); | |
817 | } | |
818 | ||
819 | static int | |
820 | configure_authentication_type(char *option) | |
868 | 821 | { |
869 | 822 | if (current_auth == NULL) { |
870 | printf("authentication_type outside authentication block\n"); | |
871 | return (KORE_RESULT_ERROR); | |
872 | } | |
873 | ||
874 | if (argv[1] == NULL) { | |
875 | printf("missing parameter for authentication_type\n"); | |
876 | return (KORE_RESULT_ERROR); | |
877 | } | |
878 | ||
879 | if (!strcmp(argv[1], "cookie")) { | |
823 | printf("authentication_type outside authentication context\n"); | |
824 | return (KORE_RESULT_ERROR); | |
825 | } | |
826 | ||
827 | if (!strcmp(option, "cookie")) { | |
880 | 828 | current_auth->type = KORE_AUTH_TYPE_COOKIE; |
881 | } else if (!strcmp(argv[1], "header")) { | |
829 | } else if (!strcmp(option, "header")) { | |
882 | 830 | current_auth->type = KORE_AUTH_TYPE_HEADER; |
883 | } else if (!strcmp(argv[1], "request")) { | |
831 | } else if (!strcmp(option, "request")) { | |
884 | 832 | current_auth->type = KORE_AUTH_TYPE_REQUEST; |
885 | 833 | } else { |
886 | printf("unknown authentication type '%s'\n", argv[1]); | |
887 | return (KORE_RESULT_ERROR); | |
888 | } | |
889 | ||
890 | return (KORE_RESULT_OK); | |
891 | } | |
892 | ||
893 | static int | |
894 | configure_authentication_value(char **argv) | |
834 | printf("unknown authentication type '%s'\n", option); | |
835 | return (KORE_RESULT_ERROR); | |
836 | } | |
837 | ||
838 | return (KORE_RESULT_OK); | |
839 | } | |
840 | ||
841 | static int | |
842 | configure_authentication_value(char *option) | |
895 | 843 | { |
896 | 844 | if (current_auth == NULL) { |
897 | printf("authentication_value outside authentication block\n"); | |
898 | return (KORE_RESULT_ERROR); | |
899 | } | |
900 | ||
901 | if (argv[1] == NULL) { | |
902 | printf("missing parameter for authentication_value\n"); | |
903 | return (KORE_RESULT_ERROR); | |
904 | } | |
905 | ||
906 | if (current_auth->value != NULL) { | |
907 | printf("duplicate authentication_value found\n"); | |
908 | return (KORE_RESULT_ERROR); | |
909 | } | |
910 | ||
911 | current_auth->value = kore_strdup(argv[1]); | |
912 | return (KORE_RESULT_OK); | |
913 | } | |
914 | ||
915 | static int | |
916 | configure_authentication_validator(char **argv) | |
845 | printf("authentication_value outside authentication context\n"); | |
846 | return (KORE_RESULT_ERROR); | |
847 | } | |
848 | ||
849 | if (current_auth->value != NULL) | |
850 | kore_free(current_auth->value); | |
851 | current_auth->value = kore_strdup(option); | |
852 | ||
853 | return (KORE_RESULT_OK); | |
854 | } | |
855 | ||
856 | static int | |
857 | configure_authentication_validator(char *validator) | |
917 | 858 | { |
918 | 859 | struct kore_validator *val; |
919 | 860 | |
922 | 863 | return (KORE_RESULT_ERROR); |
923 | 864 | } |
924 | 865 | |
925 | if (argv[1] == NULL) { | |
926 | printf("missing parameter for authentication_validator\n"); | |
927 | return (KORE_RESULT_ERROR); | |
928 | } | |
929 | ||
930 | if (current_auth->validator != NULL) { | |
931 | printf("duplicate authentication_validator found\n"); | |
932 | return (KORE_RESULT_ERROR); | |
933 | } | |
934 | ||
935 | if ((val = kore_validator_lookup(argv[1])) == NULL) { | |
936 | printf("authentication validator '%s' not found\n", argv[1]); | |
866 | if ((val = kore_validator_lookup(validator)) == NULL) { | |
867 | printf("authentication validator '%s' not found\n", validator); | |
937 | 868 | return (KORE_RESULT_ERROR); |
938 | 869 | } |
939 | 870 | |
943 | 874 | } |
944 | 875 | |
945 | 876 | static int |
946 | configure_authentication_uri(char **argv) | |
877 | configure_authentication_uri(char *uri) | |
947 | 878 | { |
948 | 879 | if (current_auth == NULL) { |
949 | printf("authentication_uri outside authentication block\n"); | |
950 | return (KORE_RESULT_ERROR); | |
951 | } | |
952 | ||
953 | if (argv[1] == NULL) { | |
954 | printf("missing parameter for authentication_uri\n"); | |
955 | return (KORE_RESULT_ERROR); | |
956 | } | |
957 | ||
958 | if (current_auth->redirect != NULL) { | |
959 | printf("duplicate authentication_uri found\n"); | |
960 | return (KORE_RESULT_ERROR); | |
961 | } | |
962 | ||
963 | current_auth->redirect = kore_strdup(argv[1]); | |
964 | ||
965 | return (KORE_RESULT_OK); | |
966 | } | |
967 | ||
968 | static int | |
969 | configure_websocket_maxframe(char **argv) | |
880 | printf("authentication_uri outside authentication context\n"); | |
881 | return (KORE_RESULT_ERROR); | |
882 | } | |
883 | ||
884 | if (current_auth->redirect != NULL) | |
885 | kore_free(current_auth->redirect); | |
886 | current_auth->redirect = kore_strdup(uri); | |
887 | ||
888 | return (KORE_RESULT_OK); | |
889 | } | |
890 | ||
891 | static int | |
892 | configure_websocket_maxframe(char *option) | |
970 | 893 | { |
971 | 894 | int err; |
972 | 895 | |
973 | if (argv[1] == NULL) { | |
974 | printf("missing parameter for kore_websocket_maxframe\n"); | |
975 | return (KORE_RESULT_ERROR); | |
976 | } | |
977 | ||
978 | kore_websocket_maxframe = kore_strtonum64(argv[1], 1, &err); | |
979 | if (err != KORE_RESULT_OK) { | |
980 | printf("bad kore_websocket_maxframe value\n"); | |
981 | return (KORE_RESULT_ERROR); | |
982 | } | |
983 | ||
984 | return (KORE_RESULT_OK); | |
985 | } | |
986 | ||
987 | static int | |
988 | configure_websocket_timeout(char **argv) | |
896 | kore_websocket_maxframe = kore_strtonum64(option, 1, &err); | |
897 | if (err != KORE_RESULT_OK) { | |
898 | printf("bad kore_websocket_maxframe value: %s\n", option); | |
899 | return (KORE_RESULT_ERROR); | |
900 | } | |
901 | ||
902 | return (KORE_RESULT_OK); | |
903 | } | |
904 | ||
905 | static int | |
906 | configure_websocket_timeout(char *option) | |
989 | 907 | { |
990 | 908 | int err; |
991 | 909 | |
992 | if (argv[1] == NULL) { | |
993 | printf("missing parameter for kore_websocket_timeout\n"); | |
994 | return (KORE_RESULT_ERROR); | |
995 | } | |
996 | ||
997 | kore_websocket_timeout = kore_strtonum64(argv[1], 1, &err); | |
998 | if (err != KORE_RESULT_OK) { | |
999 | printf("bad kore_websocket_timeout value\n"); | |
910 | kore_websocket_timeout = kore_strtonum64(option, 1, &err); | |
911 | if (err != KORE_RESULT_OK) { | |
912 | printf("bad kore_websocket_timeout value: %s\n", option); | |
1000 | 913 | return (KORE_RESULT_ERROR); |
1001 | 914 | } |
1002 | 915 | |
1005 | 918 | return (KORE_RESULT_OK); |
1006 | 919 | } |
1007 | 920 | |
1008 | static int | |
1009 | configure_socket_backlog(char **argv) | |
1010 | { | |
1011 | int err; | |
1012 | ||
1013 | if (argv[1] == NULL) | |
1014 | return (KORE_RESULT_ERROR); | |
1015 | ||
1016 | kore_socket_backlog = kore_strtonum(argv[1], 10, 0, UINT_MAX, &err); | |
1017 | if (err != KORE_RESULT_OK) { | |
1018 | printf("bad socket_backlog value: %s\n", argv[1]); | |
921 | #endif /* !KORE_NO_HTTP */ | |
922 | ||
923 | static int | |
924 | configure_chroot(char *path) | |
925 | { | |
926 | if (chroot_path != NULL) | |
927 | kore_free(chroot_path); | |
928 | chroot_path = kore_strdup(path); | |
929 | ||
930 | return (KORE_RESULT_OK); | |
931 | } | |
932 | ||
933 | static int | |
934 | configure_runas(char *user) | |
935 | { | |
936 | if (runas_user != NULL) | |
937 | kore_free(runas_user); | |
938 | runas_user = kore_strdup(user); | |
939 | ||
940 | return (KORE_RESULT_OK); | |
941 | } | |
942 | ||
943 | static int | |
944 | configure_workers(char *option) | |
945 | { | |
946 | int err; | |
947 | ||
948 | worker_count = kore_strtonum(option, 10, 1, 255, &err); | |
949 | if (err != KORE_RESULT_OK) { | |
950 | printf("%s is not a valid worker number\n", option); | |
951 | return (KORE_RESULT_ERROR); | |
952 | } | |
953 | ||
954 | return (KORE_RESULT_OK); | |
955 | } | |
956 | ||
957 | static int | |
958 | configure_pidfile(char *path) | |
959 | { | |
960 | if (strcmp(kore_pidfile, KORE_PIDFILE_DEFAULT)) | |
961 | kore_free(kore_pidfile); | |
962 | kore_pidfile = kore_strdup(path); | |
963 | ||
964 | return (KORE_RESULT_OK); | |
965 | } | |
966 | ||
967 | static int | |
968 | configure_max_connections(char *option) | |
969 | { | |
970 | int err; | |
971 | ||
972 | worker_max_connections = kore_strtonum(option, 10, 1, UINT_MAX, &err); | |
973 | if (err != KORE_RESULT_OK) { | |
974 | printf("bad value for worker_max_connections: %s\n", option); | |
975 | return (KORE_RESULT_ERROR); | |
976 | } | |
977 | ||
978 | return (KORE_RESULT_OK); | |
979 | } | |
980 | ||
981 | static int | |
982 | configure_rlimit_nofiles(char *option) | |
983 | { | |
984 | int err; | |
985 | ||
986 | worker_rlimit_nofiles = kore_strtonum(option, 10, 1, UINT_MAX, &err); | |
987 | if (err != KORE_RESULT_OK) { | |
988 | printf("bad value for worker_rlimit_nofiles: %s\n", option); | |
989 | return (KORE_RESULT_ERROR); | |
990 | } | |
991 | ||
992 | return (KORE_RESULT_OK); | |
993 | } | |
994 | ||
995 | static int | |
996 | configure_accept_threshold(char *option) | |
997 | { | |
998 | int err; | |
999 | ||
1000 | worker_accept_threshold = kore_strtonum(option, 0, 1, UINT_MAX, &err); | |
1001 | if (err != KORE_RESULT_OK) { | |
1002 | printf("bad value for worker_accept_threshold: %s\n", option); | |
1003 | return (KORE_RESULT_ERROR); | |
1004 | } | |
1005 | ||
1006 | return (KORE_RESULT_OK); | |
1007 | } | |
1008 | ||
1009 | static int | |
1010 | configure_set_affinity(char *option) | |
1011 | { | |
1012 | int err; | |
1013 | ||
1014 | worker_set_affinity = kore_strtonum(option, 10, 0, 1, &err); | |
1015 | if (err != KORE_RESULT_OK) { | |
1016 | printf("bad value for worker_set_affinity: %s\n", option); | |
1017 | return (KORE_RESULT_ERROR); | |
1018 | } | |
1019 | ||
1020 | return (KORE_RESULT_OK); | |
1021 | } | |
1022 | ||
1023 | static int | |
1024 | configure_socket_backlog(char *option) | |
1025 | { | |
1026 | int err; | |
1027 | ||
1028 | kore_socket_backlog = kore_strtonum(option, 10, 0, UINT_MAX, &err); | |
1029 | if (err != KORE_RESULT_OK) { | |
1030 | printf("bad socket_backlog value: %s\n", option); | |
1019 | 1031 | return (KORE_RESULT_ERROR); |
1020 | 1032 | } |
1021 | 1033 | |
1030 | 1042 | } |
1031 | 1043 | |
1032 | 1044 | #if defined(KORE_USE_PGSQL) |
1033 | ||
1034 | static int | |
1035 | configure_pgsql_conn_max(char **argv) | |
1036 | { | |
1037 | int err; | |
1038 | ||
1039 | if (argv[1] == NULL) { | |
1040 | printf("missing parameter for pgsql_conn_max\n"); | |
1041 | return (KORE_RESULT_ERROR); | |
1042 | } | |
1043 | ||
1044 | pgsql_conn_max = kore_strtonum(argv[1], 10, 0, USHRT_MAX, &err); | |
1045 | if (err != KORE_RESULT_OK) { | |
1046 | printf("bad value for pgsql_conn_max: %s\n", argv[1]); | |
1047 | return (KORE_RESULT_ERROR); | |
1048 | } | |
1049 | ||
1050 | return (KORE_RESULT_OK); | |
1051 | } | |
1052 | ||
1053 | #endif | |
1045 | static int | |
1046 | configure_pgsql_conn_max(char *option) | |
1047 | { | |
1048 | int err; | |
1049 | ||
1050 | pgsql_conn_max = kore_strtonum(option, 10, 0, USHRT_MAX, &err); | |
1051 | if (err != KORE_RESULT_OK) { | |
1052 | printf("bad value for pgsql_conn_max: %s\n", option); | |
1053 | return (KORE_RESULT_ERROR); | |
1054 | } | |
1055 | ||
1056 | return (KORE_RESULT_OK); | |
1057 | } | |
1058 | #endif | |
1059 | ||
1060 | #if defined(KORE_USE_TASKS) | |
1061 | static int | |
1062 | configure_task_threads(char *option) | |
1063 | { | |
1064 | int err; | |
1065 | ||
1066 | kore_task_threads = kore_strtonum(option, 10, 0, UCHAR_MAX, &err); | |
1067 | if (err != KORE_RESULT_OK) { | |
1068 | printf("bad value for task_threads: %s\n", option); | |
1069 | return (KORE_RESULT_ERROR); | |
1070 | } | |
1071 | ||
1072 | return (KORE_RESULT_OK); | |
1073 | } | |
1074 | #endif |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
24 | 24 | #include "http.h" |
25 | 25 | |
26 | 26 | struct kore_pool connection_pool; |
27 | struct connection_list connections; | |
28 | struct connection_list disconnected; | |
27 | 29 | |
28 | 30 | void |
29 | 31 | kore_connection_init(void) |
30 | 32 | { |
33 | TAILQ_INIT(&connections); | |
34 | TAILQ_INIT(&disconnected); | |
35 | ||
31 | 36 | kore_pool_init(&connection_pool, "connection_pool", |
32 | 37 | sizeof(struct connection), worker_max_connections); |
33 | 38 | } |
34 | 39 | |
40 | void | |
41 | kore_connection_cleanup(void) | |
42 | { | |
43 | kore_debug("connection_cleanup()"); | |
44 | ||
45 | /* Drop all connections */ | |
46 | kore_connection_prune(KORE_CONNECTION_PRUNE_ALL); | |
47 | kore_pool_cleanup(&connection_pool); | |
48 | } | |
49 | ||
35 | 50 | struct connection * |
36 | 51 | kore_connection_new(void *owner) |
37 | 52 | { |
39 | 54 | |
40 | 55 | c = kore_pool_get(&connection_pool); |
41 | 56 | |
57 | #if !defined(KORE_NO_TLS) | |
42 | 58 | c->ssl = NULL; |
59 | c->cert = NULL; | |
60 | c->tls_reneg = 0; | |
61 | #endif | |
43 | 62 | c->flags = 0; |
44 | 63 | c->rnb = NULL; |
45 | 64 | c->snb = NULL; |
46 | c->cert = NULL; | |
47 | c->wscbs = NULL; | |
48 | 65 | c->owner = owner; |
49 | c->tls_reneg = 0; | |
66 | c->handle = NULL; | |
50 | 67 | c->disconnect = NULL; |
51 | 68 | c->hdlr_extra = NULL; |
52 | c->inflate_started = 0; | |
53 | c->deflate_started = 0; | |
54 | c->client_stream_id = 0; | |
55 | 69 | c->proto = CONN_PROTO_UNKNOWN; |
56 | 70 | c->type = KORE_TYPE_CONNECTION; |
57 | c->wsize_initial = SPDY_INIT_WSIZE; | |
58 | c->spdy_send_wsize = SPDY_INIT_WSIZE; | |
59 | c->spdy_recv_wsize = SPDY_INIT_WSIZE; | |
60 | 71 | c->idle_timer.start = 0; |
61 | 72 | c->idle_timer.length = KORE_IDLE_TIMER_MAX; |
62 | 73 | |
74 | #if !defined(KORE_NO_HTTP) | |
75 | c->wscbs = NULL; | |
76 | TAILQ_INIT(&(c->http_requests)); | |
77 | #endif | |
78 | ||
63 | 79 | TAILQ_INIT(&(c->send_queue)); |
64 | TAILQ_INIT(&(c->spdy_streams)); | |
65 | TAILQ_INIT(&(c->http_requests)); | |
66 | 80 | |
67 | 81 | return (c); |
68 | 82 | } |
69 | 83 | |
70 | 84 | int |
71 | kore_connection_accept(struct listener *l, struct connection **out) | |
85 | kore_connection_accept(struct listener *listener, struct connection **out) | |
72 | 86 | { |
73 | 87 | struct connection *c; |
74 | 88 | struct sockaddr *sin; |
75 | 89 | socklen_t len; |
76 | 90 | |
77 | kore_debug("kore_connection_accept(%p)", l); | |
91 | kore_debug("kore_connection_accept(%p)", listener); | |
78 | 92 | |
79 | 93 | *out = NULL; |
80 | c = kore_connection_new(l); | |
81 | ||
82 | c->addrtype = l->addrtype; | |
94 | c = kore_connection_new(listener); | |
95 | ||
96 | c->addrtype = listener->addrtype; | |
83 | 97 | if (c->addrtype == AF_INET) { |
84 | 98 | len = sizeof(struct sockaddr_in); |
85 | 99 | sin = (struct sockaddr *)&(c->addr.ipv4); |
88 | 102 | sin = (struct sockaddr *)&(c->addr.ipv6); |
89 | 103 | } |
90 | 104 | |
91 | if ((c->fd = accept(l->fd, sin, &len)) == -1) { | |
105 | if ((c->fd = accept(listener->fd, sin, &len)) == -1) { | |
92 | 106 | kore_pool_put(&connection_pool, c); |
93 | 107 | kore_debug("accept(): %s", errno_s); |
94 | 108 | return (KORE_RESULT_ERROR); |
95 | 109 | } |
96 | 110 | |
97 | if (!kore_connection_nonblock(c->fd)) { | |
111 | if (!kore_connection_nonblock(c->fd, 1)) { | |
98 | 112 | close(c->fd); |
99 | 113 | kore_pool_put(&connection_pool, c); |
100 | 114 | return (KORE_RESULT_ERROR); |
101 | 115 | } |
102 | 116 | |
103 | #if !defined(KORE_BENCHMARK) | |
117 | c->handle = kore_connection_handle; | |
118 | TAILQ_INSERT_TAIL(&connections, c, list); | |
119 | ||
120 | #if !defined(KORE_NO_TLS) | |
104 | 121 | c->state = CONN_STATE_SSL_SHAKE; |
105 | 122 | c->write = net_write_ssl; |
106 | 123 | c->read = net_read_ssl; |
107 | 124 | #else |
108 | 125 | c->state = CONN_STATE_ESTABLISHED; |
109 | c->proto = CONN_PROTO_HTTP; | |
110 | 126 | c->write = net_write; |
111 | 127 | c->read = net_read; |
112 | 128 | |
113 | if (http_keepalive_time != 0) | |
114 | c->idle_timer.length = http_keepalive_time * 1000; | |
115 | ||
116 | net_recv_queue(c, http_header_max, NETBUF_CALL_CB_ALWAYS, | |
117 | http_header_recv); | |
118 | #endif | |
119 | ||
120 | kore_worker_connection_add(c); | |
129 | if (listener->connect != NULL) { | |
130 | listener->connect(c); | |
131 | } else { | |
132 | #if !defined(KORE_NO_HTTP) | |
133 | c->proto = CONN_PROTO_HTTP; | |
134 | if (http_keepalive_time != 0) | |
135 | c->idle_timer.length = http_keepalive_time * 1000; | |
136 | net_recv_queue(c, http_header_max, | |
137 | NETBUF_CALL_CB_ALWAYS, http_header_recv); | |
138 | #endif | |
139 | } | |
140 | #endif | |
141 | ||
121 | 142 | kore_connection_start_idletimer(c); |
122 | 143 | |
123 | 144 | *out = c; |
124 | 145 | return (KORE_RESULT_OK); |
146 | } | |
147 | ||
148 | void | |
149 | kore_connection_check_timeout(void) | |
150 | { | |
151 | struct connection *c; | |
152 | u_int64_t now; | |
153 | ||
154 | now = kore_time_ms(); | |
155 | TAILQ_FOREACH(c, &connections, list) { | |
156 | if (c->proto == CONN_PROTO_MSG) | |
157 | continue; | |
158 | if (!(c->flags & CONN_IDLE_TIMER_ACT)) | |
159 | continue; | |
160 | kore_connection_check_idletimer(now, c); | |
161 | } | |
162 | } | |
163 | ||
164 | void | |
165 | kore_connection_prune(int all) | |
166 | { | |
167 | struct connection *c, *cnext; | |
168 | ||
169 | if (all) { | |
170 | for (c = TAILQ_FIRST(&connections); c != NULL; c = cnext) { | |
171 | cnext = TAILQ_NEXT(c, list); | |
172 | net_send_flush(c); | |
173 | kore_connection_disconnect(c); | |
174 | } | |
175 | } | |
176 | ||
177 | for (c = TAILQ_FIRST(&disconnected); c != NULL; c = cnext) { | |
178 | cnext = TAILQ_NEXT(c, list); | |
179 | TAILQ_REMOVE(&disconnected, c, list); | |
180 | kore_connection_remove(c); | |
181 | } | |
125 | 182 | } |
126 | 183 | |
127 | 184 | void |
133 | 190 | if (c->disconnect) |
134 | 191 | c->disconnect(c); |
135 | 192 | |
136 | kore_worker_connection_move(c); | |
193 | TAILQ_REMOVE(&connections, c, list); | |
194 | TAILQ_INSERT_TAIL(&disconnected, c, list); | |
137 | 195 | } |
138 | 196 | } |
139 | 197 | |
140 | 198 | int |
141 | 199 | kore_connection_handle(struct connection *c) |
142 | 200 | { |
143 | #if !defined(KORE_BENCHMARK) | |
201 | #if !defined(KORE_NO_TLS) | |
144 | 202 | int r; |
145 | u_int32_t len; | |
146 | const u_char *data; | |
203 | struct listener *listener; | |
147 | 204 | char cn[X509_CN_LENGTH]; |
148 | 205 | #endif |
149 | 206 | |
151 | 208 | kore_connection_stop_idletimer(c); |
152 | 209 | |
153 | 210 | switch (c->state) { |
154 | #if !defined(KORE_BENCHMARK) | |
211 | #if !defined(KORE_NO_TLS) | |
155 | 212 | case CONN_STATE_SSL_SHAKE: |
156 | 213 | if (c->ssl == NULL) { |
157 | 214 | c->ssl = SSL_new(primary_dom->ssl_ctx); |
191 | 248 | "no CN found in client certificate"); |
192 | 249 | return (KORE_RESULT_ERROR); |
193 | 250 | } |
251 | } else { | |
252 | c->cert = NULL; | |
194 | 253 | } |
195 | 254 | |
196 | 255 | r = SSL_get_verify_result(c->ssl); |
200 | 259 | return (KORE_RESULT_ERROR); |
201 | 260 | } |
202 | 261 | |
203 | SSL_get0_next_proto_negotiated(c->ssl, &data, &len); | |
204 | if (data) { | |
205 | if (!memcmp(data, "spdy/3", MIN(6, len))) { | |
206 | c->proto = CONN_PROTO_SPDY; | |
207 | c->idle_timer.length = spdy_idle_time; | |
208 | net_recv_queue(c, SPDY_FRAME_SIZE, 0, | |
209 | spdy_frame_recv); | |
210 | } else if (!memcmp(data, "http/1.1", MIN(8, len))) { | |
211 | c->proto = CONN_PROTO_HTTP; | |
212 | if (http_keepalive_time != 0) { | |
213 | c->idle_timer.length = | |
214 | http_keepalive_time * 1000; | |
215 | } | |
216 | ||
217 | net_recv_queue(c, http_header_max, | |
218 | NETBUF_CALL_CB_ALWAYS, | |
219 | http_header_recv); | |
220 | } else { | |
221 | kore_log(LOG_NOTICE, | |
222 | "npn: received unknown protocol"); | |
223 | return (KORE_RESULT_ERROR); | |
262 | if (c->owner != NULL) { | |
263 | listener = (struct listener *)c->owner; | |
264 | if (listener->connect != NULL) { | |
265 | listener->connect(c); | |
266 | return (KORE_RESULT_OK); | |
224 | 267 | } |
225 | } else { | |
226 | c->proto = CONN_PROTO_HTTP; | |
227 | if (http_keepalive_time != 0) { | |
228 | c->idle_timer.length = | |
229 | http_keepalive_time * 1000; | |
230 | } | |
231 | ||
232 | net_recv_queue(c, http_header_max, | |
233 | NETBUF_CALL_CB_ALWAYS, | |
234 | http_header_recv); | |
235 | } | |
268 | } | |
269 | ||
270 | #if !defined(KORE_NO_HTTP) | |
271 | c->proto = CONN_PROTO_HTTP; | |
272 | if (http_keepalive_time != 0) { | |
273 | c->idle_timer.length = | |
274 | http_keepalive_time * 1000; | |
275 | } | |
276 | ||
277 | net_recv_queue(c, http_header_max, | |
278 | NETBUF_CALL_CB_ALWAYS, http_header_recv); | |
279 | #endif | |
236 | 280 | |
237 | 281 | c->state = CONN_STATE_ESTABLISHED; |
238 | 282 | /* FALLTHROUGH */ |
239 | #endif /* !KORE_BENCHMARK */ | |
283 | #endif /* !KORE_NO_TLS */ | |
240 | 284 | case CONN_STATE_ESTABLISHED: |
241 | 285 | if (c->flags & CONN_READ_POSSIBLE) { |
242 | 286 | if (!net_recv_flush(c)) |
264 | 308 | kore_connection_remove(struct connection *c) |
265 | 309 | { |
266 | 310 | struct netbuf *nb, *next; |
267 | struct spdy_stream *s, *snext; | |
311 | #if !defined(KORE_NO_HTTP) | |
268 | 312 | struct http_request *req, *rnext; |
313 | #endif | |
269 | 314 | |
270 | 315 | kore_debug("kore_connection_remove(%p)", c); |
271 | 316 | |
272 | #if !defined(KORE_BENCHMARK) | |
317 | #if !defined(KORE_NO_TLS) | |
273 | 318 | if (c->ssl != NULL) { |
274 | 319 | SSL_shutdown(c->ssl); |
275 | 320 | SSL_free(c->ssl); |
282 | 327 | close(c->fd); |
283 | 328 | |
284 | 329 | if (c->hdlr_extra != NULL) |
285 | kore_mem_free(c->hdlr_extra); | |
286 | ||
287 | if (c->inflate_started) | |
288 | inflateEnd(&(c->z_inflate)); | |
289 | if (c->deflate_started) | |
290 | deflateEnd(&(c->z_deflate)); | |
291 | ||
330 | kore_free(c->hdlr_extra); | |
331 | ||
332 | #if !defined(KORE_NO_HTTP) | |
292 | 333 | for (req = TAILQ_FIRST(&(c->http_requests)); req != NULL; req = rnext) { |
293 | 334 | rnext = TAILQ_NEXT(req, olist); |
294 | 335 | TAILQ_REMOVE(&(c->http_requests), req, olist); |
295 | 336 | req->flags |= HTTP_REQUEST_DELETE; |
296 | 337 | http_request_wakeup(req); |
297 | 338 | } |
339 | #endif | |
298 | 340 | |
299 | 341 | for (nb = TAILQ_FIRST(&(c->send_queue)); nb != NULL; nb = next) { |
300 | 342 | next = TAILQ_NEXT(nb, list); |
301 | 343 | TAILQ_REMOVE(&(c->send_queue), nb, list); |
302 | 344 | if (!(nb->flags & NETBUF_IS_STREAM)) { |
303 | kore_mem_free(nb->buf); | |
345 | kore_free(nb->buf); | |
304 | 346 | } else if (nb->cb != NULL) { |
305 | 347 | (void)nb->cb(nb); |
306 | 348 | } |
308 | 350 | } |
309 | 351 | |
310 | 352 | if (c->rnb != NULL) { |
311 | kore_mem_free(c->rnb->buf); | |
353 | kore_free(c->rnb->buf); | |
312 | 354 | kore_pool_put(&nb_pool, c->rnb); |
313 | 355 | } |
314 | 356 | |
315 | for (s = TAILQ_FIRST(&(c->spdy_streams)); s != NULL; s = snext) { | |
316 | snext = TAILQ_NEXT(s, list); | |
317 | TAILQ_REMOVE(&(c->spdy_streams), s, list); | |
318 | ||
319 | if (s->hblock != NULL) { | |
320 | if (s->hblock->header_block != NULL) | |
321 | kore_mem_free(s->hblock->header_block); | |
322 | kore_mem_free(s->hblock); | |
323 | } | |
324 | ||
325 | kore_mem_free(s); | |
326 | } | |
327 | ||
328 | kore_worker_connection_remove(c); | |
329 | 357 | kore_pool_put(&connection_pool, c); |
330 | 358 | } |
331 | 359 | |
337 | 365 | d = now - c->idle_timer.start; |
338 | 366 | if (d >= c->idle_timer.length) { |
339 | 367 | kore_debug("%p idle for %d ms, expiring", c, d); |
340 | if (c->proto == CONN_PROTO_SPDY) | |
341 | spdy_session_teardown(c, SPDY_SESSION_ERROR_OK); | |
342 | else | |
343 | kore_connection_disconnect(c); | |
368 | kore_connection_disconnect(c); | |
344 | 369 | } |
345 | 370 | } |
346 | 371 | |
363 | 388 | } |
364 | 389 | |
365 | 390 | int |
366 | kore_connection_nonblock(int fd) | |
391 | kore_connection_nonblock(int fd, int nodelay) | |
367 | 392 | { |
368 | 393 | int flags; |
369 | 394 | |
380 | 405 | return (KORE_RESULT_ERROR); |
381 | 406 | } |
382 | 407 | |
383 | flags = 1; | |
384 | if (setsockopt(fd, IPPROTO_TCP, | |
385 | TCP_NODELAY, (char *)&flags, sizeof(flags)) == -1) { | |
386 | kore_log(LOG_NOTICE, | |
387 | "failed to set TCP_NODELAY on %d", fd); | |
408 | if (nodelay) { | |
409 | flags = 1; | |
410 | if (setsockopt(fd, IPPROTO_TCP, | |
411 | TCP_NODELAY, (char *)&flags, sizeof(flags)) == -1) { | |
412 | kore_log(LOG_NOTICE, | |
413 | "failed to set TCP_NODELAY on %d", fd); | |
414 | } | |
388 | 415 | } |
389 | 416 | |
390 | 417 | return (KORE_RESULT_OK); |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
15 | 15 | |
16 | 16 | #include <sys/param.h> |
17 | 17 | |
18 | #if !defined(KORE_NO_TLS) | |
19 | #include <openssl/x509.h> | |
20 | #include <openssl/bio.h> | |
21 | #include <openssl/evp.h> | |
22 | #include <openssl/ec.h> | |
23 | #include <openssl/ecdsa.h> | |
24 | #include <poll.h> | |
25 | #endif | |
26 | ||
27 | #include <fnmatch.h> | |
28 | ||
18 | 29 | #include "kore.h" |
19 | 30 | |
20 | 31 | #define SSL_SESSION_ID "kore_ssl_sessionid" |
21 | 32 | |
22 | 33 | struct kore_domain_h domains; |
23 | 34 | struct kore_domain *primary_dom = NULL; |
35 | ||
36 | #if !defined(KORE_NO_TLS) | |
37 | static u_int8_t keymgr_buf[2048]; | |
38 | static size_t keymgr_buflen = 0; | |
39 | static int keymgr_response = 0; | |
24 | 40 | DH *tls_dhparam = NULL; |
25 | 41 | int tls_version = KORE_TLS_VERSION_1_2; |
42 | #endif | |
26 | 43 | |
27 | 44 | static void domain_load_crl(struct kore_domain *); |
28 | 45 | |
29 | #if !defined(KORE_BENCHMARK) | |
46 | #if !defined(KORE_NO_TLS) | |
30 | 47 | static int domain_x509_verify(int, X509_STORE_CTX *); |
48 | ||
49 | static void keymgr_init(void); | |
50 | static void keymgr_await_data(void); | |
51 | static void keymgr_msg_response(struct kore_msg *, const void *); | |
52 | ||
53 | static int keymgr_rsa_init(RSA *); | |
54 | static int keymgr_rsa_finish(RSA *); | |
55 | static int keymgr_rsa_privenc(int, const unsigned char *, | |
56 | unsigned char *, RSA *, int); | |
57 | ||
58 | static ECDSA_SIG *keymgr_ecdsa_sign(const unsigned char *, int, | |
59 | const BIGNUM *, const BIGNUM *, EC_KEY *); | |
60 | ||
61 | #if !defined(LIBRESSL_VERSION_TEXT) | |
62 | /* | |
63 | * Run own ecdsa_method data structure as OpenSSL has this in ecs_locl.h | |
64 | * and does not export this on systems. | |
65 | * | |
66 | * XXX - OpenSSL is merging ECDSA functionality into EC in 1.1.0. | |
67 | */ | |
68 | struct ecdsa_method { | |
69 | const char *name; | |
70 | ECDSA_SIG *(*ecdsa_do_sign)(const unsigned char *, | |
71 | int, const BIGNUM *, const BIGNUM *, EC_KEY *); | |
72 | int (*ecdsa_sign_setup)(EC_KEY *, BN_CTX *, BIGNUM **, | |
73 | BIGNUM **); | |
74 | int (*ecdsa_do_verify)(const unsigned char *, int, | |
75 | const ECDSA_SIG *, EC_KEY *); | |
76 | int flags; | |
77 | char *app_data; | |
78 | }; | |
79 | #endif | |
80 | ||
81 | static ECDSA_METHOD keymgr_ecdsa = { | |
82 | "kore ECDSA keymgr method", | |
83 | keymgr_ecdsa_sign, | |
84 | NULL, | |
85 | NULL, | |
86 | 0, | |
87 | NULL | |
88 | }; | |
89 | ||
90 | static RSA_METHOD keymgr_rsa = { | |
91 | "kore RSA keymgr method", | |
92 | NULL, | |
93 | NULL, | |
94 | keymgr_rsa_privenc, | |
95 | NULL, | |
96 | NULL, | |
97 | NULL, | |
98 | keymgr_rsa_init, | |
99 | keymgr_rsa_finish, | |
100 | RSA_METHOD_FLAG_NO_CHECK, | |
101 | NULL, | |
102 | NULL, | |
103 | NULL, | |
104 | NULL | |
105 | }; | |
106 | ||
31 | 107 | #endif |
32 | 108 | |
33 | 109 | void |
34 | 110 | kore_domain_init(void) |
35 | 111 | { |
36 | 112 | TAILQ_INIT(&domains); |
113 | } | |
114 | ||
115 | void | |
116 | kore_domain_cleanup(void) | |
117 | { | |
118 | struct kore_domain *dom; | |
119 | ||
120 | while ((dom = TAILQ_FIRST(&domains)) != NULL) { | |
121 | TAILQ_REMOVE(&domains, dom, list); | |
122 | kore_domain_free(dom); | |
123 | } | |
37 | 124 | } |
38 | 125 | |
39 | 126 | int |
48 | 135 | |
49 | 136 | dom = kore_malloc(sizeof(*dom)); |
50 | 137 | dom->accesslog = -1; |
138 | #if !defined(KORE_NO_TLS) | |
51 | 139 | dom->cafile = NULL; |
52 | 140 | dom->certkey = NULL; |
53 | 141 | dom->ssl_ctx = NULL; |
54 | 142 | dom->certfile = NULL; |
55 | 143 | dom->crlfile = NULL; |
144 | #endif | |
56 | 145 | dom->domain = kore_strdup(domain); |
57 | 146 | TAILQ_INIT(&(dom->handlers)); |
58 | 147 | TAILQ_INSERT_TAIL(&domains, dom, list); |
64 | 153 | } |
65 | 154 | |
66 | 155 | void |
156 | kore_domain_free(struct kore_domain *dom) | |
157 | { | |
158 | #if !defined(KORE_NO_HTTP) | |
159 | struct kore_module_handle *hdlr; | |
160 | #endif | |
161 | if (dom == NULL) | |
162 | return; | |
163 | ||
164 | if (primary_dom == dom) | |
165 | primary_dom = NULL; | |
166 | ||
167 | TAILQ_REMOVE(&domains, dom, list); | |
168 | ||
169 | if (dom->domain != NULL) | |
170 | kore_free(dom->domain); | |
171 | ||
172 | #if !defined(KORE_NO_TLS) | |
173 | if (dom->ssl_ctx != NULL) | |
174 | SSL_CTX_free(dom->ssl_ctx); | |
175 | if (dom->cafile != NULL) | |
176 | kore_free(dom->cafile); | |
177 | if (dom->certkey != NULL) | |
178 | kore_free(dom->certkey); | |
179 | if (dom->certfile != NULL) | |
180 | kore_free(dom->certfile); | |
181 | if (dom->crlfile != NULL) | |
182 | kore_free(dom->crlfile); | |
183 | #endif | |
184 | ||
185 | #if !defined(KORE_NO_HTTP) | |
186 | /* Drop all handlers associated with this domain */ | |
187 | while ((hdlr = TAILQ_FIRST(&(dom->handlers))) != NULL) { | |
188 | TAILQ_REMOVE(&(dom->handlers), hdlr, list); | |
189 | kore_module_handler_free(hdlr); | |
190 | } | |
191 | #endif | |
192 | kore_free(dom); | |
193 | } | |
194 | ||
195 | void | |
67 | 196 | kore_domain_sslstart(struct kore_domain *dom) |
68 | 197 | { |
69 | #if !defined(KORE_BENCHMARK) | |
198 | #if !defined(KORE_NO_TLS) | |
199 | BIO *in; | |
200 | RSA *rsa; | |
201 | X509 *x509; | |
202 | EVP_PKEY *pkey; | |
70 | 203 | STACK_OF(X509_NAME) *certs; |
204 | EC_KEY *eckey; | |
71 | 205 | X509_STORE *store; |
72 | 206 | const SSL_METHOD *method; |
73 | 207 | #if !defined(OPENSSL_NO_EC) |
99 | 233 | dom->certfile, ssl_errno_s); |
100 | 234 | } |
101 | 235 | |
102 | if (!SSL_CTX_use_PrivateKey_file(dom->ssl_ctx, dom->certkey, | |
103 | SSL_FILETYPE_PEM)) { | |
104 | fatal("SSL_CTX_use_PrivateKey_file(%s): %s", | |
105 | dom->certkey, ssl_errno_s); | |
106 | } | |
236 | if ((in = BIO_new(BIO_s_file_internal())) == NULL) | |
237 | fatal("BIO_new: %s", ssl_errno_s); | |
238 | if (BIO_read_filename(in, dom->certfile) <= 0) | |
239 | fatal("BIO_read_filename: %s", ssl_errno_s); | |
240 | if ((x509 = PEM_read_bio_X509(in, NULL, NULL, NULL)) == NULL) | |
241 | fatal("PEM_read_bio_X509: %s", ssl_errno_s); | |
242 | ||
243 | BIO_free(in); | |
244 | ||
245 | if ((pkey = X509_get_pubkey(x509)) == NULL) | |
246 | fatal("certificate has no public key"); | |
247 | ||
248 | switch (EVP_PKEY_id(pkey)) { | |
249 | case EVP_PKEY_RSA: | |
250 | if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL) | |
251 | fatal("no RSA public key present"); | |
252 | RSA_set_app_data(rsa, dom); | |
253 | RSA_set_method(rsa, &keymgr_rsa); | |
254 | break; | |
255 | case EVP_PKEY_EC: | |
256 | if ((eckey = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) | |
257 | fatal("no EC public key present"); | |
258 | ECDSA_set_ex_data(eckey, 0, dom); | |
259 | ECDSA_set_method(eckey, &keymgr_ecdsa); | |
260 | break; | |
261 | default: | |
262 | fatal("unknown public key in certificate"); | |
263 | } | |
264 | ||
265 | if (!SSL_CTX_use_PrivateKey(dom->ssl_ctx, pkey)) | |
266 | fatal("SSL_CTX_use_PrivateKey(): %s", ssl_errno_s); | |
107 | 267 | |
108 | 268 | if (!SSL_CTX_check_private_key(dom->ssl_ctx)) |
109 | 269 | fatal("Public/Private key for %s do not match", dom->domain); |
114 | 274 | SSL_CTX_set_tmp_dh(dom->ssl_ctx, tls_dhparam); |
115 | 275 | SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_SINGLE_DH_USE); |
116 | 276 | |
117 | #if !defined(OPENSSL_NO_EC) | |
118 | if ((ecdh = EC_KEY_new_by_curve_name(NID_secp384r1)) != NULL) { | |
119 | SSL_CTX_set_tmp_ecdh(dom->ssl_ctx, ecdh); | |
120 | EC_KEY_free(ecdh); | |
121 | } | |
122 | #endif | |
277 | if ((ecdh = EC_KEY_new_by_curve_name(NID_secp384r1)) == NULL) | |
278 | fatal("EC_KEY_new_by_curve_name: %s", ssl_errno_s); | |
279 | ||
280 | SSL_CTX_set_tmp_ecdh(dom->ssl_ctx, ecdh); | |
281 | EC_KEY_free(ecdh); | |
123 | 282 | |
124 | 283 | SSL_CTX_set_options(dom->ssl_ctx, SSL_OP_NO_COMPRESSION); |
125 | 284 | |
153 | 312 | * Note that OpenBSD has since heartbleed removed freelists |
154 | 313 | * from its OpenSSL in base so we don't need to care about it. |
155 | 314 | */ |
156 | #if !defined(OpenBSD) || (OpenBSD < 201405) | |
315 | #if !defined(LIBRESSL_VERSION_TEXT) | |
157 | 316 | dom->ssl_ctx->freelist_max_len = 0; |
158 | 317 | #endif |
159 | 318 | SSL_CTX_set_mode(dom->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); |
169 | 328 | |
170 | 329 | SSL_CTX_set_info_callback(dom->ssl_ctx, kore_tls_info_callback); |
171 | 330 | SSL_CTX_set_tlsext_servername_callback(dom->ssl_ctx, kore_tls_sni_cb); |
172 | SSL_CTX_set_next_protos_advertised_cb(dom->ssl_ctx, | |
173 | kore_tls_npn_cb, NULL); | |
174 | ||
175 | kore_mem_free(dom->certfile); | |
176 | kore_mem_free(dom->certkey); | |
177 | #endif | |
331 | ||
332 | kore_free(dom->certfile); | |
333 | dom->certfile = NULL; | |
334 | #endif | |
335 | } | |
336 | ||
337 | void | |
338 | kore_domain_callback(void (*cb)(struct kore_domain *)) | |
339 | { | |
340 | struct kore_domain *dom; | |
341 | ||
342 | TAILQ_FOREACH(dom, &domains, list) { | |
343 | cb(dom); | |
344 | } | |
178 | 345 | } |
179 | 346 | |
180 | 347 | struct kore_domain * |
183 | 350 | struct kore_domain *dom; |
184 | 351 | |
185 | 352 | TAILQ_FOREACH(dom, &domains, list) { |
186 | if (!strcmp(dom->domain, domain)) | |
353 | if (!fnmatch(dom->domain, domain, FNM_CASEFOLD)) | |
187 | 354 | return (dom); |
188 | 355 | } |
189 | 356 | |
195 | 362 | { |
196 | 363 | struct kore_domain *dom; |
197 | 364 | |
198 | TAILQ_FOREACH(dom, &domains, list) | |
199 | close(dom->accesslog); | |
365 | TAILQ_FOREACH(dom, &domains, list) { | |
366 | if (dom->accesslog != -1) { | |
367 | (void)close(dom->accesslog); | |
368 | } | |
369 | } | |
200 | 370 | } |
201 | 371 | |
202 | 372 | void |
208 | 378 | domain_load_crl(dom); |
209 | 379 | } |
210 | 380 | |
381 | void | |
382 | kore_domain_keymgr_init(void) | |
383 | { | |
384 | #if !defined(KORE_NO_TLS) | |
385 | keymgr_init(); | |
386 | kore_msg_register(KORE_MSG_KEYMGR_RESP, keymgr_msg_response); | |
387 | #endif | |
388 | } | |
389 | ||
211 | 390 | static void |
212 | 391 | domain_load_crl(struct kore_domain *dom) |
213 | 392 | { |
214 | #if !defined(KORE_BENCHMARK) | |
393 | #if !defined(KORE_NO_TLS) | |
215 | 394 | X509_STORE *store; |
216 | 395 | |
217 | 396 | ERR_clear_error(); |
240 | 419 | #endif |
241 | 420 | } |
242 | 421 | |
243 | #if !defined(KORE_BENCHMARK) | |
422 | #if !defined(KORE_NO_TLS) | |
423 | static void | |
424 | keymgr_init(void) | |
425 | { | |
426 | const RSA_METHOD *meth; | |
427 | ||
428 | if ((meth = RSA_get_default_method()) == NULL) | |
429 | fatal("failed to obtain RSA method"); | |
430 | ||
431 | keymgr_rsa.rsa_pub_enc = meth->rsa_pub_enc; | |
432 | keymgr_rsa.rsa_pub_dec = meth->rsa_pub_dec; | |
433 | keymgr_rsa.bn_mod_exp = meth->bn_mod_exp; | |
434 | } | |
435 | ||
436 | static int | |
437 | keymgr_rsa_init(RSA *rsa) | |
438 | { | |
439 | if (rsa != NULL) { | |
440 | rsa->flags |= RSA_FLAG_EXT_PKEY | RSA_METHOD_FLAG_NO_CHECK; | |
441 | return (1); | |
442 | } | |
443 | ||
444 | return (0); | |
445 | } | |
446 | ||
447 | static int | |
448 | keymgr_rsa_privenc(int flen, const unsigned char *from, unsigned char *to, | |
449 | RSA *rsa, int padding) | |
450 | { | |
451 | int ret; | |
452 | size_t len; | |
453 | struct kore_keyreq *req; | |
454 | struct kore_domain *dom; | |
455 | ||
456 | len = sizeof(*req) + flen; | |
457 | if (len > sizeof(keymgr_buf)) | |
458 | fatal("keymgr_buf too small"); | |
459 | ||
460 | if ((dom = RSA_get_app_data(rsa)) == NULL) | |
461 | fatal("RSA key has no domain attached"); | |
462 | if (strlen(dom->domain) >= KORE_DOMAINNAME_LEN - 1) | |
463 | fatal("domain name too long"); | |
464 | ||
465 | memset(keymgr_buf, 0, sizeof(keymgr_buf)); | |
466 | ||
467 | req = (struct kore_keyreq *)keymgr_buf; | |
468 | req->data_len = flen; | |
469 | req->padding = padding; | |
470 | req->domain_len = strlen(dom->domain); | |
471 | ||
472 | memcpy(&req->data[0], from, req->data_len); | |
473 | memcpy(req->domain, dom->domain, req->domain_len); | |
474 | ||
475 | kore_msg_send(KORE_WORKER_KEYMGR, KORE_MSG_KEYMGR_REQ, keymgr_buf, len); | |
476 | keymgr_await_data(); | |
477 | ||
478 | ret = -1; | |
479 | if (keymgr_response) { | |
480 | if (keymgr_buflen < INT_MAX && | |
481 | (int)keymgr_buflen == RSA_size(rsa)) { | |
482 | ret = RSA_size(rsa); | |
483 | memcpy(to, keymgr_buf, RSA_size(rsa)); | |
484 | } | |
485 | } | |
486 | ||
487 | keymgr_buflen = 0; | |
488 | keymgr_response = 0; | |
489 | kore_platform_event_all(worker->msg[1]->fd, worker->msg[1]); | |
490 | ||
491 | return (ret); | |
492 | } | |
493 | ||
494 | static int | |
495 | keymgr_rsa_finish(RSA *rsa) | |
496 | { | |
497 | return (1); | |
498 | } | |
499 | ||
500 | static ECDSA_SIG * | |
501 | keymgr_ecdsa_sign(const unsigned char *dgst, int dgst_len, | |
502 | const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *eckey) | |
503 | { | |
504 | size_t len; | |
505 | ECDSA_SIG *sig; | |
506 | const u_int8_t *ptr; | |
507 | struct kore_domain *dom; | |
508 | struct kore_keyreq *req; | |
509 | ||
510 | if (in_kinv != NULL || in_r != NULL) | |
511 | return (NULL); | |
512 | ||
513 | len = sizeof(*req) + dgst_len; | |
514 | if (len > sizeof(keymgr_buf)) | |
515 | fatal("keymgr_buf too small"); | |
516 | ||
517 | if ((dom = ECDSA_get_ex_data(eckey, 0)) == NULL) | |
518 | fatal("EC_KEY has no domain"); | |
519 | ||
520 | memset(keymgr_buf, 0, sizeof(keymgr_buf)); | |
521 | ||
522 | req = (struct kore_keyreq *)keymgr_buf; | |
523 | req->data_len = dgst_len; | |
524 | req->domain_len = strlen(dom->domain); | |
525 | ||
526 | memcpy(&req->data[0], dgst, req->data_len); | |
527 | memcpy(req->domain, dom->domain, req->domain_len); | |
528 | ||
529 | kore_msg_send(KORE_WORKER_KEYMGR, KORE_MSG_KEYMGR_REQ, keymgr_buf, len); | |
530 | keymgr_await_data(); | |
531 | ||
532 | if (keymgr_response) { | |
533 | ptr = keymgr_buf; | |
534 | sig = d2i_ECDSA_SIG(NULL, &ptr, keymgr_buflen); | |
535 | } else { | |
536 | sig = NULL; | |
537 | } | |
538 | ||
539 | keymgr_buflen = 0; | |
540 | keymgr_response = 0; | |
541 | kore_platform_event_all(worker->msg[1]->fd, worker->msg[1]); | |
542 | ||
543 | return (sig); | |
544 | } | |
545 | ||
546 | static void | |
547 | keymgr_await_data(void) | |
548 | { | |
549 | int ret; | |
550 | struct pollfd pfd[1]; | |
551 | u_int64_t start, cur; | |
552 | ||
553 | /* | |
554 | * We need to wait until the keymgr responds to us, so keep doing | |
555 | * net_recv_flush() until our callback for KORE_MSG_KEYMGR_RESP | |
556 | * tells us that we have obtained the response. | |
557 | * | |
558 | * This means other internal messages can still be delivered by | |
559 | * this worker process to the appropriate callbacks but we do not | |
560 | * drop out until we've either received an answer from the keymgr | |
561 | * or until the timeout has been reached. | |
562 | * | |
563 | * It will however block any other I/O and request handling on | |
564 | * this worker until either of the above criteria is met. | |
565 | */ | |
566 | start = kore_time_ms(); | |
567 | kore_platform_disable_read(worker->msg[1]->fd); | |
568 | ||
569 | keymgr_response = 0; | |
570 | memset(keymgr_buf, 0, sizeof(keymgr_buf)); | |
571 | ||
572 | for (;;) { | |
573 | pfd[0].fd = worker->msg[1]->fd; | |
574 | pfd[0].events = POLLIN; | |
575 | pfd[0].revents = 0; | |
576 | ||
577 | ret = poll(pfd, 1, 100); | |
578 | if (ret == -1) { | |
579 | if (errno == EINTR) | |
580 | continue; | |
581 | fatal("poll: %s", errno_s); | |
582 | } | |
583 | ||
584 | cur = kore_time_ms(); | |
585 | if ((cur - start) > 1000) | |
586 | break; | |
587 | ||
588 | if (ret == 0) | |
589 | continue; | |
590 | ||
591 | if (pfd[0].revents & (POLLERR | POLLHUP)) | |
592 | break; | |
593 | if (!(pfd[0].revents & POLLIN)) | |
594 | break; | |
595 | ||
596 | worker->msg[1]->flags |= CONN_READ_POSSIBLE; | |
597 | if (!net_recv_flush(worker->msg[1])) | |
598 | break; | |
599 | ||
600 | if (keymgr_response) | |
601 | break; | |
602 | } | |
603 | } | |
604 | ||
605 | static void | |
606 | keymgr_msg_response(struct kore_msg *msg, const void *data) | |
607 | { | |
608 | keymgr_response = 1; | |
609 | keymgr_buflen = msg->length; | |
610 | ||
611 | if (keymgr_buflen > sizeof(keymgr_buf)) | |
612 | return; | |
613 | ||
614 | memcpy(keymgr_buf, data, keymgr_buflen); | |
615 | } | |
616 | ||
244 | 617 | static int |
245 | 618 | domain_x509_verify(int ok, X509_STORE_CTX *ctx) |
246 | 619 | { |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
17 | 17 | |
18 | 18 | #include <ctype.h> |
19 | 19 | #include <inttypes.h> |
20 | ||
21 | #include "spdy.h" | |
20 | #include <stdio.h> | |
21 | #include <string.h> | |
22 | ||
23 | #include <fcntl.h> | |
24 | #include <unistd.h> | |
25 | ||
22 | 26 | #include "kore.h" |
23 | 27 | #include "http.h" |
24 | 28 | |
30 | 34 | #include "tasks.h" |
31 | 35 | #endif |
32 | 36 | |
33 | static int http_body_recv(struct netbuf *); | |
34 | static void http_error_response(struct connection *, | |
35 | struct spdy_stream *, int); | |
36 | static void http_argument_add(struct http_request *, const char *, | |
37 | void *, u_int32_t, int); | |
38 | static void http_file_add(struct http_request *, const char *, | |
39 | const char *, u_int8_t *, u_int32_t); | |
40 | static void http_response_normal(struct http_request *, | |
41 | struct connection *, int, void *, u_int32_t); | |
42 | static void http_response_spdy(struct http_request *, | |
43 | struct connection *, struct spdy_stream *, | |
44 | int, void *, u_int32_t); | |
37 | static int http_body_recv(struct netbuf *); | |
38 | static int http_body_rewind(struct http_request *); | |
39 | static void http_error_response(struct connection *, int); | |
40 | static void http_argument_add(struct http_request *, const char *, char *); | |
41 | static void http_response_normal(struct http_request *, | |
42 | struct connection *, int, const void *, size_t); | |
43 | static void multipart_add_field(struct http_request *, struct kore_buf *, | |
44 | const char *, const char *, const int); | |
45 | static void multipart_file_add(struct http_request *, struct kore_buf *, | |
46 | const char *, const char *, const char *, const int); | |
47 | static int multipart_find_data(struct kore_buf *, struct kore_buf *, | |
48 | size_t *, struct http_request *, const void *, size_t); | |
49 | static int multipart_parse_headers(struct http_request *, | |
50 | struct kore_buf *, struct kore_buf *, | |
51 | const char *, const int); | |
45 | 52 | |
46 | 53 | static struct kore_buf *header_buf; |
47 | 54 | static char http_version[32]; |
48 | 55 | static u_int16_t http_version_len; |
49 | static char http_version_spdy[32]; | |
50 | 56 | static TAILQ_HEAD(, http_request) http_requests; |
51 | 57 | static TAILQ_HEAD(, http_request) http_requests_sleeping; |
52 | 58 | static struct kore_pool http_request_pool; |
53 | 59 | static struct kore_pool http_header_pool; |
54 | 60 | static struct kore_pool http_host_pool; |
55 | 61 | static struct kore_pool http_path_pool; |
62 | static struct kore_pool http_body_path; | |
56 | 63 | |
57 | 64 | int http_request_count = 0; |
58 | 65 | u_int32_t http_request_limit = HTTP_REQUEST_LIMIT; |
60 | 67 | u_int16_t http_header_max = HTTP_HEADER_MAX_LEN; |
61 | 68 | u_int16_t http_keepalive_time = HTTP_KEEPALIVE_TIME; |
62 | 69 | u_int64_t http_body_max = HTTP_BODY_MAX_LEN; |
70 | u_int64_t http_body_disk_offload = HTTP_BODY_DISK_OFFLOAD; | |
71 | char *http_body_disk_path = HTTP_BODY_DISK_PATH; | |
63 | 72 | |
64 | 73 | void |
65 | 74 | http_init(void) |
69 | 78 | TAILQ_INIT(&http_requests); |
70 | 79 | TAILQ_INIT(&http_requests_sleeping); |
71 | 80 | |
72 | header_buf = kore_buf_create(1024); | |
73 | ||
74 | l = snprintf(http_version_spdy, sizeof(http_version_spdy), | |
75 | "kore (%d.%d.%d-%s)", KORE_VERSION_MAJOR, KORE_VERSION_MINOR, | |
76 | KORE_VERSION_PATCH, KORE_VERSION_STATE); | |
77 | if (l == -1 || (size_t)l >= sizeof(http_version_spdy)) | |
78 | fatal("http_init(): http_version_spdy buffer too small"); | |
81 | header_buf = kore_buf_alloc(1024); | |
79 | 82 | |
80 | 83 | l = snprintf(http_version, sizeof(http_version), |
81 | 84 | "server: kore (%d.%d.%d-%s)\r\n", KORE_VERSION_MAJOR, |
95 | 98 | "http_host_pool", KORE_DOMAINNAME_LEN, prealloc); |
96 | 99 | kore_pool_init(&http_path_pool, |
97 | 100 | "http_path_pool", HTTP_URI_LEN, prealloc); |
101 | kore_pool_init(&http_body_path, | |
102 | "http_body_path", HTTP_BODY_PATH_MAX, prealloc); | |
103 | } | |
104 | ||
105 | void | |
106 | http_cleanup(void) | |
107 | { | |
108 | if (header_buf != NULL) { | |
109 | kore_buf_free(header_buf); | |
110 | header_buf = NULL; | |
111 | } | |
112 | ||
113 | kore_pool_cleanup(&http_request_pool); | |
114 | kore_pool_cleanup(&http_header_pool); | |
115 | kore_pool_cleanup(&http_host_pool); | |
116 | kore_pool_cleanup(&http_path_pool); | |
117 | kore_pool_cleanup(&http_body_path); | |
98 | 118 | } |
99 | 119 | |
100 | 120 | int |
101 | http_request_new(struct connection *c, struct spdy_stream *s, const char *host, | |
121 | http_request_new(struct connection *c, const char *host, | |
102 | 122 | const char *method, const char *path, const char *version, |
103 | 123 | struct http_request **out) |
104 | 124 | { |
105 | 125 | char *p; |
106 | 126 | struct http_request *req; |
127 | struct kore_module_handle *hdlr; | |
107 | 128 | int m, flags; |
108 | size_t hostlen, pathlen; | |
109 | ||
110 | kore_debug("http_request_new(%p, %p, %s, %s, %s, %s)", c, s, | |
111 | host, method, path, version); | |
129 | size_t hostlen, pathlen, qsoff; | |
130 | ||
131 | kore_debug("http_request_new(%p, %s, %s, %s, %s)", c, host, | |
132 | method, path, version); | |
112 | 133 | |
113 | 134 | if ((hostlen = strlen(host)) >= KORE_DOMAINNAME_LEN - 1) { |
114 | http_error_response(c, s, 500); | |
135 | http_error_response(c, 500); | |
115 | 136 | return (KORE_RESULT_ERROR); |
116 | 137 | } |
117 | 138 | |
118 | 139 | if ((pathlen = strlen(path)) >= HTTP_URI_LEN - 1) { |
119 | http_error_response(c, s, 414); | |
140 | http_error_response(c, 414); | |
120 | 141 | return (KORE_RESULT_ERROR); |
121 | 142 | } |
122 | 143 | |
123 | 144 | if (strcasecmp(version, "http/1.1")) { |
124 | http_error_response(c, s, 505); | |
145 | http_error_response(c, 505); | |
125 | 146 | return (KORE_RESULT_ERROR); |
126 | 147 | } |
148 | ||
149 | if ((p = strchr(path, '?')) != NULL) { | |
150 | *p = '\0'; | |
151 | qsoff = p - path; | |
152 | } else { | |
153 | qsoff = 0; | |
154 | } | |
155 | ||
156 | if ((hdlr = kore_module_handler_find(host, path)) == NULL) { | |
157 | http_error_response(c, 404); | |
158 | return (KORE_RESULT_ERROR); | |
159 | } | |
160 | ||
161 | if (p != NULL) | |
162 | *p = '?'; | |
127 | 163 | |
128 | 164 | if (!strcasecmp(method, "get")) { |
129 | 165 | m = HTTP_METHOD_GET; |
141 | 177 | m = HTTP_METHOD_HEAD; |
142 | 178 | flags = HTTP_REQUEST_COMPLETE; |
143 | 179 | } else { |
144 | http_error_response(c, s, 400); | |
180 | http_error_response(c, 400); | |
145 | 181 | return (KORE_RESULT_ERROR); |
146 | 182 | } |
147 | 183 | |
151 | 187 | req->start = 0; |
152 | 188 | req->owner = c; |
153 | 189 | req->status = 0; |
154 | req->stream = s; | |
155 | 190 | req->method = m; |
156 | req->hdlr = NULL; | |
191 | req->hdlr = hdlr; | |
157 | 192 | req->agent = NULL; |
158 | 193 | req->flags = flags; |
159 | 194 | req->fsm_state = 0; |
160 | 195 | req->http_body = NULL; |
196 | req->http_body_fd = -1; | |
161 | 197 | req->hdlr_extra = NULL; |
162 | 198 | req->query_string = NULL; |
163 | req->multipart_body = NULL; | |
199 | req->http_body_length = 0; | |
200 | req->http_body_offset = 0; | |
201 | req->http_body_path = NULL; | |
164 | 202 | |
165 | 203 | if ((p = strrchr(host, ':')) != NULL) |
166 | 204 | *p = '\0'; |
167 | 205 | |
168 | 206 | req->host = kore_pool_get(&http_host_pool); |
169 | (void)memcpy(req->host, host, hostlen); | |
207 | memcpy(req->host, host, hostlen); | |
170 | 208 | req->host[hostlen] = '\0'; |
171 | 209 | |
172 | 210 | req->path = kore_pool_get(&http_path_pool); |
173 | (void)memcpy(req->path, path, pathlen); | |
211 | memcpy(req->path, path, pathlen); | |
174 | 212 | req->path[pathlen] = '\0'; |
175 | 213 | |
176 | if ((req->query_string = strchr(req->path, '?')) != NULL) | |
214 | if (qsoff > 0) { | |
215 | req->query_string = req->path + qsoff; | |
177 | 216 | *(req->query_string)++ = '\0'; |
217 | } else { | |
218 | req->query_string = NULL; | |
219 | } | |
178 | 220 | |
179 | 221 | TAILQ_INIT(&(req->resp_headers)); |
180 | 222 | TAILQ_INIT(&(req->req_headers)); |
181 | 223 | TAILQ_INIT(&(req->arguments)); |
182 | 224 | TAILQ_INIT(&(req->files)); |
183 | 225 | |
184 | if (s != NULL) { | |
185 | if (!http_request_header(req, "user-agent", &(req->agent))) | |
186 | req->agent = kore_strdup("unknown"); | |
187 | } | |
188 | ||
189 | 226 | #if defined(KORE_USE_TASKS) |
190 | 227 | LIST_INIT(&(req->tasks)); |
191 | 228 | #endif |
253 | 290 | continue; |
254 | 291 | |
255 | 292 | count++; |
256 | http_process_request(req, 0); | |
293 | http_process_request(req); | |
257 | 294 | } |
258 | 295 | } |
259 | 296 | |
260 | 297 | void |
261 | http_process_request(struct http_request *req, int retry_only) | |
262 | { | |
263 | struct kore_module_handle *hdlr; | |
264 | int r, (*cb)(struct http_request *); | |
298 | http_process_request(struct http_request *req) | |
299 | { | |
300 | int r, (*cb)(struct http_request *); | |
265 | 301 | |
266 | 302 | kore_debug("http_process_request: %p->%p (%s)", |
267 | 303 | req->owner, req, req->path); |
268 | 304 | |
269 | if (req->flags & HTTP_REQUEST_DELETE) | |
305 | if (req->flags & HTTP_REQUEST_DELETE || req->hdlr == NULL) | |
270 | 306 | return; |
271 | 307 | |
272 | if (req->hdlr != NULL) | |
273 | hdlr = req->hdlr; | |
308 | req->start = kore_time_ms(); | |
309 | if (req->hdlr->auth != NULL && !(req->flags & HTTP_REQUEST_AUTHED)) | |
310 | r = kore_auth_run(req, req->hdlr->auth); | |
274 | 311 | else |
275 | hdlr = kore_module_handler_find(req->host, req->path); | |
276 | ||
277 | req->start = kore_time_ms(); | |
278 | if (hdlr == NULL) { | |
279 | r = http_generic_404(req); | |
280 | } else { | |
281 | if (req->hdlr != hdlr && hdlr->auth != NULL) | |
282 | r = kore_auth_run(req, hdlr->auth); | |
283 | else | |
284 | r = KORE_RESULT_OK; | |
285 | ||
286 | switch (r) { | |
287 | case KORE_RESULT_OK: | |
288 | req->hdlr = hdlr; | |
289 | cb = hdlr->addr; | |
290 | worker->active_hdlr = hdlr; | |
291 | r = cb(req); | |
292 | worker->active_hdlr = NULL; | |
293 | break; | |
294 | case KORE_RESULT_RETRY: | |
295 | break; | |
296 | case KORE_RESULT_ERROR: | |
297 | /* | |
298 | * Set r to KORE_RESULT_OK so we can properly | |
299 | * flush the result from kore_auth_run(). | |
300 | */ | |
301 | r = KORE_RESULT_OK; | |
302 | break; | |
303 | default: | |
304 | fatal("kore_auth() returned unknown %d", r); | |
305 | } | |
312 | r = KORE_RESULT_OK; | |
313 | ||
314 | switch (r) { | |
315 | case KORE_RESULT_OK: | |
316 | *(void **)&(cb) = req->hdlr->addr; | |
317 | worker->active_hdlr = req->hdlr; | |
318 | r = cb(req); | |
319 | worker->active_hdlr = NULL; | |
320 | break; | |
321 | case KORE_RESULT_RETRY: | |
322 | break; | |
323 | case KORE_RESULT_ERROR: | |
324 | /* | |
325 | * Set r to KORE_RESULT_OK so we can properly | |
326 | * flush the result from kore_auth_run(). | |
327 | */ | |
328 | r = KORE_RESULT_OK; | |
329 | break; | |
330 | default: | |
331 | fatal("kore_auth() returned unknown %d", r); | |
306 | 332 | } |
307 | 333 | req->end = kore_time_ms(); |
308 | 334 | req->total += req->end - req->start; |
309 | ||
310 | if (retry_only == 1 && r != KORE_RESULT_RETRY) | |
311 | fatal("http_process_request: expected RETRY but got %d", r); | |
312 | 335 | |
313 | 336 | switch (r) { |
314 | 337 | case KORE_RESULT_OK: |
325 | 348 | fatal("A page handler returned an unknown result: %d", r); |
326 | 349 | } |
327 | 350 | |
328 | if (hdlr != NULL && hdlr->dom->accesslog != -1) | |
351 | if (req->hdlr->dom->accesslog != -1) | |
329 | 352 | kore_accesslog(req); |
330 | 353 | |
331 | 354 | req->flags |= HTTP_REQUEST_DELETE; |
401 | 424 | next = TAILQ_NEXT(hdr, list); |
402 | 425 | |
403 | 426 | TAILQ_REMOVE(&(req->resp_headers), hdr, list); |
404 | kore_mem_free(hdr->header); | |
405 | kore_mem_free(hdr->value); | |
427 | kore_free(hdr->header); | |
428 | kore_free(hdr->value); | |
406 | 429 | kore_pool_put(&http_header_pool, hdr); |
407 | 430 | } |
408 | 431 | |
410 | 433 | next = TAILQ_NEXT(hdr, list); |
411 | 434 | |
412 | 435 | TAILQ_REMOVE(&(req->req_headers), hdr, list); |
413 | kore_mem_free(hdr->header); | |
414 | kore_mem_free(hdr->value); | |
436 | kore_free(hdr->header); | |
437 | kore_free(hdr->value); | |
415 | 438 | kore_pool_put(&http_header_pool, hdr); |
416 | 439 | } |
417 | 440 | |
419 | 442 | qnext = TAILQ_NEXT(q, list); |
420 | 443 | |
421 | 444 | TAILQ_REMOVE(&(req->arguments), q, list); |
422 | kore_mem_free(q->name); | |
423 | ||
424 | if (q->value != NULL) | |
425 | kore_mem_free(q->value); | |
445 | kore_free(q->name); | |
426 | 446 | if (q->s_value != NULL) |
427 | kore_mem_free(q->s_value); | |
428 | ||
429 | kore_mem_free(q); | |
447 | kore_free(q->s_value); | |
448 | kore_free(q); | |
430 | 449 | } |
431 | 450 | |
432 | 451 | for (f = TAILQ_FIRST(&(req->files)); f != NULL; f = fnext) { |
433 | 452 | fnext = TAILQ_NEXT(f, list); |
434 | 453 | TAILQ_REMOVE(&(req->files), f, list); |
435 | 454 | |
436 | kore_mem_free(f->filename); | |
437 | kore_mem_free(f->name); | |
438 | kore_mem_free(f); | |
455 | kore_free(f->filename); | |
456 | kore_free(f->name); | |
457 | kore_free(f); | |
439 | 458 | } |
440 | 459 | |
441 | 460 | if (req->http_body != NULL) |
442 | 461 | kore_buf_free(req->http_body); |
443 | if (req->multipart_body != NULL) | |
444 | kore_mem_free(req->multipart_body); | |
445 | ||
446 | if (req->agent != NULL) | |
447 | kore_mem_free(req->agent); | |
462 | ||
463 | if (req->http_body_fd != -1) | |
464 | (void)close(req->http_body_fd); | |
465 | ||
466 | if (req->http_body_path != NULL) { | |
467 | if (unlink(req->http_body_path) == -1) { | |
468 | kore_log(LOG_NOTICE, "failed to unlink %s: %s", | |
469 | req->http_body_path, errno_s); | |
470 | } | |
471 | kore_pool_put(&http_body_path, req->http_body_path); | |
472 | } | |
473 | ||
448 | 474 | if (req->hdlr_extra != NULL && |
449 | 475 | !(req->flags & HTTP_REQUEST_RETAIN_EXTRA)) |
450 | kore_mem_free(req->hdlr_extra); | |
476 | kore_free(req->hdlr_extra); | |
451 | 477 | |
452 | 478 | kore_pool_put(&http_request_pool, req); |
453 | 479 | http_request_count--; |
454 | 480 | } |
455 | 481 | |
456 | 482 | void |
457 | http_response(struct http_request *req, int status, void *d, u_int32_t l) | |
458 | { | |
459 | kore_debug("http_response(%p, %d, %p, %d)", req, status, d, l); | |
483 | http_response(struct http_request *req, int status, const void *d, size_t l) | |
484 | { | |
485 | kore_debug("http_response(%p, %d, %p, %zu)", req, status, d, l); | |
460 | 486 | |
461 | 487 | req->status = status; |
462 | 488 | |
463 | 489 | switch (req->owner->proto) { |
464 | case CONN_PROTO_SPDY: | |
465 | http_response_spdy(req, req->owner, req->stream, status, d, l); | |
466 | break; | |
467 | 490 | case CONN_PROTO_HTTP: |
491 | case CONN_PROTO_WEBSOCKET: | |
468 | 492 | http_response_normal(req, req->owner, status, d, l); |
469 | 493 | break; |
470 | 494 | default: |
475 | 499 | |
476 | 500 | void |
477 | 501 | http_response_stream(struct http_request *req, int status, void *base, |
478 | u_int64_t len, int (*cb)(struct netbuf *), void *arg) | |
502 | size_t len, int (*cb)(struct netbuf *), void *arg) | |
479 | 503 | { |
480 | 504 | struct netbuf *nb; |
481 | 505 | |
482 | 506 | req->status = status; |
483 | 507 | |
484 | 508 | switch (req->owner->proto) { |
485 | case CONN_PROTO_SPDY: | |
486 | http_response_spdy(req, req->owner, | |
487 | req->stream, status, NULL, len); | |
488 | break; | |
489 | 509 | case CONN_PROTO_HTTP: |
490 | 510 | http_response_normal(req, req->owner, status, NULL, len); |
491 | 511 | break; |
495 | 515 | } |
496 | 516 | |
497 | 517 | if (req->method != HTTP_METHOD_HEAD) { |
498 | net_send_stream(req->owner, base, len, req->stream, cb, &nb); | |
518 | net_send_stream(req->owner, base, len, cb, &nb); | |
499 | 519 | nb->extra = arg; |
500 | 520 | } |
501 | 521 | } |
503 | 523 | int |
504 | 524 | http_request_header(struct http_request *req, const char *header, char **out) |
505 | 525 | { |
506 | int r; | |
507 | 526 | struct http_header *hdr; |
508 | 527 | |
509 | if (req->owner->proto == CONN_PROTO_SPDY) { | |
510 | r = spdy_stream_get_header(req->stream->hblock, header, out); | |
511 | } else { | |
512 | TAILQ_FOREACH(hdr, &(req->req_headers), list) { | |
513 | if (!strcasecmp(hdr->header, header)) { | |
514 | r = strlen(hdr->value) + 1; | |
515 | *out = kore_malloc(r); | |
516 | kore_strlcpy(*out, hdr->value, r); | |
517 | return (KORE_RESULT_OK); | |
518 | } | |
519 | } | |
520 | ||
521 | r = KORE_RESULT_ERROR; | |
522 | } | |
523 | ||
524 | return (r); | |
528 | TAILQ_FOREACH(hdr, &(req->req_headers), list) { | |
529 | if (!strcasecmp(hdr->header, header)) { | |
530 | *out = hdr->value; | |
531 | return (KORE_RESULT_OK); | |
532 | } | |
533 | } | |
534 | ||
535 | return (KORE_RESULT_ERROR); | |
525 | 536 | } |
526 | 537 | |
527 | 538 | int |
528 | 539 | http_header_recv(struct netbuf *nb) |
529 | 540 | { |
530 | 541 | size_t len; |
531 | u_int64_t clen; | |
542 | ssize_t ret; | |
532 | 543 | struct http_header *hdr; |
533 | 544 | struct http_request *req; |
545 | u_int64_t bytes_left; | |
534 | 546 | u_int8_t *end_headers; |
535 | int h, i, v, skip, bytes_left; | |
547 | int h, i, v, skip, l; | |
536 | 548 | char *request[4], *host[3], *hbuf; |
537 | 549 | char *p, *headers[HTTP_REQ_HEADER_MAX]; |
538 | 550 | struct connection *c = (struct connection *)nb->owner; |
553 | 565 | |
554 | 566 | *end_headers = '\0'; |
555 | 567 | end_headers += skip; |
556 | nb->flags |= NETBUF_FORCE_REMOVE; | |
557 | 568 | len = end_headers - nb->buf; |
558 | 569 | hbuf = (char *)nb->buf; |
559 | 570 | |
560 | 571 | h = kore_split_string(hbuf, "\r\n", headers, HTTP_REQ_HEADER_MAX); |
561 | 572 | if (h < 2) { |
562 | http_error_response(c, NULL, 400); | |
573 | http_error_response(c, 400); | |
563 | 574 | return (KORE_RESULT_OK); |
564 | 575 | } |
565 | 576 | |
566 | 577 | v = kore_split_string(headers[0], " ", request, 4); |
567 | 578 | if (v != 3) { |
568 | http_error_response(c, NULL, 400); | |
579 | http_error_response(c, 400); | |
569 | 580 | return (KORE_RESULT_OK); |
570 | 581 | } |
571 | 582 | |
577 | 588 | |
578 | 589 | v = kore_split_string(headers[i], ":", host, 3); |
579 | 590 | if (v != 2) { |
580 | http_error_response(c, NULL, 400); | |
591 | http_error_response(c, 400); | |
581 | 592 | return (KORE_RESULT_OK); |
582 | 593 | } |
583 | 594 | |
584 | 595 | if ((host[1] - host[0]) != 5 || |
585 | 596 | strncasecmp(host[0], "host", 4) || host[1] == '\0') { |
586 | http_error_response(c, NULL, 400); | |
597 | http_error_response(c, 400); | |
587 | 598 | return (KORE_RESULT_OK); |
588 | 599 | } |
589 | 600 | |
593 | 604 | } |
594 | 605 | |
595 | 606 | if (host[0] == NULL) { |
596 | http_error_response(c, NULL, 400); | |
607 | http_error_response(c, 400); | |
597 | 608 | return (KORE_RESULT_OK); |
598 | 609 | } |
599 | 610 | |
600 | if (!http_request_new(c, NULL, host[1], | |
611 | if (!http_request_new(c, host[1], | |
601 | 612 | request[0], request[1], request[2], &req)) |
602 | 613 | return (KORE_RESULT_OK); |
603 | 614 | |
621 | 632 | |
622 | 633 | if (req->agent == NULL && |
623 | 634 | !strcasecmp(hdr->header, "user-agent")) |
624 | req->agent = kore_strdup(hdr->value); | |
635 | req->agent = hdr->value; | |
625 | 636 | } |
626 | 637 | |
627 | 638 | if (req->flags & HTTP_REQUEST_EXPECT_BODY) { |
639 | if (http_body_max == 0) { | |
640 | req->flags |= HTTP_REQUEST_DELETE; | |
641 | http_error_response(req->owner, 405); | |
642 | return (KORE_RESULT_OK); | |
643 | } | |
644 | ||
628 | 645 | if (!http_request_header(req, "content-length", &p)) { |
629 | 646 | kore_debug("expected body but no content-length"); |
630 | 647 | req->flags |= HTTP_REQUEST_DELETE; |
631 | http_error_response(req->owner, NULL, 411); | |
632 | return (KORE_RESULT_OK); | |
633 | } | |
634 | ||
635 | clen = kore_strtonum(p, 10, 0, LONG_MAX, &v); | |
648 | http_error_response(req->owner, 411); | |
649 | return (KORE_RESULT_OK); | |
650 | } | |
651 | ||
652 | req->content_length = kore_strtonum(p, 10, 0, LONG_MAX, &v); | |
636 | 653 | if (v == KORE_RESULT_ERROR) { |
637 | 654 | kore_debug("content-length invalid: %s", p); |
638 | kore_mem_free(p); | |
639 | 655 | req->flags |= HTTP_REQUEST_DELETE; |
640 | http_error_response(req->owner, NULL, 411); | |
641 | return (KORE_RESULT_OK); | |
642 | } | |
643 | ||
644 | kore_mem_free(p); | |
645 | ||
646 | if (clen == 0) { | |
656 | http_error_response(req->owner, 411); | |
657 | return (KORE_RESULT_OK); | |
658 | } | |
659 | ||
660 | if (req->content_length == 0) { | |
647 | 661 | req->flags |= HTTP_REQUEST_COMPLETE; |
648 | 662 | req->flags &= ~HTTP_REQUEST_EXPECT_BODY; |
649 | 663 | return (KORE_RESULT_OK); |
650 | 664 | } |
651 | 665 | |
652 | if (clen > http_body_max) { | |
666 | if (req->content_length > http_body_max) { | |
653 | 667 | kore_log(LOG_NOTICE, "body too large (%ld > %ld)", |
654 | clen, http_body_max); | |
668 | req->content_length, http_body_max); | |
655 | 669 | req->flags |= HTTP_REQUEST_DELETE; |
656 | http_error_response(req->owner, NULL, 411); | |
657 | return (KORE_RESULT_OK); | |
658 | } | |
659 | ||
660 | req->http_body = kore_buf_create(clen); | |
661 | kore_buf_append(req->http_body, end_headers, | |
662 | (nb->s_off - len)); | |
663 | ||
664 | bytes_left = clen - (nb->s_off - len); | |
670 | http_error_response(req->owner, 413); | |
671 | return (KORE_RESULT_OK); | |
672 | } | |
673 | ||
674 | req->http_body_length = req->content_length; | |
675 | ||
676 | if (http_body_disk_offload > 0 && | |
677 | req->content_length > http_body_disk_offload) { | |
678 | req->http_body_path = kore_pool_get(&http_body_path); | |
679 | l = snprintf(req->http_body_path, HTTP_BODY_PATH_MAX, | |
680 | "%s/http_body.XXXXXX", http_body_disk_path); | |
681 | if (l == -1 || (size_t)l >= HTTP_BODY_PATH_MAX) { | |
682 | req->flags |= HTTP_REQUEST_DELETE; | |
683 | http_error_response(req->owner, 500); | |
684 | return (KORE_RESULT_ERROR); | |
685 | } | |
686 | ||
687 | req->http_body = NULL; | |
688 | req->http_body_fd = mkstemp(req->http_body_path); | |
689 | if (req->http_body_fd == -1) { | |
690 | req->flags |= HTTP_REQUEST_DELETE; | |
691 | http_error_response(req->owner, 500); | |
692 | return (KORE_RESULT_OK); | |
693 | } | |
694 | ||
695 | ret = write(req->http_body_fd, | |
696 | end_headers, (nb->s_off - len)); | |
697 | if (ret == -1 || (size_t)ret != (nb->s_off - len)) { | |
698 | req->flags |= HTTP_REQUEST_DELETE; | |
699 | http_error_response(req->owner, 500); | |
700 | return (KORE_RESULT_OK); | |
701 | } | |
702 | } else { | |
703 | req->http_body_fd = -1; | |
704 | req->http_body = kore_buf_alloc(req->content_length); | |
705 | kore_buf_append(req->http_body, end_headers, | |
706 | (nb->s_off - len)); | |
707 | } | |
708 | ||
709 | bytes_left = req->content_length - (nb->s_off - len); | |
665 | 710 | if (bytes_left > 0) { |
666 | 711 | kore_debug("%ld/%ld (%ld - %ld) more bytes for body", |
667 | bytes_left, clen, nb->s_off, len); | |
668 | net_recv_reset(c, bytes_left, http_body_recv); | |
712 | bytes_left, req->content_length, nb->s_off, len); | |
713 | net_recv_reset(c, | |
714 | MIN(bytes_left, NETBUF_SEND_PAYLOAD_MAX), | |
715 | http_body_recv); | |
669 | 716 | c->rnb->extra = req; |
670 | c->rnb->flags &= ~NETBUF_CALL_CB_ALWAYS; | |
717 | http_request_sleep(req); | |
718 | req->content_length = bytes_left; | |
671 | 719 | } else if (bytes_left == 0) { |
672 | 720 | req->flags |= HTTP_REQUEST_COMPLETE; |
673 | 721 | req->flags &= ~HTTP_REQUEST_EXPECT_BODY; |
722 | if (!http_body_rewind(req)) { | |
723 | req->flags |= HTTP_REQUEST_DELETE; | |
724 | http_error_response(req->owner, 500); | |
725 | return (KORE_RESULT_OK); | |
726 | } | |
674 | 727 | } else { |
675 | kore_debug("bytes_left would become zero (%ld)", clen); | |
676 | http_error_response(req->owner, NULL, 500); | |
728 | http_error_response(req->owner, 500); | |
677 | 729 | } |
678 | 730 | } |
679 | 731 | |
680 | 732 | return (KORE_RESULT_OK); |
681 | } | |
682 | ||
683 | int | |
684 | http_populate_arguments(struct http_request *req) | |
685 | { | |
686 | u_int32_t len; | |
687 | int i, v, c, count; | |
688 | char *query, *args[HTTP_MAX_QUERY_ARGS], *val[3]; | |
689 | ||
690 | if (req->method == HTTP_METHOD_POST) { | |
691 | if (req->http_body == NULL) | |
692 | return (0); | |
693 | query = http_body_text(req); | |
694 | } else { | |
695 | if (req->query_string == NULL) | |
696 | return (0); | |
697 | query = kore_strdup(req->query_string); | |
698 | } | |
699 | ||
700 | count = 0; | |
701 | v = kore_split_string(query, "&", args, HTTP_MAX_QUERY_ARGS); | |
702 | for (i = 0; i < v; i++) { | |
703 | c = kore_split_string(args[i], "=", val, 3); | |
704 | if (c != 1 && c != 2) { | |
705 | kore_debug("malformed query argument"); | |
706 | continue; | |
707 | } | |
708 | ||
709 | if (val[1] != NULL) { | |
710 | len = strlen(val[1]); | |
711 | http_argument_add(req, val[0], val[1], | |
712 | len, HTTP_ARG_TYPE_STRING); | |
713 | count++; | |
714 | } | |
715 | } | |
716 | ||
717 | kore_mem_free(query); | |
718 | return (count); | |
719 | 733 | } |
720 | 734 | |
721 | 735 | int |
722 | 736 | http_argument_get(struct http_request *req, const char *name, |
723 | void **out, void *nout, u_int32_t *len, int type) | |
737 | void **out, void *nout, int type) | |
724 | 738 | { |
725 | 739 | struct http_arg *q; |
726 | 740 | |
727 | if (len != NULL) | |
728 | *len = 0; | |
729 | ||
730 | 741 | TAILQ_FOREACH(q, &(req->arguments), list) { |
731 | if (!strcmp(q->name, name)) { | |
732 | switch (type) { | |
733 | case HTTP_ARG_TYPE_RAW: | |
734 | if (len != NULL) | |
735 | *len = q->len; | |
736 | *out = q->value; | |
737 | return (KORE_RESULT_OK); | |
738 | case HTTP_ARG_TYPE_BYTE: | |
739 | COPY_ARG_TYPE(*(u_int8_t *)q->value, | |
740 | len, u_int8_t); | |
741 | return (KORE_RESULT_OK); | |
742 | case HTTP_ARG_TYPE_INT16: | |
743 | COPY_AS_INTTYPE(SHRT_MIN, SHRT_MAX, int16_t); | |
744 | return (KORE_RESULT_OK); | |
745 | case HTTP_ARG_TYPE_UINT16: | |
746 | COPY_AS_INTTYPE(0, USHRT_MAX, u_int16_t); | |
747 | return (KORE_RESULT_OK); | |
748 | case HTTP_ARG_TYPE_INT32: | |
749 | COPY_AS_INTTYPE(INT_MIN, INT_MAX, int32_t); | |
750 | return (KORE_RESULT_OK); | |
751 | case HTTP_ARG_TYPE_UINT32: | |
752 | COPY_AS_INTTYPE(0, UINT_MAX, u_int32_t); | |
753 | return (KORE_RESULT_OK); | |
754 | case HTTP_ARG_TYPE_INT64: | |
755 | COPY_AS_INTTYPE_64(int64_t, 1); | |
756 | return (KORE_RESULT_OK); | |
757 | case HTTP_ARG_TYPE_UINT64: | |
758 | COPY_AS_INTTYPE_64(u_int64_t, 0); | |
759 | return (KORE_RESULT_OK); | |
760 | case HTTP_ARG_TYPE_STRING: | |
761 | CACHE_STRING(); | |
762 | *out = q->s_value; | |
763 | if (len != NULL) | |
764 | *len = q->s_len - 1; | |
765 | return (KORE_RESULT_OK); | |
766 | default: | |
767 | return (KORE_RESULT_ERROR); | |
768 | } | |
769 | } | |
742 | if (strcmp(q->name, name)) | |
743 | continue; | |
744 | ||
745 | switch (type) { | |
746 | case HTTP_ARG_TYPE_RAW: | |
747 | *out = q->s_value; | |
748 | return (KORE_RESULT_OK); | |
749 | case HTTP_ARG_TYPE_BYTE: | |
750 | COPY_ARG_TYPE(*(u_int8_t *)q->s_value, u_int8_t); | |
751 | return (KORE_RESULT_OK); | |
752 | case HTTP_ARG_TYPE_INT16: | |
753 | COPY_AS_INTTYPE(SHRT_MIN, SHRT_MAX, int16_t); | |
754 | return (KORE_RESULT_OK); | |
755 | case HTTP_ARG_TYPE_UINT16: | |
756 | COPY_AS_INTTYPE(0, USHRT_MAX, u_int16_t); | |
757 | return (KORE_RESULT_OK); | |
758 | case HTTP_ARG_TYPE_INT32: | |
759 | COPY_AS_INTTYPE(INT_MIN, INT_MAX, int32_t); | |
760 | return (KORE_RESULT_OK); | |
761 | case HTTP_ARG_TYPE_UINT32: | |
762 | COPY_AS_INTTYPE(0, UINT_MAX, u_int32_t); | |
763 | return (KORE_RESULT_OK); | |
764 | case HTTP_ARG_TYPE_INT64: | |
765 | COPY_AS_INTTYPE_64(int64_t, 1); | |
766 | return (KORE_RESULT_OK); | |
767 | case HTTP_ARG_TYPE_UINT64: | |
768 | COPY_AS_INTTYPE_64(u_int64_t, 0); | |
769 | return (KORE_RESULT_OK); | |
770 | case HTTP_ARG_TYPE_STRING: | |
771 | *out = q->s_value; | |
772 | return (KORE_RESULT_OK); | |
773 | default: | |
774 | break; | |
775 | } | |
776 | ||
777 | return (KORE_RESULT_ERROR); | |
770 | 778 | } |
771 | 779 | |
772 | 780 | return (KORE_RESULT_ERROR); |
820 | 828 | return (KORE_RESULT_OK); |
821 | 829 | } |
822 | 830 | |
823 | int | |
824 | http_file_lookup(struct http_request *req, const char *name, char **fname, | |
825 | u_int8_t **data, u_int32_t *len) | |
831 | struct http_file * | |
832 | http_file_lookup(struct http_request *req, const char *name) | |
826 | 833 | { |
827 | 834 | struct http_file *f; |
828 | 835 | |
829 | 836 | TAILQ_FOREACH(f, &(req->files), list) { |
830 | if (!strcmp(f->name, name)) { | |
831 | *len = f->len; | |
832 | *data = f->data; | |
833 | *fname = f->filename; | |
834 | return (KORE_RESULT_OK); | |
835 | } | |
836 | } | |
837 | ||
838 | return (KORE_RESULT_ERROR); | |
839 | } | |
840 | ||
841 | int | |
842 | http_populate_multipart_form(struct http_request *req, int *v) | |
843 | { | |
844 | int h, i, c, l; | |
845 | u_int32_t blen, slen, len; | |
846 | u_int8_t *s, *end, *e, *end_headers, *data; | |
847 | char *d, *val, *type, *boundary, *fname; | |
848 | char *headers[5], *args[5], *opt[5], *name; | |
849 | ||
850 | *v = 0; | |
837 | if (!strcmp(f->name, name)) | |
838 | return (f); | |
839 | } | |
840 | ||
841 | return (NULL); | |
842 | } | |
843 | ||
844 | ssize_t | |
845 | http_file_read(struct http_file *file, void *buf, size_t len) | |
846 | { | |
847 | ssize_t ret; | |
848 | size_t toread, off; | |
849 | ||
850 | if (file->length < file->offset) | |
851 | return (-1); | |
852 | if ((file->offset + len) < file->offset) | |
853 | return (-1); | |
854 | if ((file->position + file->offset) < file->position) | |
855 | return (-1); | |
856 | ||
857 | off = file->position + file->offset; | |
858 | toread = MIN(len, (file->length - file->offset)); | |
859 | if (toread <= 0) | |
860 | return (0); | |
861 | ||
862 | if (file->req->http_body_fd != -1) { | |
863 | if (lseek(file->req->http_body_fd, off, SEEK_SET) == -1) { | |
864 | kore_log(LOG_ERR, "http_file_read: lseek(%s): %s", | |
865 | file->req->http_body_path, errno_s); | |
866 | return (-1); | |
867 | } | |
868 | ||
869 | for (;;) { | |
870 | ret = read(file->req->http_body_fd, buf, toread); | |
871 | if (ret == -1) { | |
872 | if (errno == EINTR) | |
873 | continue; | |
874 | kore_log(LOG_ERR, "failed to read %s: %s", | |
875 | file->req->http_body_path, errno_s); | |
876 | return (-1); | |
877 | } | |
878 | if (ret == 0) | |
879 | return (0); | |
880 | break; | |
881 | } | |
882 | } else if (file->req->http_body != NULL) { | |
883 | if (off > file->req->http_body->length) | |
884 | return (0); | |
885 | memcpy(buf, file->req->http_body->data + off, toread); | |
886 | ret = toread; | |
887 | } else { | |
888 | kore_log(LOG_ERR, "http_file_read: called without body"); | |
889 | return (-1); | |
890 | } | |
891 | ||
892 | file->offset += (size_t)ret; | |
893 | return (ret); | |
894 | } | |
895 | ||
896 | void | |
897 | http_file_rewind(struct http_file *file) | |
898 | { | |
899 | file->offset = 0; | |
900 | } | |
901 | ||
902 | void | |
903 | http_populate_post(struct http_request *req) | |
904 | { | |
905 | ssize_t ret; | |
906 | int i, v; | |
907 | struct kore_buf *body; | |
908 | char data[BUFSIZ]; | |
909 | char *args[HTTP_MAX_QUERY_ARGS], *val[3], *string; | |
851 | 910 | |
852 | 911 | if (req->method != HTTP_METHOD_POST) |
853 | return (KORE_RESULT_ERROR); | |
912 | return; | |
913 | ||
914 | if (req->http_body != NULL) { | |
915 | body = NULL; | |
916 | req->http_body->offset = req->content_length; | |
917 | string = kore_buf_stringify(req->http_body, NULL); | |
918 | } else { | |
919 | body = kore_buf_alloc(128); | |
920 | for (;;) { | |
921 | ret = http_body_read(req, data, sizeof(data)); | |
922 | if (ret == -1) | |
923 | goto out; | |
924 | if (ret == 0) | |
925 | break; | |
926 | kore_buf_append(body, data, ret); | |
927 | } | |
928 | string = kore_buf_stringify(body, NULL); | |
929 | } | |
930 | ||
931 | v = kore_split_string(string, "&", args, HTTP_MAX_QUERY_ARGS); | |
932 | for (i = 0; i < v; i++) { | |
933 | kore_split_string(args[i], "=", val, 3); | |
934 | if (val[0] != NULL && val[1] != NULL) | |
935 | http_argument_add(req, val[0], val[1]); | |
936 | } | |
937 | ||
938 | out: | |
939 | if (body != NULL) | |
940 | kore_buf_free(body); | |
941 | } | |
942 | ||
943 | void | |
944 | http_populate_get(struct http_request *req) | |
945 | { | |
946 | int i, v; | |
947 | char *query, *args[HTTP_MAX_QUERY_ARGS], *val[3]; | |
948 | ||
949 | if (req->method != HTTP_METHOD_GET || req->query_string == NULL) | |
950 | return; | |
951 | ||
952 | query = kore_strdup(req->query_string); | |
953 | v = kore_split_string(query, "&", args, HTTP_MAX_QUERY_ARGS); | |
954 | for (i = 0; i < v; i++) { | |
955 | kore_split_string(args[i], "=", val, 3); | |
956 | if (val[0] != NULL && val[1] != NULL) | |
957 | http_argument_add(req, val[0], val[1]); | |
958 | } | |
959 | ||
960 | kore_free(query); | |
961 | } | |
962 | ||
963 | void | |
964 | http_populate_multipart_form(struct http_request *req) | |
965 | { | |
966 | int h, blen; | |
967 | struct kore_buf *in, *out; | |
968 | char *type, *val, *args[3]; | |
969 | char boundary[HTTP_BOUNDARY_MAX]; | |
970 | ||
971 | if (req->method != HTTP_METHOD_POST) | |
972 | return; | |
854 | 973 | |
855 | 974 | if (!http_request_header(req, "content-type", &type)) |
856 | return (KORE_RESULT_ERROR); | |
975 | return; | |
857 | 976 | |
858 | 977 | h = kore_split_string(type, ";", args, 3); |
859 | if (h != 2) { | |
860 | kore_mem_free(type); | |
861 | return (KORE_RESULT_ERROR); | |
862 | } | |
863 | ||
864 | if (strcasecmp(args[0], "multipart/form-data")) { | |
865 | kore_mem_free(type); | |
866 | return (KORE_RESULT_ERROR); | |
867 | } | |
868 | ||
869 | if ((val = strchr(args[1], '=')) == NULL) { | |
870 | kore_mem_free(type); | |
871 | return (KORE_RESULT_ERROR); | |
872 | } | |
978 | if (h != 2) | |
979 | return; | |
980 | ||
981 | if (strcasecmp(args[0], "multipart/form-data")) | |
982 | return; | |
983 | ||
984 | if ((val = strchr(args[1], '=')) == NULL) | |
985 | return; | |
873 | 986 | |
874 | 987 | val++; |
875 | slen = strlen(val); | |
876 | boundary = kore_malloc(slen + 3); | |
877 | if (!kore_snprintf(boundary, slen + 3, &l, "--%s", val)) { | |
878 | kore_mem_free(boundary); | |
879 | kore_mem_free(type); | |
880 | return (KORE_RESULT_ERROR); | |
881 | } | |
882 | ||
883 | slen = l; | |
884 | kore_mem_free(type); | |
885 | ||
886 | req->multipart_body = http_body_bytes(req, &blen); | |
887 | if (slen < 3 || blen < (slen * 2)) { | |
888 | kore_mem_free(boundary); | |
889 | return (KORE_RESULT_ERROR); | |
890 | } | |
891 | ||
892 | end = req->multipart_body + blen - 2; | |
893 | if (end < req->multipart_body || (end - 2) < req->multipart_body) { | |
894 | kore_mem_free(boundary); | |
895 | return (KORE_RESULT_ERROR); | |
896 | } | |
897 | ||
898 | if (memcmp((end - slen - 2), boundary, slen) || | |
899 | memcmp((end - 2), "--", 2)) { | |
900 | kore_mem_free(boundary); | |
901 | return (KORE_RESULT_ERROR); | |
902 | } | |
903 | ||
904 | s = req->multipart_body + slen + 2; | |
905 | while (s < end) { | |
906 | e = kore_mem_find(s, end - s, boundary, slen); | |
907 | if (e == NULL) { | |
908 | kore_mem_free(boundary); | |
909 | return (KORE_RESULT_ERROR); | |
910 | } | |
911 | ||
912 | *(e - 2) = '\0'; | |
913 | end_headers = kore_mem_find(s, (e - 2) - s, "\r\n\r\n", 4); | |
914 | if (end_headers == NULL) { | |
915 | kore_mem_free(boundary); | |
916 | return (KORE_RESULT_ERROR); | |
917 | } | |
918 | ||
919 | *end_headers = '\0'; | |
920 | data = end_headers + 4; | |
921 | ||
922 | h = kore_split_string((char *)s, "\r\n", headers, 5); | |
923 | for (i = 0; i < h; i++) { | |
924 | c = kore_split_string(headers[i], ":", args, 5); | |
925 | if (c != 2) | |
926 | continue; | |
927 | ||
928 | /* Ignore other headers for now. */ | |
929 | if (strcasecmp(args[0], "content-disposition")) | |
930 | continue; | |
931 | ||
932 | for (d = args[1]; isspace(*d); d++) | |
933 | ; | |
934 | ||
935 | c = kore_split_string(d, ";", opt, 5); | |
936 | if (c < 2) | |
937 | continue; | |
938 | ||
939 | if (strcasecmp(opt[0], "form-data")) | |
940 | continue; | |
941 | ||
942 | if ((val = strchr(opt[1], '=')) == NULL) | |
943 | continue; | |
944 | if (strlen(val) < 3) | |
945 | continue; | |
946 | ||
947 | val++; | |
948 | kore_strip_chars(val, '"', &name); | |
949 | ||
950 | if (opt[2] == NULL) { | |
951 | *v = *v + 1; | |
952 | http_argument_add(req, name, | |
953 | data, (e - 2) - data, HTTP_ARG_TYPE_STRING); | |
954 | kore_mem_free(name); | |
955 | continue; | |
988 | blen = snprintf(boundary, sizeof(boundary), "--%s", val); | |
989 | if (blen == -1 || (size_t)blen >= sizeof(boundary)) | |
990 | return; | |
991 | ||
992 | in = kore_buf_alloc(128); | |
993 | out = kore_buf_alloc(128); | |
994 | ||
995 | if (!multipart_find_data(in, NULL, NULL, req, boundary, blen)) | |
996 | goto cleanup; | |
997 | ||
998 | for (;;) { | |
999 | if (!multipart_find_data(in, NULL, NULL, req, "\r\n", 2)) | |
1000 | break; | |
1001 | if (in->offset < 4 && req->http_body_length == 0) | |
1002 | break; | |
1003 | if (!multipart_find_data(in, out, NULL, req, "\r\n\r\n", 4)) | |
1004 | break; | |
1005 | if (!multipart_parse_headers(req, in, out, boundary, blen)) | |
1006 | break; | |
1007 | ||
1008 | kore_buf_reset(out); | |
1009 | } | |
1010 | ||
1011 | cleanup: | |
1012 | kore_buf_free(in); | |
1013 | kore_buf_free(out); | |
1014 | } | |
1015 | ||
1016 | ssize_t | |
1017 | http_body_read(struct http_request *req, void *out, size_t len) | |
1018 | { | |
1019 | ssize_t ret; | |
1020 | size_t toread; | |
1021 | ||
1022 | toread = MIN(req->http_body_length, len); | |
1023 | if (toread <= 0) | |
1024 | return (0); | |
1025 | ||
1026 | if (req->http_body_fd != -1) { | |
1027 | for (;;) { | |
1028 | ret = read(req->http_body_fd, out, toread); | |
1029 | if (ret == -1) { | |
1030 | if (errno == EINTR) | |
1031 | continue; | |
1032 | kore_log(LOG_ERR, "failed to read %s: %s", | |
1033 | req->http_body_path, errno_s); | |
1034 | return (-1); | |
956 | 1035 | } |
957 | ||
958 | for (d = opt[2]; isspace(*d); d++) | |
959 | ; | |
960 | ||
961 | len = MIN(strlen("filename="), strlen(d)); | |
962 | if (!strncasecmp(d, "filename=", len)) { | |
963 | if ((val = strchr(d, '=')) == NULL) { | |
964 | kore_mem_free(name); | |
965 | continue; | |
966 | } | |
967 | ||
968 | val++; | |
969 | kore_strip_chars(val, '"', &fname); | |
970 | if (strlen(fname) > 0) { | |
971 | *v = *v + 1; | |
972 | http_file_add(req, name, fname, | |
973 | data, (e - 2) - data); | |
974 | } | |
975 | ||
976 | kore_mem_free(fname); | |
977 | } else { | |
978 | kore_debug("got unknown: %s", opt[2]); | |
979 | } | |
980 | ||
981 | kore_mem_free(name); | |
982 | } | |
983 | ||
984 | s = e + slen + 2; | |
985 | } | |
986 | ||
987 | kore_mem_free(boundary); | |
988 | ||
989 | return (KORE_RESULT_OK); | |
990 | } | |
991 | ||
992 | int | |
993 | http_generic_404(struct http_request *req) | |
994 | { | |
995 | kore_debug("http_generic_404(%s, %d, %s)", | |
996 | req->host, req->method, req->path); | |
997 | ||
998 | http_response(req, 404, NULL, 0); | |
999 | ||
1000 | return (KORE_RESULT_OK); | |
1001 | } | |
1002 | ||
1003 | char * | |
1004 | http_body_text(struct http_request *req) | |
1005 | { | |
1006 | u_int32_t len; | |
1007 | u_int8_t *data; | |
1008 | char *text; | |
1009 | ||
1010 | if (req->http_body == NULL) | |
1011 | return (NULL); | |
1012 | ||
1013 | data = kore_buf_release(req->http_body, &len); | |
1014 | req->http_body = NULL; | |
1015 | len++; | |
1016 | ||
1017 | text = kore_malloc(len); | |
1018 | kore_strlcpy(text, (char *)data, len); | |
1019 | kore_mem_free(data); | |
1020 | ||
1021 | return (text); | |
1022 | } | |
1023 | ||
1024 | u_int8_t * | |
1025 | http_body_bytes(struct http_request *req, u_int32_t *len) | |
1026 | { | |
1027 | u_int8_t *data; | |
1028 | ||
1029 | if (req->http_body == NULL) | |
1030 | return (NULL); | |
1031 | ||
1032 | data = kore_buf_release(req->http_body, len); | |
1033 | req->http_body = NULL; | |
1034 | ||
1035 | return (data); | |
1036 | if (ret == 0) | |
1037 | return (0); | |
1038 | break; | |
1039 | } | |
1040 | } else if (req->http_body != NULL) { | |
1041 | memcpy(out, | |
1042 | (req->http_body->data + req->http_body->offset), toread); | |
1043 | req->http_body->offset += toread; | |
1044 | ret = toread; | |
1045 | } else { | |
1046 | kore_log(LOG_ERR, "http_body_read: called without body"); | |
1047 | return (-1); | |
1048 | } | |
1049 | ||
1050 | req->http_body_length -= (size_t)ret; | |
1051 | req->http_body_offset += (size_t)ret; | |
1052 | ||
1053 | return (ret); | |
1036 | 1054 | } |
1037 | 1055 | |
1038 | 1056 | int |
1074 | 1092 | return (KORE_RESULT_OK); |
1075 | 1093 | } |
1076 | 1094 | |
1095 | static int | |
1096 | multipart_find_data(struct kore_buf *in, struct kore_buf *out, | |
1097 | size_t *olen, struct http_request *req, const void *needle, size_t len) | |
1098 | { | |
1099 | ssize_t ret; | |
1100 | size_t left; | |
1101 | u_int8_t *p, first, data[4096]; | |
1102 | ||
1103 | if (olen != NULL) | |
1104 | *olen = 0; | |
1105 | ||
1106 | first = *(const u_int8_t *)needle; | |
1107 | for (;;) { | |
1108 | if (in->offset < len) { | |
1109 | ret = http_body_read(req, data, sizeof(data)); | |
1110 | if (ret == -1) | |
1111 | return (KORE_RESULT_ERROR); | |
1112 | if (ret == 0) | |
1113 | return (KORE_RESULT_ERROR); | |
1114 | ||
1115 | kore_buf_append(in, data, ret); | |
1116 | continue; | |
1117 | } | |
1118 | ||
1119 | p = kore_mem_find(in->data, in->offset, &first, 1); | |
1120 | if (p == NULL) { | |
1121 | if (out != NULL) | |
1122 | kore_buf_append(out, in->data, in->offset); | |
1123 | if (olen != NULL) | |
1124 | *olen += in->offset; | |
1125 | kore_buf_reset(in); | |
1126 | continue; | |
1127 | } | |
1128 | ||
1129 | left = in->offset - (p - in->data); | |
1130 | if (left < len) { | |
1131 | if (out != NULL) | |
1132 | kore_buf_append(out, in->data, (p - in->data)); | |
1133 | if (olen != NULL) | |
1134 | *olen += (p - in->data); | |
1135 | memmove(in->data, p, left); | |
1136 | in->offset = left; | |
1137 | continue; | |
1138 | } | |
1139 | ||
1140 | if (!memcmp(p, needle, len)) { | |
1141 | if (out != NULL) | |
1142 | kore_buf_append(out, in->data, p - in->data); | |
1143 | if (olen != NULL) | |
1144 | *olen += (p - in->data); | |
1145 | ||
1146 | in->offset = left - len; | |
1147 | if (in->offset > 0) | |
1148 | memmove(in->data, p + len, in->offset); | |
1149 | return (KORE_RESULT_OK); | |
1150 | } | |
1151 | ||
1152 | if (out != NULL) | |
1153 | kore_buf_append(out, in->data, (p - in->data) + 1); | |
1154 | if (olen != NULL) | |
1155 | *olen += (p - in->data) + 1; | |
1156 | ||
1157 | in->offset = left - 1; | |
1158 | if (in->offset > 0) | |
1159 | memmove(in->data, p + 1, in->offset); | |
1160 | } | |
1161 | ||
1162 | return (KORE_RESULT_ERROR); | |
1163 | } | |
1164 | ||
1165 | static int | |
1166 | multipart_parse_headers(struct http_request *req, struct kore_buf *in, | |
1167 | struct kore_buf *hbuf, const char *boundary, const int blen) | |
1168 | { | |
1169 | int h, c, i; | |
1170 | char *headers[5], *args[5], *opt[5]; | |
1171 | char *d, *val, *name, *fname, *string; | |
1172 | ||
1173 | string = kore_buf_stringify(hbuf, NULL); | |
1174 | h = kore_split_string(string, "\r\n", headers, 5); | |
1175 | for (i = 0; i < h; i++) { | |
1176 | c = kore_split_string(headers[i], ":", args, 5); | |
1177 | if (c != 2) | |
1178 | continue; | |
1179 | ||
1180 | /* Ignore other headers for now. */ | |
1181 | if (strcasecmp(args[0], "content-disposition")) | |
1182 | continue; | |
1183 | ||
1184 | for (d = args[1]; isspace(*d); d++) | |
1185 | ; | |
1186 | ||
1187 | c = kore_split_string(d, ";", opt, 5); | |
1188 | if (c < 2) | |
1189 | continue; | |
1190 | ||
1191 | if (strcasecmp(opt[0], "form-data")) | |
1192 | continue; | |
1193 | ||
1194 | if ((val = strchr(opt[1], '=')) == NULL) | |
1195 | continue; | |
1196 | if (strlen(val) < 3) | |
1197 | continue; | |
1198 | ||
1199 | val++; | |
1200 | kore_strip_chars(val, '"', &name); | |
1201 | ||
1202 | if (opt[2] == NULL) { | |
1203 | multipart_add_field(req, in, name, boundary, blen); | |
1204 | kore_free(name); | |
1205 | continue; | |
1206 | } | |
1207 | ||
1208 | for (d = opt[2]; isspace(*d); d++) | |
1209 | ; | |
1210 | ||
1211 | if (!strncasecmp(d, "filename=", 9)) { | |
1212 | if ((val = strchr(d, '=')) == NULL) { | |
1213 | kore_free(name); | |
1214 | continue; | |
1215 | } | |
1216 | ||
1217 | val++; | |
1218 | kore_strip_chars(val, '"', &fname); | |
1219 | if (strlen(fname) > 0) { | |
1220 | multipart_file_add(req, | |
1221 | in, name, fname, boundary, blen); | |
1222 | } | |
1223 | kore_free(fname); | |
1224 | } else { | |
1225 | kore_debug("got unknown: %s", opt[2]); | |
1226 | } | |
1227 | ||
1228 | kore_free(name); | |
1229 | } | |
1230 | ||
1231 | return (KORE_RESULT_OK); | |
1232 | } | |
1233 | ||
1077 | 1234 | static void |
1078 | http_argument_add(struct http_request *req, const char *name, | |
1079 | void *value, u_int32_t len, int type) | |
1235 | multipart_add_field(struct http_request *req, struct kore_buf *in, | |
1236 | const char *name, const char *boundary, const int blen) | |
1237 | { | |
1238 | struct kore_buf *data; | |
1239 | char *string; | |
1240 | ||
1241 | data = kore_buf_alloc(128); | |
1242 | ||
1243 | if (!multipart_find_data(in, data, NULL, req, boundary, blen)) { | |
1244 | kore_buf_free(data); | |
1245 | return; | |
1246 | } | |
1247 | ||
1248 | if (data->offset < 3) { | |
1249 | kore_buf_free(data); | |
1250 | return; | |
1251 | } | |
1252 | ||
1253 | data->offset -= 2; | |
1254 | string = kore_buf_stringify(data, NULL); | |
1255 | http_argument_add(req, name, string); | |
1256 | kore_buf_free(data); | |
1257 | } | |
1258 | ||
1259 | static void | |
1260 | multipart_file_add(struct http_request *req, struct kore_buf *in, | |
1261 | const char *name, const char *fname, const char *boundary, const int blen) | |
1262 | { | |
1263 | struct http_file *f; | |
1264 | size_t position, len; | |
1265 | ||
1266 | position= req->http_body_offset - in->offset; | |
1267 | if (!multipart_find_data(in, NULL, &len, req, boundary, blen)) | |
1268 | return; | |
1269 | ||
1270 | if (len < 3) | |
1271 | return; | |
1272 | len -= 2; | |
1273 | ||
1274 | f = kore_malloc(sizeof(struct http_file)); | |
1275 | f->req = req; | |
1276 | f->offset = 0; | |
1277 | f->length = len; | |
1278 | f->position = position; | |
1279 | f->name = kore_strdup(name); | |
1280 | f->filename = kore_strdup(fname); | |
1281 | ||
1282 | TAILQ_INSERT_TAIL(&(req->files), f, list); | |
1283 | } | |
1284 | ||
1285 | static void | |
1286 | http_argument_add(struct http_request *req, const char *name, char *value) | |
1080 | 1287 | { |
1081 | 1288 | struct http_arg *q; |
1082 | 1289 | struct kore_handler_params *p; |
1083 | 1290 | |
1084 | if (len == 0 || value == NULL) { | |
1085 | kore_debug("http_argument_add: with NULL value"); | |
1086 | return; | |
1087 | } | |
1088 | ||
1089 | 1291 | TAILQ_FOREACH(p, &(req->hdlr->params), list) { |
1090 | 1292 | if (p->method != req->method) |
1091 | 1293 | continue; |
1092 | ||
1093 | if (!strcmp(p->name, name)) { | |
1094 | if (type == HTTP_ARG_TYPE_STRING) { | |
1095 | http_argument_urldecode(value); | |
1096 | len = strlen(value); | |
1097 | } | |
1098 | ||
1099 | if (kore_validator_check(req, p->validator, value)) { | |
1100 | q = kore_malloc(sizeof(struct http_arg)); | |
1101 | q->len = len; | |
1102 | q->s_value = NULL; | |
1103 | q->name = kore_strdup(name); | |
1104 | q->value = kore_malloc(len); | |
1105 | memcpy(q->value, value, len); | |
1106 | TAILQ_INSERT_TAIL(&(req->arguments), q, list); | |
1107 | } | |
1108 | ||
1109 | return; | |
1110 | } | |
1111 | } | |
1112 | } | |
1113 | ||
1114 | static void | |
1115 | http_file_add(struct http_request *req, const char *name, const char *filename, | |
1116 | u_int8_t *data, u_int32_t len) | |
1117 | { | |
1118 | struct http_file *f; | |
1119 | ||
1120 | f = kore_malloc(sizeof(struct http_file)); | |
1121 | f->len = len; | |
1122 | f->data = data; | |
1123 | f->name = kore_strdup(name); | |
1124 | f->filename = kore_strdup(filename); | |
1125 | ||
1126 | TAILQ_INSERT_TAIL(&(req->files), f, list); | |
1294 | if (strcmp(p->name, name)) | |
1295 | continue; | |
1296 | ||
1297 | http_argument_urldecode(value); | |
1298 | if (!kore_validator_check(req, p->validator, value)) | |
1299 | break; | |
1300 | ||
1301 | q = kore_malloc(sizeof(struct http_arg)); | |
1302 | q->name = kore_strdup(name); | |
1303 | q->s_value = kore_strdup(value); | |
1304 | TAILQ_INSERT_TAIL(&(req->arguments), q, list); | |
1305 | break; | |
1306 | } | |
1127 | 1307 | } |
1128 | 1308 | |
1129 | 1309 | static int |
1130 | 1310 | http_body_recv(struct netbuf *nb) |
1131 | 1311 | { |
1312 | ssize_t ret; | |
1313 | u_int64_t bytes_left; | |
1132 | 1314 | struct http_request *req = (struct http_request *)nb->extra; |
1133 | 1315 | |
1134 | kore_buf_append(req->http_body, nb->buf, nb->s_off); | |
1135 | ||
1136 | req->flags |= HTTP_REQUEST_COMPLETE; | |
1137 | req->flags &= ~HTTP_REQUEST_EXPECT_BODY; | |
1138 | ||
1139 | nb->extra = NULL; | |
1140 | kore_debug("received all body data for request %p", req); | |
1316 | if (req->http_body_fd != -1) { | |
1317 | ret = write(req->http_body_fd, nb->buf, nb->s_off); | |
1318 | if (ret == -1 || (size_t)ret != nb->s_off) { | |
1319 | req->flags |= HTTP_REQUEST_DELETE; | |
1320 | http_error_response(req->owner, 500); | |
1321 | return (KORE_RESULT_ERROR); | |
1322 | } | |
1323 | } else if (req->http_body != NULL) { | |
1324 | kore_buf_append(req->http_body, nb->buf, nb->s_off); | |
1325 | } else { | |
1326 | req->flags |= HTTP_REQUEST_DELETE; | |
1327 | http_error_response(req->owner, 500); | |
1328 | return (KORE_RESULT_ERROR); | |
1329 | } | |
1330 | ||
1331 | req->content_length -= nb->s_off; | |
1332 | ||
1333 | if (req->content_length == 0) { | |
1334 | nb->extra = NULL; | |
1335 | http_request_wakeup(req); | |
1336 | req->flags |= HTTP_REQUEST_COMPLETE; | |
1337 | req->flags &= ~HTTP_REQUEST_EXPECT_BODY; | |
1338 | req->content_length = req->http_body_length; | |
1339 | if (!http_body_rewind(req)) { | |
1340 | req->flags |= HTTP_REQUEST_DELETE; | |
1341 | http_error_response(req->owner, 500); | |
1342 | return (KORE_RESULT_ERROR); | |
1343 | } | |
1344 | net_recv_reset(nb->owner, http_header_max, http_header_recv); | |
1345 | } else { | |
1346 | bytes_left = req->content_length; | |
1347 | net_recv_reset(nb->owner, | |
1348 | MIN(bytes_left, NETBUF_SEND_PAYLOAD_MAX), | |
1349 | http_body_recv); | |
1350 | } | |
1141 | 1351 | |
1142 | 1352 | return (KORE_RESULT_OK); |
1143 | 1353 | } |
1144 | 1354 | |
1355 | static int | |
1356 | http_body_rewind(struct http_request *req) | |
1357 | { | |
1358 | if (req->http_body_fd != -1) { | |
1359 | if (lseek(req->http_body_fd, 0, SEEK_SET) == -1) { | |
1360 | kore_log(LOG_ERR, "lseek(%s) failed: %s", | |
1361 | req->http_body_path, errno_s); | |
1362 | return (KORE_RESULT_ERROR); | |
1363 | } | |
1364 | } else { | |
1365 | kore_buf_reset(req->http_body); | |
1366 | } | |
1367 | ||
1368 | return (KORE_RESULT_OK); | |
1369 | } | |
1370 | ||
1145 | 1371 | static void |
1146 | http_error_response(struct connection *c, struct spdy_stream *s, int status) | |
1147 | { | |
1148 | kore_debug("http_error_response(%p, %p, %d)", c, s, status); | |
1372 | http_error_response(struct connection *c, int status) | |
1373 | { | |
1374 | kore_debug("http_error_response(%p, %d)", c, status); | |
1149 | 1375 | |
1150 | 1376 | switch (c->proto) { |
1151 | case CONN_PROTO_SPDY: | |
1152 | http_response_spdy(NULL, c, s, status, NULL, 0); | |
1153 | break; | |
1154 | 1377 | case CONN_PROTO_HTTP: |
1155 | if (s != NULL) | |
1156 | kore_log(LOG_NOTICE, "http_error_response: s != NULL"); | |
1157 | 1378 | http_response_normal(NULL, c, status, NULL, 0); |
1158 | 1379 | break; |
1159 | 1380 | default: |
1163 | 1384 | } |
1164 | 1385 | |
1165 | 1386 | static void |
1166 | http_response_spdy(struct http_request *req, struct connection *c, | |
1167 | struct spdy_stream *s, int status, void *d, u_int32_t len) | |
1168 | { | |
1169 | u_int32_t hlen; | |
1170 | struct http_header *hdr; | |
1171 | u_int8_t *htext; | |
1172 | struct spdy_header_block *hblock; | |
1173 | char sbuf[512]; | |
1174 | ||
1175 | (void)snprintf(sbuf, sizeof(sbuf), "%d %s", | |
1176 | status, http_status_text(status)); | |
1177 | ||
1178 | hblock = spdy_header_block_create(SPDY_HBLOCK_NORMAL); | |
1179 | spdy_header_block_add(hblock, ":status", sbuf); | |
1180 | spdy_header_block_add(hblock, ":version", "HTTP/1.1"); | |
1181 | spdy_header_block_add(hblock, ":server", http_version_spdy); | |
1182 | ||
1183 | if (http_hsts_enable) { | |
1184 | (void)snprintf(sbuf, sizeof(sbuf), | |
1185 | "max-age=%" PRIu64 "; includeSubDomains", http_hsts_enable); | |
1186 | spdy_header_block_add(hblock, | |
1187 | ":strict-transport-security", sbuf); | |
1188 | } | |
1189 | ||
1190 | if (req != NULL) { | |
1191 | TAILQ_FOREACH(hdr, &(req->resp_headers), list) | |
1192 | spdy_header_block_add(hblock, hdr->header, hdr->value); | |
1193 | } | |
1194 | ||
1195 | htext = spdy_header_block_release(c, hblock, &hlen); | |
1196 | if (htext == NULL) { | |
1197 | spdy_session_teardown(c, SPDY_SESSION_ERROR_INTERNAL); | |
1198 | return; | |
1199 | } | |
1200 | ||
1201 | spdy_frame_send(c, SPDY_CTRL_FRAME_SYN_REPLY, 0, hlen, s, 0); | |
1202 | net_send_queue(c, htext, hlen, NULL, NETBUF_LAST_CHAIN); | |
1203 | kore_mem_free(htext); | |
1204 | ||
1205 | if (len > 0 && req != NULL && req->method != HTTP_METHOD_HEAD) { | |
1206 | s->send_size += len; | |
1207 | s->flags |= SPDY_DATAFRAME_PRELUDE; | |
1208 | ||
1209 | if (d != NULL) | |
1210 | net_send_queue(c, d, len, s, NETBUF_LAST_CHAIN); | |
1211 | } | |
1212 | ||
1213 | if ((req != NULL && req->method == HTTP_METHOD_HEAD) || | |
1214 | (len == 0 && !(s->flags & SPDY_NO_CLOSE))) { | |
1215 | spdy_frame_send(c, SPDY_DATA_FRAME, FLAG_FIN, 0, s, 0); | |
1216 | spdy_stream_close(c, s, SPDY_KEEP_NETBUFS); | |
1217 | } | |
1218 | } | |
1219 | ||
1220 | static void | |
1221 | 1387 | http_response_normal(struct http_request *req, struct connection *c, |
1222 | int status, void *d, u_int32_t len) | |
1388 | int status, const void *d, size_t len) | |
1223 | 1389 | { |
1224 | 1390 | struct http_header *hdr; |
1225 | 1391 | char *conn; |
1241 | 1407 | if ((*conn == 'c' || *conn == 'C') && |
1242 | 1408 | !strcasecmp(conn, "close")) |
1243 | 1409 | connection_close = 1; |
1244 | kore_mem_free(conn); | |
1245 | } | |
1246 | } | |
1247 | ||
1248 | if (http_keepalive_time && connection_close == 0) { | |
1249 | kore_buf_appendf(header_buf, "connection: keep-alive\r\n"); | |
1250 | kore_buf_appendf(header_buf, "keep-alive: timeout=%d\r\n", | |
1251 | http_keepalive_time); | |
1252 | } else { | |
1253 | c->flags |= CONN_CLOSE_EMPTY; | |
1254 | kore_buf_appendf(header_buf, "connection: close\r\n"); | |
1410 | } | |
1411 | } | |
1412 | ||
1413 | /* Note that req CAN be NULL. */ | |
1414 | if (req != NULL && req->owner->proto != CONN_PROTO_WEBSOCKET) { | |
1415 | if (http_keepalive_time && connection_close == 0) { | |
1416 | kore_buf_appendf(header_buf, | |
1417 | "connection: keep-alive\r\n"); | |
1418 | kore_buf_appendf(header_buf, | |
1419 | "keep-alive: timeout=%d\r\n", http_keepalive_time); | |
1420 | } else { | |
1421 | c->flags |= CONN_CLOSE_EMPTY; | |
1422 | kore_buf_appendf(header_buf, "connection: close\r\n"); | |
1423 | } | |
1255 | 1424 | } |
1256 | 1425 | |
1257 | 1426 | if (http_hsts_enable) { |
1270 | 1439 | if (status != 204 && status >= 200 && |
1271 | 1440 | !(req->flags & HTTP_REQUEST_NO_CONTENT_LENGTH)) { |
1272 | 1441 | kore_buf_appendf(header_buf, |
1273 | "content-length: %d\r\n", len); | |
1442 | "content-length: %zu\r\n", len); | |
1274 | 1443 | } |
1275 | 1444 | } else { |
1276 | 1445 | if (status != 204 && status >= 200) { |
1277 | 1446 | kore_buf_appendf(header_buf, |
1278 | "content-length: %d\r\n", len); | |
1447 | "content-length: %zu\r\n", len); | |
1279 | 1448 | } |
1280 | 1449 | } |
1281 | 1450 | |
1282 | 1451 | kore_buf_append(header_buf, "\r\n", 2); |
1283 | net_send_queue(c, header_buf->data, header_buf->offset, | |
1284 | NULL, NETBUF_LAST_CHAIN); | |
1452 | net_send_queue(c, header_buf->data, header_buf->offset); | |
1285 | 1453 | |
1286 | 1454 | if (d != NULL && req != NULL && req->method != HTTP_METHOD_HEAD) |
1287 | net_send_queue(c, d, len, NULL, NETBUF_LAST_CHAIN); | |
1455 | net_send_queue(c, d, len); | |
1288 | 1456 | |
1289 | 1457 | if (!(c->flags & CONN_CLOSE_EMPTY)) |
1290 | 1458 | net_recv_reset(c, http_header_max, http_header_recv); |
1423 | 1591 | |
1424 | 1592 | return (r); |
1425 | 1593 | } |
1594 | ||
1595 | const char * | |
1596 | http_method_text(int method) | |
1597 | { | |
1598 | char *r; | |
1599 | ||
1600 | switch(method) { | |
1601 | case HTTP_METHOD_GET: | |
1602 | r = "GET"; | |
1603 | break; | |
1604 | case HTTP_METHOD_POST: | |
1605 | r = "POST"; | |
1606 | break; | |
1607 | case HTTP_METHOD_PUT: | |
1608 | r = "PUT"; | |
1609 | break; | |
1610 | case HTTP_METHOD_DELETE: | |
1611 | r = "DELETE"; | |
1612 | break; | |
1613 | case HTTP_METHOD_HEAD: | |
1614 | r = "HEAD"; | |
1615 | break; | |
1616 | default: | |
1617 | r = ""; | |
1618 | break; | |
1619 | } | |
1620 | ||
1621 | return (r); | |
1622 | } |
0 | /* | |
1 | * Copyright (c) 2016 Raphaël Monrouzeau <raphael.monrouzeau@gmail.com> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | #include <limits.h> | |
17 | #include <stdbool.h> | |
18 | ||
19 | #include <yajl/yajl_tree.h> | |
20 | #include <yajl/yajl_gen.h> | |
21 | ||
22 | #include "kore.h" | |
23 | #include "http.h" | |
24 | #include "jsonrpc.h" | |
25 | ||
26 | static void | |
27 | init_log(struct jsonrpc_log *log) | |
28 | { | |
29 | log->msg = NULL; | |
30 | log->next = log; | |
31 | log->prev = log; | |
32 | } | |
33 | ||
34 | static void | |
35 | append_log(struct jsonrpc_log *prev, int lvl, char *msg) | |
36 | { | |
37 | struct jsonrpc_log *new = kore_malloc(sizeof(struct jsonrpc_log)); | |
38 | ||
39 | new->lvl = lvl; | |
40 | new->msg = msg; | |
41 | ||
42 | new->prev = prev; | |
43 | new->next = prev->next; | |
44 | prev->next->prev = new; | |
45 | prev->next = new; | |
46 | } | |
47 | ||
48 | static void | |
49 | free_log(struct jsonrpc_log *root) | |
50 | { | |
51 | for (struct jsonrpc_log *it = root->next; it != root; it = it->next) { | |
52 | kore_free(it); | |
53 | } | |
54 | } | |
55 | ||
56 | static void | |
57 | init_request(struct jsonrpc_request *req) | |
58 | { | |
59 | init_log(&req->log); | |
60 | kore_buf_init(&req->buf, 256); | |
61 | req->gen = NULL; | |
62 | req->http = NULL; | |
63 | req->json = NULL; | |
64 | req->id = NULL; | |
65 | req->method = NULL; | |
66 | req->params = NULL; | |
67 | req->log_levels = (1 << LOG_EMERG) | (1 << LOG_ERR) | (1 << LOG_WARNING) | |
68 | | (1 << LOG_NOTICE); | |
69 | req->flags = 0; | |
70 | } | |
71 | ||
72 | void | |
73 | jsonrpc_destroy_request(struct jsonrpc_request *req) | |
74 | { | |
75 | if (req->gen != NULL) { | |
76 | yajl_gen_free(req->gen); | |
77 | req->gen = NULL; | |
78 | } | |
79 | if (req->json != NULL) { | |
80 | yajl_tree_free(req->json); | |
81 | req->json = NULL; | |
82 | } | |
83 | kore_buf_cleanup(&req->buf); | |
84 | free_log(&req->log); | |
85 | } | |
86 | ||
87 | void | |
88 | jsonrpc_log(struct jsonrpc_request *req, int lvl, const char *fmt, ...) | |
89 | { | |
90 | va_list ap; | |
91 | char *msg; | |
92 | size_t start = req->buf.offset; | |
93 | ||
94 | va_start(ap, fmt); | |
95 | kore_buf_appendv(&req->buf, fmt, ap); | |
96 | va_end(ap); | |
97 | ||
98 | msg = kore_buf_stringify(&req->buf, NULL) + start; | |
99 | ||
100 | append_log(&req->log, lvl, msg); | |
101 | } | |
102 | ||
103 | static int | |
104 | read_json_body(struct http_request *http_req, struct jsonrpc_request *req) | |
105 | { | |
106 | char *body_string; | |
107 | ssize_t body_len = 0, chunk_len; | |
108 | u_int8_t chunk_buffer[BUFSIZ]; | |
109 | char error_buffer[1024]; | |
110 | ||
111 | for (;;) { | |
112 | chunk_len = http_body_read(http_req, chunk_buffer, | |
113 | sizeof(chunk_buffer)); | |
114 | if (chunk_len == -1) { | |
115 | jsonrpc_log(req, LOG_CRIT, | |
116 | "Failed to read request body"); | |
117 | return (JSONRPC_SERVER_ERROR); | |
118 | } | |
119 | ||
120 | if (chunk_len == 0) | |
121 | break; | |
122 | ||
123 | if (body_len > SSIZE_MAX - chunk_len) { | |
124 | jsonrpc_log(req, LOG_CRIT, | |
125 | "Request body bigger than the platform accepts"); | |
126 | return (JSONRPC_SERVER_ERROR); | |
127 | } | |
128 | body_len += chunk_len; | |
129 | ||
130 | kore_buf_append(&req->buf, chunk_buffer, chunk_len); | |
131 | } | |
132 | ||
133 | /* Grab our body data as a NUL-terminated string. */ | |
134 | body_string = kore_buf_stringify(&req->buf, NULL); | |
135 | ||
136 | /* Parse the body via yajl now. */ | |
137 | *error_buffer = 0; | |
138 | req->json = yajl_tree_parse(body_string, error_buffer, | |
139 | sizeof(error_buffer)); | |
140 | if (req->json == NULL) { | |
141 | if (strlen(error_buffer)) { | |
142 | jsonrpc_log(req, LOG_ERR, "Invalid json: %s", | |
143 | error_buffer); | |
144 | } else { | |
145 | jsonrpc_log(req, LOG_ERR, "Invalid json"); | |
146 | } | |
147 | return (JSONRPC_PARSE_ERROR); | |
148 | } | |
149 | ||
150 | return (0); | |
151 | } | |
152 | ||
153 | static int | |
154 | parse_json_body(struct jsonrpc_request *req) | |
155 | { | |
156 | static const char *proto_path[] = { "jsonrpc", NULL }; | |
157 | static const char *id_path[] = { "id", NULL }; | |
158 | static const char *method_path[] = { "method", NULL }; | |
159 | static const char *params_path[] = { "params", NULL }; | |
160 | ||
161 | /* Check protocol first. */ | |
162 | yajl_val proto = yajl_tree_get(req->json, proto_path, yajl_t_string); | |
163 | if (proto == NULL) { | |
164 | jsonrpc_log(req, LOG_ERR, | |
165 | "JSON-RPC protocol MUST be indicated and \"2.0\""); | |
166 | return (JSONRPC_PARSE_ERROR); | |
167 | } | |
168 | ||
169 | char *proto_string = YAJL_GET_STRING(proto); | |
170 | if (proto_string == NULL) { | |
171 | jsonrpc_log(req, LOG_ERR, | |
172 | "JSON-RPC protocol MUST be indicated and \"2.0\""); | |
173 | return (JSONRPC_PARSE_ERROR); | |
174 | } | |
175 | ||
176 | if (strcmp("2.0", proto_string) != 0) { | |
177 | jsonrpc_log(req, LOG_ERR, | |
178 | "JSON-RPC protocol MUST be indicated and \"2.0\""); | |
179 | return (JSONRPC_PARSE_ERROR); | |
180 | } | |
181 | ||
182 | /* Check id. */ | |
183 | if ((req->id = yajl_tree_get(req->json, id_path, yajl_t_any)) != NULL) { | |
184 | if (YAJL_IS_NUMBER(req->id)) { | |
185 | if (!YAJL_IS_INTEGER(req->id)) { | |
186 | jsonrpc_log(req, LOG_ERR, | |
187 | "JSON-RPC id SHOULD NOT contain fractional" | |
188 | " parts"); | |
189 | return (JSONRPC_PARSE_ERROR); | |
190 | } | |
191 | } else if (!YAJL_IS_STRING(req->id)) { | |
192 | jsonrpc_log(req, LOG_ERR, | |
193 | "JSON-RPC id MUST contain a String or Number"); | |
194 | return (JSONRPC_PARSE_ERROR); | |
195 | } | |
196 | } | |
197 | ||
198 | /* Check method. */ | |
199 | if ((req->method = YAJL_GET_STRING(yajl_tree_get(req->json, method_path, | |
200 | yajl_t_string))) == NULL) { | |
201 | jsonrpc_log(req, LOG_ERR, | |
202 | "JSON-RPC method MUST exist and be a String"); | |
203 | return (JSONRPC_PARSE_ERROR); | |
204 | } | |
205 | ||
206 | /* Check params. */ | |
207 | req->params = yajl_tree_get(req->json, params_path, yajl_t_any); | |
208 | if (!(req->params == NULL || YAJL_IS_ARRAY(req->params) | |
209 | || YAJL_IS_OBJECT(req->params))) { | |
210 | jsonrpc_log(req, LOG_ERR, | |
211 | "JSON-RPC params MUST be Object or Array"); | |
212 | return (JSONRPC_PARSE_ERROR); | |
213 | } | |
214 | ||
215 | return (0); | |
216 | } | |
217 | ||
218 | int | |
219 | jsonrpc_read_request(struct http_request *http_req, struct jsonrpc_request *req) | |
220 | { | |
221 | int ret; | |
222 | ||
223 | init_request(req); | |
224 | req->http = http_req; | |
225 | ||
226 | if ((ret = read_json_body(http_req, req)) != 0) | |
227 | return (ret); | |
228 | ||
229 | return parse_json_body(req); | |
230 | } | |
231 | ||
232 | static int | |
233 | write_id(yajl_gen gen, yajl_val id) | |
234 | { | |
235 | int status; | |
236 | ||
237 | if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(gen, "id"))) | |
238 | return (status); | |
239 | ||
240 | if (YAJL_IS_NULL(id)) | |
241 | return yajl_gen_null(gen); | |
242 | ||
243 | if (YAJL_IS_NUMBER(id)) { | |
244 | if (YAJL_IS_INTEGER(id)) | |
245 | return yajl_gen_integer(gen, YAJL_GET_INTEGER(id)); | |
246 | return yajl_gen_null(gen); | |
247 | } | |
248 | ||
249 | if (YAJL_IS_STRING(id)) { | |
250 | char *id_str = YAJL_GET_STRING(id); | |
251 | ||
252 | return yajl_gen_string(gen, (unsigned char *)id_str, | |
253 | strlen(id_str)); | |
254 | } | |
255 | ||
256 | return yajl_gen_null(gen); | |
257 | } | |
258 | ||
259 | static int | |
260 | open_response(yajl_gen genctx, yajl_val id) | |
261 | { | |
262 | int status; | |
263 | ||
264 | if (YAJL_GEN_KO(status = yajl_gen_map_open(genctx))) | |
265 | goto failed; | |
266 | if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(genctx, "jsonrpc"))) | |
267 | goto failed; | |
268 | if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(genctx, "2.0"))) | |
269 | goto failed; | |
270 | status = write_id(genctx, id); | |
271 | failed: | |
272 | return (status); | |
273 | } | |
274 | ||
275 | static int | |
276 | close_response(yajl_gen genctx) | |
277 | { | |
278 | int status; | |
279 | ||
280 | if (YAJL_GEN_KO(status = yajl_gen_map_close(genctx))) | |
281 | goto failed; | |
282 | status = yajl_gen_map_close(genctx); | |
283 | failed: | |
284 | return (status); | |
285 | } | |
286 | ||
287 | static int | |
288 | write_log(struct jsonrpc_request *req) | |
289 | { | |
290 | bool wrote_smth = false; | |
291 | int status = 0; | |
292 | ||
293 | for (struct jsonrpc_log *log = req->log.next; log != &req->log; | |
294 | log = log->next) { | |
295 | ||
296 | if (((1 << log->lvl) & req->log_levels) == 0) | |
297 | continue; | |
298 | ||
299 | if (!wrote_smth) { | |
300 | if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(req->gen, | |
301 | "data"))) | |
302 | goto failed; | |
303 | if (YAJL_GEN_KO(status = yajl_gen_array_open(req->gen))) | |
304 | goto failed; | |
305 | yajl_gen_config(req->gen, yajl_gen_validate_utf8, 1); | |
306 | wrote_smth = true; | |
307 | } | |
308 | ||
309 | if (YAJL_GEN_KO(status = yajl_gen_array_open(req->gen))) | |
310 | goto failed; | |
311 | if (YAJL_GEN_KO(status = yajl_gen_integer(req->gen, log->lvl))) | |
312 | goto failed; | |
313 | if (YAJL_GEN_KO(status = yajl_gen_string(req->gen, | |
314 | (unsigned char *)log->msg, strlen(log->msg)))) | |
315 | goto failed; | |
316 | if (YAJL_GEN_KO(status = yajl_gen_array_close(req->gen))) | |
317 | goto failed; | |
318 | } | |
319 | ||
320 | if (wrote_smth) { | |
321 | yajl_gen_config(req->gen, yajl_gen_validate_utf8, 0); | |
322 | status = yajl_gen_array_close(req->gen); | |
323 | } | |
324 | failed: | |
325 | return (status); | |
326 | } | |
327 | ||
328 | static int | |
329 | write_error(struct jsonrpc_request *req, int code, const char *message) | |
330 | { | |
331 | int status; | |
332 | ||
333 | yajl_gen_config(req->gen, yajl_gen_validate_utf8, 0); | |
334 | ||
335 | if (YAJL_GEN_KO(status = open_response(req->gen, req->id))) | |
336 | goto failed; | |
337 | if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(req->gen, "error"))) | |
338 | goto failed; | |
339 | if (YAJL_GEN_KO(status = yajl_gen_map_open(req->gen))) | |
340 | goto failed; | |
341 | if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(req->gen, "code"))) | |
342 | goto failed; | |
343 | if (YAJL_GEN_KO(status = yajl_gen_integer(req->gen, code))) | |
344 | goto failed; | |
345 | if (YAJL_GEN_KO(status = YAJL_GEN_CONST_STRING(req->gen, "message"))) | |
346 | goto failed; | |
347 | ||
348 | yajl_gen_config(req->gen, yajl_gen_validate_utf8, 1); | |
349 | ||
350 | if (YAJL_GEN_KO(status = yajl_gen_string(req->gen, | |
351 | (const unsigned char *)message, strlen(message)))) | |
352 | goto failed; | |
353 | ||
354 | yajl_gen_config(req->gen, yajl_gen_validate_utf8, 0); | |
355 | ||
356 | if (YAJL_GEN_KO(status = write_log(req))) | |
357 | goto failed; | |
358 | ||
359 | status = close_response(req->gen); | |
360 | failed: | |
361 | return (status); | |
362 | } | |
363 | ||
364 | static const char * | |
365 | known_msg(int code) | |
366 | { | |
367 | switch (code) { | |
368 | case JSONRPC_PARSE_ERROR: | |
369 | return (JSONRPC_PARSE_ERROR_MSG); | |
370 | case JSONRPC_INVALID_REQUEST: | |
371 | return (JSONRPC_INVALID_REQUEST_MSG); | |
372 | case JSONRPC_METHOD_NOT_FOUND: | |
373 | return (JSONRPC_METHOD_NOT_FOUND_MSG); | |
374 | case JSONRPC_INVALID_PARAMS: | |
375 | return (JSONRPC_INVALID_PARAMS_MSG); | |
376 | case JSONRPC_INTERNAL_ERROR: | |
377 | return (JSONRPC_INTERNAL_ERROR_MSG); | |
378 | case JSONRPC_SERVER_ERROR: | |
379 | return (JSONRPC_SERVER_ERROR_MSG); | |
380 | case JSONRPC_LIMIT_REACHED: | |
381 | return (JSONRPC_LIMIT_REACHED_MSG); | |
382 | default: | |
383 | return (NULL); | |
384 | } | |
385 | } | |
386 | ||
387 | int | |
388 | jsonrpc_error(struct jsonrpc_request *req, int code, const char *msg) | |
389 | { | |
390 | char *msg_fallback; | |
391 | const unsigned char *body = NULL; | |
392 | size_t body_len = 0; | |
393 | int status; | |
394 | ||
395 | if (req->id == NULL) | |
396 | goto succeeded; | |
397 | ||
398 | if ((req->gen = yajl_gen_alloc(NULL)) == NULL) { | |
399 | kore_log(LOG_ERR, "jsonrpc_error: Failed to allocate yajl gen"); | |
400 | goto failed; | |
401 | } | |
402 | ||
403 | yajl_gen_config(req->gen, yajl_gen_beautify, | |
404 | req->flags & yajl_gen_beautify); | |
405 | ||
406 | if (msg == NULL) | |
407 | msg = known_msg(code); | |
408 | ||
409 | if (msg == NULL) { | |
410 | size_t start = req->buf.offset; | |
411 | kore_buf_appendf(&req->buf, "%d", code); | |
412 | msg_fallback = kore_buf_stringify(&req->buf, NULL) + start; | |
413 | } | |
414 | ||
415 | if (YAJL_GEN_KO(status = write_error(req, code, | |
416 | msg ? msg : msg_fallback))) { | |
417 | kore_log(LOG_ERR, "jsonrpc_error: Failed to yajl gen text [%d]", | |
418 | status); | |
419 | goto failed; | |
420 | } | |
421 | ||
422 | http_response_header(req->http, "content-type", "application/json"); | |
423 | yajl_gen_get_buf(req->gen, &body, &body_len); | |
424 | succeeded: | |
425 | http_response(req->http, 200, body, body_len); | |
426 | if (req->gen != NULL) | |
427 | yajl_gen_clear(req->gen); | |
428 | jsonrpc_destroy_request(req); | |
429 | return (KORE_RESULT_OK); | |
430 | failed: | |
431 | http_response(req->http, 500, NULL, 0); | |
432 | jsonrpc_destroy_request(req); | |
433 | return (KORE_RESULT_OK); | |
434 | } | |
435 | ||
436 | int | |
437 | jsonrpc_result(struct jsonrpc_request *req, | |
438 | int (*write_result)(struct jsonrpc_request *, void *), void *ctx) | |
439 | { | |
440 | const unsigned char *body = NULL; | |
441 | size_t body_len = 0; | |
442 | ||
443 | if (req->id == NULL) | |
444 | goto succeeded; | |
445 | ||
446 | if ((req->gen = yajl_gen_alloc(NULL)) == NULL) { | |
447 | kore_log(LOG_ERR, "jsonrpc_result: Failed to allocate yajl gen"); | |
448 | goto failed; | |
449 | } | |
450 | ||
451 | yajl_gen_config(req->gen, yajl_gen_beautify, | |
452 | req->flags & yajl_gen_beautify); | |
453 | ||
454 | yajl_gen_config(req->gen, yajl_gen_validate_utf8, 0); | |
455 | ||
456 | if (YAJL_GEN_KO(open_response(req->gen, req->id))) | |
457 | goto failed; | |
458 | if (YAJL_GEN_KO(YAJL_GEN_CONST_STRING(req->gen, "result"))) | |
459 | goto failed; | |
460 | if (YAJL_GEN_KO(write_result(req, ctx))) | |
461 | goto failed; | |
462 | if (YAJL_GEN_KO(yajl_gen_map_close(req->gen))) | |
463 | goto failed; | |
464 | ||
465 | http_response_header(req->http, "content-type", "application/json"); | |
466 | yajl_gen_get_buf(req->gen, &body, &body_len); | |
467 | succeeded: | |
468 | http_response(req->http, 200, body, body_len); | |
469 | if (req->gen != NULL) | |
470 | yajl_gen_clear(req->gen); | |
471 | jsonrpc_destroy_request(req); | |
472 | return (KORE_RESULT_OK); | |
473 | failed: | |
474 | http_response(req->http, 500, NULL, 0); | |
475 | jsonrpc_destroy_request(req); | |
476 | return (KORE_RESULT_OK); | |
477 | } |
0 | /* | |
1 | * Copyright (c) 2016 Joris Vink <joris@coders.se> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | #include <sys/param.h> | |
17 | ||
18 | #include <openssl/evp.h> | |
19 | ||
20 | #include <stdio.h> | |
21 | #include <stdlib.h> | |
22 | #include <signal.h> | |
23 | #include <unistd.h> | |
24 | ||
25 | #include "kore.h" | |
26 | ||
27 | #if !defined(KORE_NO_TLS) | |
28 | struct key { | |
29 | EVP_PKEY *pkey; | |
30 | struct kore_domain *dom; | |
31 | TAILQ_ENTRY(key) list; | |
32 | }; | |
33 | ||
34 | static TAILQ_HEAD(, key) keys; | |
35 | extern volatile sig_atomic_t sig_recv; | |
36 | static int initialized = 0; | |
37 | ||
38 | static void keymgr_load_privatekey(struct kore_domain *); | |
39 | static void keymgr_msg_recv(struct kore_msg *, const void *); | |
40 | ||
41 | static void keymgr_rsa_encrypt(struct kore_msg *, const void *, | |
42 | struct key *); | |
43 | static void keymgr_ecdsa_sign(struct kore_msg *, const void *, | |
44 | struct key *); | |
45 | ||
46 | void | |
47 | kore_keymgr_run(void) | |
48 | { | |
49 | int quit; | |
50 | ||
51 | quit = 0; | |
52 | initialized = 1; | |
53 | TAILQ_INIT(&keys); | |
54 | ||
55 | kore_listener_cleanup(); | |
56 | kore_module_cleanup(); | |
57 | ||
58 | kore_domain_callback(keymgr_load_privatekey); | |
59 | kore_worker_privdrop(); | |
60 | ||
61 | net_init(); | |
62 | kore_connection_init(); | |
63 | kore_platform_event_init(); | |
64 | ||
65 | kore_msg_worker_init(); | |
66 | kore_msg_register(KORE_MSG_KEYMGR_REQ, keymgr_msg_recv); | |
67 | ||
68 | kore_log(LOG_NOTICE, "key manager started"); | |
69 | ||
70 | while (quit != 1) { | |
71 | if (sig_recv != 0) { | |
72 | switch (sig_recv) { | |
73 | case SIGQUIT: | |
74 | case SIGINT: | |
75 | case SIGTERM: | |
76 | quit = 1; | |
77 | break; | |
78 | default: | |
79 | break; | |
80 | } | |
81 | sig_recv = 0; | |
82 | } | |
83 | ||
84 | kore_platform_event_wait(1000); | |
85 | kore_connection_prune(KORE_CONNECTION_PRUNE_DISCONNECT); | |
86 | } | |
87 | ||
88 | kore_keymgr_cleanup(); | |
89 | kore_platform_event_cleanup(); | |
90 | kore_connection_cleanup(); | |
91 | net_cleanup(); | |
92 | } | |
93 | ||
94 | void | |
95 | kore_keymgr_cleanup(void) | |
96 | { | |
97 | struct key *key, *next; | |
98 | ||
99 | kore_log(LOG_NOTICE, "cleaning up keys"); | |
100 | ||
101 | if (initialized == 0) | |
102 | return; | |
103 | ||
104 | for (key = TAILQ_FIRST(&keys); key != NULL; key = next) { | |
105 | next = TAILQ_NEXT(key, list); | |
106 | TAILQ_REMOVE(&keys, key, list); | |
107 | ||
108 | EVP_PKEY_free(key->pkey); | |
109 | kore_free(key); | |
110 | } | |
111 | } | |
112 | ||
113 | static void | |
114 | keymgr_load_privatekey(struct kore_domain *dom) | |
115 | { | |
116 | FILE *fp; | |
117 | struct key *key; | |
118 | ||
119 | if (dom->certkey == NULL) | |
120 | return; | |
121 | ||
122 | if ((fp = fopen(dom->certkey, "r")) == NULL) | |
123 | fatal("failed to open private key: %s", dom->certkey); | |
124 | ||
125 | key = kore_malloc(sizeof(*key)); | |
126 | key->dom = dom; | |
127 | ||
128 | if ((key->pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) | |
129 | fatal("PEM_read_PrivateKey: %s", ssl_errno_s); | |
130 | ||
131 | (void)fclose(fp); | |
132 | kore_free(dom->certkey); | |
133 | dom->certkey = NULL; | |
134 | ||
135 | TAILQ_INSERT_TAIL(&keys, key, list); | |
136 | } | |
137 | ||
138 | static void | |
139 | keymgr_msg_recv(struct kore_msg *msg, const void *data) | |
140 | { | |
141 | const struct kore_keyreq *req; | |
142 | struct key *key; | |
143 | ||
144 | if (msg->length < sizeof(*req)) | |
145 | return; | |
146 | ||
147 | req = (const struct kore_keyreq *)data; | |
148 | if (msg->length != (sizeof(*req) + req->data_len)) | |
149 | return; | |
150 | ||
151 | key = NULL; | |
152 | TAILQ_FOREACH(key, &keys, list) { | |
153 | if (!strncmp(key->dom->domain, req->domain, req->domain_len)) | |
154 | break; | |
155 | } | |
156 | ||
157 | if (key == NULL) | |
158 | return; | |
159 | ||
160 | switch (EVP_PKEY_id(key->pkey)) { | |
161 | case EVP_PKEY_RSA: | |
162 | keymgr_rsa_encrypt(msg, data, key); | |
163 | break; | |
164 | case EVP_PKEY_EC: | |
165 | keymgr_ecdsa_sign(msg, data, key); | |
166 | break; | |
167 | default: | |
168 | break; | |
169 | } | |
170 | } | |
171 | ||
172 | static void | |
173 | keymgr_rsa_encrypt(struct kore_msg *msg, const void *data, struct key *key) | |
174 | { | |
175 | int ret; | |
176 | const struct kore_keyreq *req; | |
177 | size_t keylen; | |
178 | u_int8_t buf[1024]; | |
179 | ||
180 | req = (const struct kore_keyreq *)data; | |
181 | ||
182 | keylen = RSA_size(key->pkey->pkey.rsa); | |
183 | if (req->data_len > keylen || keylen > sizeof(buf)) | |
184 | return; | |
185 | ||
186 | ret = RSA_private_encrypt(req->data_len, req->data, | |
187 | buf, key->pkey->pkey.rsa, req->padding); | |
188 | if (ret != RSA_size(key->pkey->pkey.rsa)) | |
189 | return; | |
190 | ||
191 | kore_msg_send(msg->src, KORE_MSG_KEYMGR_RESP, buf, ret); | |
192 | } | |
193 | ||
194 | static void | |
195 | keymgr_ecdsa_sign(struct kore_msg *msg, const void *data, struct key *key) | |
196 | { | |
197 | size_t len; | |
198 | const struct kore_keyreq *req; | |
199 | unsigned int siglen; | |
200 | u_int8_t sig[1024]; | |
201 | ||
202 | req = (const struct kore_keyreq *)data; | |
203 | ||
204 | len = ECDSA_size(key->pkey->pkey.ec); | |
205 | if (req->data_len > len || len > sizeof(sig)) | |
206 | return; | |
207 | ||
208 | if (ECDSA_sign(key->pkey->save_type, req->data, req->data_len, | |
209 | sig, &siglen, key->pkey->pkey.ec) == 0) | |
210 | return; | |
211 | ||
212 | if (siglen > sizeof(sig)) | |
213 | return; | |
214 | ||
215 | kore_msg_send(msg->src, KORE_MSG_KEYMGR_RESP, sig, siglen); | |
216 | } | |
217 | ||
218 | #endif |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
14 | 14 | */ |
15 | 15 | |
16 | 16 | #include <sys/types.h> |
17 | #include <sys/stat.h> | |
18 | ||
17 | 19 | #include <sys/socket.h> |
18 | 20 | #include <sys/resource.h> |
19 | 21 | |
22 | #include <stdio.h> | |
20 | 23 | #include <netdb.h> |
21 | 24 | #include <signal.h> |
22 | 25 | |
23 | 26 | #include "kore.h" |
27 | ||
28 | #if !defined(KORE_NO_HTTP) | |
29 | #include "http.h" | |
30 | #endif | |
24 | 31 | |
25 | 32 | volatile sig_atomic_t sig_recv; |
26 | 33 | |
39 | 46 | char *kore_pidfile = KORE_PIDFILE_DEFAULT; |
40 | 47 | char *kore_tls_cipher_list = KORE_DEFAULT_CIPHER_LIST; |
41 | 48 | |
49 | extern char *__progname; | |
50 | ||
42 | 51 | static void usage(void); |
43 | 52 | static void version(void); |
44 | 53 | static void kore_server_start(void); |
48 | 57 | static void |
49 | 58 | usage(void) |
50 | 59 | { |
60 | #if !defined(KORE_SINGLE_BINARY) | |
51 | 61 | fprintf(stderr, "Usage: kore [options | command]\n"); |
62 | #else | |
63 | fprintf(stderr, "Usage: %s [options]\n", __progname); | |
64 | #endif | |
52 | 65 | fprintf(stderr, "\n"); |
53 | 66 | fprintf(stderr, "Available options:\n"); |
54 | fprintf(stderr, "\t-c\tspecify the configuration file to use\n"); | |
55 | fprintf(stderr, "\t-d\trun with debug on (if compiled in)\n"); | |
56 | fprintf(stderr, "\t-f\tstart kore in foreground mode\n"); | |
67 | #if !defined(KORE_SINGLE_BINARY) | |
68 | fprintf(stderr, "\t-c\tconfiguration to use\n"); | |
69 | #endif | |
70 | #if defined(KORE_DEBUG) | |
71 | fprintf(stderr, "\t-d\trun with debug on)\n"); | |
72 | #endif | |
73 | fprintf(stderr, "\t-f\tstart in foreground\n"); | |
57 | 74 | fprintf(stderr, "\t-h\tthis help text\n"); |
58 | fprintf(stderr, "\t-n\tdo not chroot (if not starting kore as root)\n"); | |
59 | fprintf(stderr, "\t-r\tdo not runas (uid drop) (if not starting kore as root)\n"); | |
60 | fprintf(stderr, "\t-v\tdisplay kore's version information\n"); | |
61 | ||
75 | fprintf(stderr, "\t-n\tdo not chroot\n"); | |
76 | fprintf(stderr, "\t-r\tdo not drop privileges\n"); | |
77 | fprintf(stderr, "\t-v\tdisplay kore build information\n"); | |
78 | ||
79 | #if !defined(KORE_SINGLE_BINARY) | |
62 | 80 | kore_cli_usage(0); |
81 | #else | |
82 | fprintf(stderr, "\nbuilt with https://kore.io\n"); | |
83 | exit(1); | |
84 | #endif | |
63 | 85 | } |
64 | 86 | |
65 | 87 | static void |
66 | 88 | version(void) |
67 | 89 | { |
68 | printf("kore %d.%d.%d-%s ", KORE_VERSION_MAJOR, KORE_VERSION_MINOR, | |
90 | printf("%d.%d.%d-%s ", KORE_VERSION_MAJOR, KORE_VERSION_MINOR, | |
69 | 91 | KORE_VERSION_PATCH, KORE_VERSION_STATE); |
92 | #if defined(KORE_NO_TLS) | |
93 | printf("no-tls "); | |
94 | #endif | |
95 | #if defined(KORE_NO_HTTP) | |
96 | printf("no-http "); | |
97 | #endif | |
70 | 98 | #if defined(KORE_USE_PGSQL) |
71 | 99 | printf("pgsql "); |
72 | 100 | #endif |
73 | 101 | #if defined(KORE_USE_TASKS) |
74 | 102 | printf("tasks "); |
75 | 103 | #endif |
104 | #if defined(KORE_DEBUG) | |
105 | printf("debug "); | |
106 | #endif | |
107 | #if defined(KORE_SINGLE_BINARY) | |
108 | printf("single "); | |
109 | #endif | |
76 | 110 | printf("\n"); |
77 | 111 | |
78 | 112 | exit(0); |
81 | 115 | int |
82 | 116 | main(int argc, char *argv[]) |
83 | 117 | { |
84 | struct listener *l; | |
85 | int ch, flags; | |
118 | int ch, flags; | |
86 | 119 | |
87 | 120 | flags = 0; |
88 | 121 | |
122 | #if !defined(KORE_SINGLE_BINARY) | |
89 | 123 | while ((ch = getopt(argc, argv, "c:dfhnrv")) != -1) { |
124 | #else | |
125 | while ((ch = getopt(argc, argv, "dfhnrv")) != -1) { | |
126 | #endif | |
90 | 127 | flags++; |
91 | 128 | switch (ch) { |
129 | #if !defined(KORE_SINGLE_BINARY) | |
92 | 130 | case 'c': |
93 | 131 | config_file = optarg; |
94 | 132 | break; |
133 | #endif | |
134 | #if defined(KORE_DEBUG) | |
95 | 135 | case 'd': |
96 | #if defined(KORE_DEBUG) | |
97 | 136 | kore_debug = 1; |
98 | #else | |
99 | printf("kore not compiled with debug support\n"); | |
100 | #endif | |
101 | break; | |
137 | break; | |
138 | #endif | |
102 | 139 | case 'f': |
103 | 140 | foreground = 1; |
104 | 141 | break; |
124 | 161 | |
125 | 162 | kore_mem_init(); |
126 | 163 | |
164 | #if !defined(KORE_SINGLE_BINARY) | |
127 | 165 | if (argc > 0) { |
128 | 166 | if (flags) |
129 | 167 | fatal("You cannot specify kore flags and a command"); |
130 | 168 | return (kore_cli_main(argc, argv)); |
131 | 169 | } |
170 | #endif | |
132 | 171 | |
133 | 172 | kore_pid = getpid(); |
134 | 173 | nlisteners = 0; |
135 | 174 | LIST_INIT(&listeners); |
136 | 175 | |
137 | 176 | kore_log_init(); |
177 | #if !defined(KORE_NO_HTTP) | |
138 | 178 | kore_auth_init(); |
179 | kore_validator_init(); | |
180 | #endif | |
139 | 181 | kore_domain_init(); |
140 | 182 | kore_module_init(); |
141 | kore_validator_init(); | |
142 | 183 | kore_server_sslstart(); |
143 | 184 | |
185 | #if !defined(KORE_SINGLE_BINARY) | |
144 | 186 | if (config_file == NULL) |
145 | 187 | usage(); |
188 | #else | |
189 | kore_module_load(NULL, NULL); | |
190 | #endif | |
146 | 191 | |
147 | 192 | kore_parse_config(); |
148 | 193 | kore_platform_init(); |
194 | ||
195 | #if !defined(KORE_NO_HTTP) | |
149 | 196 | kore_accesslog_init(); |
197 | if (http_body_disk_offload > 0) { | |
198 | if (mkdir(http_body_disk_path, 0700) == -1 && errno != EEXIST) { | |
199 | printf("can't create http_body_disk_path '%s': %s\n", | |
200 | http_body_disk_path, errno_s); | |
201 | return (KORE_RESULT_ERROR); | |
202 | } | |
203 | } | |
204 | #endif | |
150 | 205 | |
151 | 206 | sig_recv = 0; |
152 | 207 | signal(SIGHUP, kore_signal); |
153 | 208 | signal(SIGQUIT, kore_signal); |
209 | signal(SIGTERM, kore_signal); | |
154 | 210 | |
155 | 211 | if (foreground) |
156 | 212 | signal(SIGINT, kore_signal); |
165 | 221 | if (!foreground) |
166 | 222 | unlink(kore_pidfile); |
167 | 223 | |
168 | LIST_FOREACH(l, &listeners, list) | |
169 | close(l->fd); | |
170 | ||
224 | kore_listener_cleanup(); | |
171 | 225 | kore_log(LOG_NOTICE, "goodbye"); |
226 | ||
172 | 227 | return (0); |
173 | 228 | } |
174 | 229 | |
175 | #if !defined(KORE_BENCHMARK) | |
176 | int | |
177 | kore_tls_npn_cb(SSL *ssl, const u_char **data, unsigned int *len, void *arg) | |
178 | { | |
179 | kore_debug("kore_tls_npn_cb(): sending protocols"); | |
180 | ||
181 | *data = (const unsigned char *)KORE_SSL_PROTO_STRING; | |
182 | *len = strlen(KORE_SSL_PROTO_STRING); | |
183 | ||
184 | return (SSL_TLSEXT_ERR_OK); | |
185 | } | |
186 | ||
230 | #if !defined(KORE_NO_TLS) | |
187 | 231 | int |
188 | 232 | kore_tls_sni_cb(SSL *ssl, int *ad, void *arg) |
189 | 233 | { |
224 | 268 | #endif |
225 | 269 | |
226 | 270 | int |
227 | kore_server_bind(const char *ip, const char *port) | |
271 | kore_server_bind(const char *ip, const char *port, const char *ccb) | |
228 | 272 | { |
229 | 273 | struct listener *l; |
230 | 274 | int on, r; |
231 | struct addrinfo *results; | |
275 | struct addrinfo hints, *results; | |
232 | 276 | |
233 | 277 | kore_debug("kore_server_bind(%s, %s)", ip, port); |
234 | 278 | |
235 | r = getaddrinfo(ip, port, NULL, &results); | |
279 | memset(&hints, 0, sizeof(hints)); | |
280 | hints.ai_family = AF_UNSPEC; | |
281 | hints.ai_socktype = SOCK_STREAM; | |
282 | hints.ai_protocol = IPPROTO_TCP; | |
283 | hints.ai_flags = 0; | |
284 | ||
285 | r = getaddrinfo(ip, port, &hints, &results); | |
236 | 286 | if (r != 0) |
237 | 287 | fatal("getaddrinfo(%s): %s", ip, gai_strerror(r)); |
238 | 288 | |
244 | 294 | fatal("getaddrinfo(): unknown address family %d", l->addrtype); |
245 | 295 | |
246 | 296 | if ((l->fd = socket(results->ai_family, SOCK_STREAM, 0)) == -1) { |
247 | kore_mem_free(l); | |
297 | kore_free(l); | |
248 | 298 | freeaddrinfo(results); |
249 | 299 | kore_debug("socket(): %s", errno_s); |
250 | 300 | printf("failed to create socket: %s\n", errno_s); |
251 | 301 | return (KORE_RESULT_ERROR); |
252 | 302 | } |
253 | 303 | |
254 | if (!kore_connection_nonblock(l->fd)) { | |
255 | kore_mem_free(l); | |
304 | if (!kore_connection_nonblock(l->fd, 1)) { | |
305 | kore_free(l); | |
256 | 306 | freeaddrinfo(results); |
257 | 307 | printf("failed to make socket non blocking: %s\n", errno_s); |
258 | 308 | return (KORE_RESULT_ERROR); |
262 | 312 | if (setsockopt(l->fd, SOL_SOCKET, |
263 | 313 | SO_REUSEADDR, (const char *)&on, sizeof(on)) == -1) { |
264 | 314 | close(l->fd); |
265 | kore_mem_free(l); | |
315 | kore_free(l); | |
266 | 316 | freeaddrinfo(results); |
267 | 317 | kore_debug("setsockopt(): %s", errno_s); |
268 | 318 | printf("failed to set SO_REUSEADDR: %s\n", errno_s); |
271 | 321 | |
272 | 322 | if (bind(l->fd, results->ai_addr, results->ai_addrlen) == -1) { |
273 | 323 | close(l->fd); |
274 | kore_mem_free(l); | |
324 | kore_free(l); | |
275 | 325 | freeaddrinfo(results); |
276 | 326 | kore_debug("bind(): %s", errno_s); |
277 | 327 | printf("failed to bind to %s port %s: %s\n", ip, port, errno_s); |
282 | 332 | |
283 | 333 | if (listen(l->fd, kore_socket_backlog) == -1) { |
284 | 334 | close(l->fd); |
285 | kore_mem_free(l); | |
335 | kore_free(l); | |
286 | 336 | kore_debug("listen(): %s", errno_s); |
287 | 337 | printf("failed to listen on socket: %s\n", errno_s); |
288 | 338 | return (KORE_RESULT_ERROR); |
289 | 339 | } |
290 | 340 | |
341 | if (ccb != NULL) { | |
342 | *(void **)&(l->connect) = kore_module_getsym(ccb); | |
343 | if (l->connect == NULL) { | |
344 | printf("no such callback: '%s'\n", ccb); | |
345 | close(l->fd); | |
346 | kore_free(l); | |
347 | return (KORE_RESULT_ERROR); | |
348 | } | |
349 | } else { | |
350 | l->connect = NULL; | |
351 | } | |
352 | ||
291 | 353 | nlisteners++; |
292 | 354 | LIST_INSERT_HEAD(&listeners, l, list); |
293 | 355 | |
294 | if (foreground) | |
356 | if (foreground) { | |
357 | #if !defined(KORE_NO_TLS) | |
295 | 358 | kore_log(LOG_NOTICE, "running on https://%s:%s", ip, port); |
359 | #else | |
360 | kore_log(LOG_NOTICE, "running on http://%s:%s", ip, port); | |
361 | #endif | |
362 | } | |
296 | 363 | |
297 | 364 | return (KORE_RESULT_OK); |
365 | } | |
366 | ||
367 | void | |
368 | kore_listener_cleanup(void) | |
369 | { | |
370 | struct listener *l; | |
371 | ||
372 | while (!LIST_EMPTY(&listeners)) { | |
373 | l = LIST_FIRST(&listeners); | |
374 | LIST_REMOVE(l, list); | |
375 | close(l->fd); | |
376 | kore_free(l); | |
377 | } | |
298 | 378 | } |
299 | 379 | |
300 | 380 | void |
306 | 386 | static void |
307 | 387 | kore_server_sslstart(void) |
308 | 388 | { |
309 | #if !defined(KORE_BENCHMARK) | |
389 | #if !defined(KORE_NO_TLS) | |
310 | 390 | kore_debug("kore_server_sslstart()"); |
311 | 391 | |
312 | 392 | SSL_library_init(); |
317 | 397 | static void |
318 | 398 | kore_server_start(void) |
319 | 399 | { |
400 | u_int32_t tmp; | |
320 | 401 | int quit; |
321 | 402 | |
322 | 403 | if (foreground == 0 && daemon(1, 1) == -1) |
326 | 407 | if (!foreground) |
327 | 408 | kore_write_kore_pid(); |
328 | 409 | |
329 | kore_log(LOG_NOTICE, "kore is starting up"); | |
410 | kore_log(LOG_NOTICE, "%s is starting up", __progname); | |
330 | 411 | #if defined(KORE_USE_PGSQL) |
331 | 412 | kore_log(LOG_NOTICE, "pgsql built-in enabled"); |
332 | 413 | #endif |
333 | 414 | #if defined(KORE_USE_TASKS) |
334 | 415 | kore_log(LOG_NOTICE, "tasks built-in enabled"); |
335 | 416 | #endif |
417 | #if defined(KORE_USE_JSONRPC) | |
418 | kore_log(LOG_NOTICE, "jsonrpc built-in enabled"); | |
419 | #endif | |
336 | 420 | |
337 | 421 | kore_platform_proctitle("kore [parent]"); |
422 | kore_msg_init(); | |
338 | 423 | kore_worker_init(); |
339 | 424 | |
425 | /* Set worker_max_connections for kore_connection_init(). */ | |
426 | tmp = worker_max_connections; | |
427 | worker_max_connections = worker_count; | |
428 | ||
429 | net_init(); | |
430 | kore_connection_init(); | |
431 | kore_platform_event_init(); | |
432 | kore_msg_parent_init(); | |
433 | ||
340 | 434 | quit = 0; |
435 | worker_max_connections = tmp; | |
341 | 436 | while (quit != 1) { |
342 | 437 | if (sig_recv != 0) { |
343 | 438 | switch (sig_recv) { |
344 | 439 | case SIGHUP: |
440 | #if !defined(KORE_SINGLE_BINARY) | |
345 | 441 | kore_worker_dispatch_signal(sig_recv); |
346 | 442 | kore_module_reload(0); |
443 | #endif | |
347 | 444 | break; |
348 | 445 | case SIGINT: |
349 | 446 | case SIGQUIT: |
447 | case SIGTERM: | |
350 | 448 | quit = 1; |
351 | 449 | kore_worker_dispatch_signal(sig_recv); |
352 | 450 | continue; |
359 | 457 | sig_recv = 0; |
360 | 458 | } |
361 | 459 | |
362 | if (!kore_accesslog_wait()) | |
363 | break; | |
364 | ||
365 | 460 | kore_worker_wait(0); |
366 | } | |
461 | kore_platform_event_wait(100); | |
462 | kore_connection_prune(KORE_CONNECTION_PRUNE_DISCONNECT); | |
463 | } | |
464 | ||
465 | kore_platform_event_cleanup(); | |
466 | kore_connection_cleanup(); | |
467 | kore_domain_cleanup(); | |
468 | net_cleanup(); | |
367 | 469 | } |
368 | 470 | |
369 | 471 | static void |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
70 | 70 | events = kore_calloc(event_count, sizeof(struct epoll_event)); |
71 | 71 | } |
72 | 72 | |
73 | void | |
74 | kore_platform_event_cleanup(void) | |
75 | { | |
76 | if (efd != -1) { | |
77 | close(efd); | |
78 | efd = -1; | |
79 | } | |
80 | ||
81 | if (events != NULL) { | |
82 | kore_free(events); | |
83 | events = NULL; | |
84 | } | |
85 | } | |
86 | ||
73 | 87 | int |
74 | 88 | kore_platform_event_wait(u_int64_t timer) |
75 | 89 | { |
153 | 167 | !(c->flags & CONN_WRITE_BLOCK)) |
154 | 168 | c->flags |= CONN_WRITE_POSSIBLE; |
155 | 169 | |
156 | if (!kore_connection_handle(c)) | |
170 | if (c->handle != NULL && !c->handle(c)) | |
157 | 171 | kore_connection_disconnect(c); |
158 | 172 | break; |
159 | 173 | #if defined(KORE_USE_PGSQL) |
208 | 222 | } |
209 | 223 | |
210 | 224 | void |
225 | kore_platform_schedule_write(int fd, void *data) | |
226 | { | |
227 | kore_platform_event_schedule(fd, EPOLLOUT, 0, data); | |
228 | } | |
229 | ||
230 | void | |
211 | 231 | kore_platform_disable_read(int fd) |
212 | 232 | { |
213 | 233 | if (epoll_ctl(efd, EPOLL_CTL_DEL, fd, NULL) == -1) |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
20 | 20 | |
21 | 21 | #include "kore.h" |
22 | 22 | |
23 | #define KORE_MEM_BLOCKS 11 | |
24 | #define KORE_MEM_BLOCK_SIZE_MAX 8192 | |
25 | #define KORE_MEM_BLOCK_PREALLOC 128 | |
26 | ||
27 | #define KORE_MEM_ALIGN (sizeof(size_t)) | |
23 | 28 | #define KORE_MEM_MAGIC 0xd0d0 |
24 | 29 | #define KORE_MEMSIZE(x) \ |
25 | (*(u_int32_t *)((u_int8_t *)x - sizeof(u_int32_t))) | |
30 | (*(size_t *)((u_int8_t *)x - sizeof(size_t))) | |
26 | 31 | #define KORE_MEMINFO(x) \ |
27 | 32 | (struct meminfo *)((u_int8_t *)x + KORE_MEMSIZE(x)) |
28 | 33 | |
30 | 35 | u_int16_t magic; |
31 | 36 | }; |
32 | 37 | |
38 | struct memblock { | |
39 | struct kore_pool pool; | |
40 | }; | |
41 | ||
42 | static size_t memblock_index(size_t); | |
43 | ||
44 | static struct memblock blocks[KORE_MEM_BLOCKS]; | |
45 | ||
33 | 46 | void |
34 | 47 | kore_mem_init(void) |
35 | 48 | { |
49 | int i, len; | |
50 | char name[32]; | |
51 | u_int32_t size, elm, mlen; | |
52 | ||
53 | size = 8; | |
54 | ||
55 | for (i = 0; i < KORE_MEM_BLOCKS; i++) { | |
56 | len = snprintf(name, sizeof(name), "block-%u", size); | |
57 | if (len == -1 || (size_t)len >= sizeof(name)) | |
58 | fatal("kore_mem_init: snprintf"); | |
59 | ||
60 | elm = (KORE_MEM_BLOCK_PREALLOC * 1024) / size; | |
61 | mlen = sizeof(size_t) + size + | |
62 | sizeof(struct meminfo) + KORE_MEM_ALIGN; | |
63 | mlen = mlen & ~(KORE_MEM_ALIGN - 1); | |
64 | ||
65 | kore_pool_init(&blocks[i].pool, name, mlen, elm); | |
66 | ||
67 | size = size << 1; | |
68 | } | |
36 | 69 | } |
37 | 70 | |
38 | 71 | void * |
39 | 72 | kore_malloc(size_t len) |
40 | 73 | { |
41 | size_t mlen; | |
42 | 74 | void *ptr; |
43 | 75 | struct meminfo *mem; |
44 | 76 | u_int8_t *addr; |
45 | u_int32_t *plen; | |
77 | size_t mlen, idx, *plen; | |
46 | 78 | |
47 | 79 | if (len == 0) |
48 | 80 | fatal("kore_malloc(): zero size"); |
49 | 81 | |
50 | mlen = sizeof(u_int32_t) + len + sizeof(struct meminfo); | |
51 | if ((ptr = malloc(mlen)) == NULL) | |
52 | fatal("kore_malloc(%d): %d", len, errno); | |
82 | if (len <= KORE_MEM_BLOCK_SIZE_MAX) { | |
83 | idx = memblock_index(len); | |
84 | ptr = kore_pool_get(&blocks[idx].pool); | |
85 | } else { | |
86 | mlen = sizeof(size_t) + len + sizeof(struct meminfo); | |
87 | if ((ptr = calloc(1, mlen)) == NULL) | |
88 | fatal("kore_malloc(%zd): %d", len, errno); | |
89 | } | |
53 | 90 | |
54 | plen = (u_int32_t *)ptr; | |
91 | plen = (size_t *)ptr; | |
55 | 92 | *plen = len; |
56 | addr = (u_int8_t *)ptr + sizeof(u_int32_t); | |
93 | addr = (u_int8_t *)ptr + sizeof(size_t); | |
57 | 94 | |
58 | 95 | mem = KORE_MEMINFO(addr); |
59 | 96 | mem->magic = KORE_MEM_MAGIC; |
60 | ||
61 | #if defined(KORE_PEDANTIC_MALLOC) | |
62 | explicit_bzero(addr, len); | |
63 | #endif | |
64 | 97 | |
65 | 98 | return (addr); |
66 | 99 | } |
83 | 116 | |
84 | 117 | nptr = kore_malloc(len); |
85 | 118 | memcpy(nptr, ptr, MIN(len, KORE_MEMSIZE(ptr))); |
86 | kore_mem_free(ptr); | |
119 | kore_free(ptr); | |
87 | 120 | } |
88 | 121 | |
89 | 122 | return (nptr); |
95 | 128 | if (memb == 0 || len == 0) |
96 | 129 | fatal("kore_calloc(): zero size"); |
97 | 130 | if (SIZE_MAX / memb < len) |
98 | fatal("kore_calloc: memb * len > SIZE_MAX"); | |
131 | fatal("kore_calloc(): memb * len > SIZE_MAX"); | |
99 | 132 | |
100 | 133 | return (kore_malloc(memb * len)); |
101 | 134 | } |
102 | 135 | |
103 | 136 | void |
104 | kore_mem_free(void *ptr) | |
137 | kore_free(void *ptr) | |
105 | 138 | { |
106 | u_int8_t *addr; | |
107 | struct meminfo *mem; | |
139 | u_int8_t *addr; | |
140 | struct meminfo *mem; | |
141 | size_t len, idx; | |
108 | 142 | |
109 | 143 | if (ptr == NULL) |
110 | 144 | return; |
111 | 145 | |
112 | 146 | mem = KORE_MEMINFO(ptr); |
113 | 147 | if (mem->magic != KORE_MEM_MAGIC) |
114 | fatal("kore_mem_free(): magic boundary not found"); | |
148 | fatal("kore_free(): magic boundary not found"); | |
115 | 149 | |
116 | #if defined(KORE_PEDANTIC_MALLOC) | |
117 | explicit_bzero(ptr, KORE_MEMSIZE(ptr)); | |
118 | #endif | |
150 | len = KORE_MEMSIZE(ptr); | |
151 | addr = (u_int8_t *)ptr - sizeof(size_t); | |
119 | 152 | |
120 | addr = (u_int8_t *)ptr - sizeof(u_int32_t); | |
121 | free(addr); | |
153 | if (len <= KORE_MEM_BLOCK_SIZE_MAX) { | |
154 | idx = memblock_index(len); | |
155 | kore_pool_put(&blocks[idx].pool, addr); | |
156 | } else { | |
157 | free(addr); | |
158 | } | |
122 | 159 | } |
123 | 160 | |
124 | 161 | char * |
129 | 166 | |
130 | 167 | len = strlen(str) + 1; |
131 | 168 | nstr = kore_malloc(len); |
132 | kore_strlcpy(nstr, str, len); | |
169 | (void)kore_strlcpy(nstr, str, len); | |
133 | 170 | |
134 | 171 | return (nstr); |
135 | 172 | } |
136 | 173 | |
137 | #if defined(KORE_PEDANTIC_MALLOC) | |
138 | void | |
139 | explicit_bzero(void *addr, size_t len) | |
174 | static size_t | |
175 | memblock_index(size_t len) | |
140 | 176 | { |
141 | bzero(addr, len); | |
177 | size_t mlen, idx; | |
178 | ||
179 | idx = 0; | |
180 | mlen = 8; | |
181 | while (mlen < len) { | |
182 | idx++; | |
183 | mlen = mlen << 1; | |
184 | } | |
185 | ||
186 | if (idx > (KORE_MEM_BLOCKS - 1)) | |
187 | fatal("kore_malloc: idx too high"); | |
188 | ||
189 | return (idx); | |
142 | 190 | } |
143 | #endif |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
25 | 25 | kore_module_init(void) |
26 | 26 | { |
27 | 27 | TAILQ_INIT(&modules); |
28 | TAILQ_INIT(&domains); | |
28 | } | |
29 | ||
30 | void | |
31 | kore_module_cleanup(void) | |
32 | { | |
33 | struct kore_module *module, *next; | |
34 | ||
35 | for (module = TAILQ_FIRST(&modules); module != NULL; module = next) { | |
36 | next = TAILQ_NEXT(module, list); | |
37 | TAILQ_REMOVE(&modules, module, list); | |
38 | ||
39 | kore_free(module->path); | |
40 | (void)dlclose(module->handle); | |
41 | kore_free(module); | |
42 | } | |
29 | 43 | } |
30 | 44 | |
31 | 45 | void |
32 | 46 | kore_module_load(const char *path, const char *onload) |
33 | 47 | { |
48 | #if !defined(KORE_SINGLE_BINARY) | |
34 | 49 | struct stat st; |
50 | #endif | |
35 | 51 | struct kore_module *module; |
36 | 52 | |
37 | 53 | kore_debug("kore_module_load(%s, %s)", path, onload); |
38 | 54 | |
55 | module = kore_malloc(sizeof(struct kore_module)); | |
56 | module->onload = NULL; | |
57 | module->ocb = NULL; | |
58 | ||
59 | #if !defined(KORE_SINGLE_BINARY) | |
39 | 60 | if (stat(path, &st) == -1) |
40 | 61 | fatal("stat(%s): %s", path, errno_s); |
41 | 62 | |
42 | module = kore_malloc(sizeof(struct kore_module)); | |
43 | 63 | module->path = kore_strdup(path); |
44 | 64 | module->mtime = st.st_mtime; |
45 | module->onload = NULL; | |
46 | module->ocb = NULL; | |
65 | #else | |
66 | module->path = NULL; | |
67 | module->mtime = 0; | |
68 | #endif | |
47 | 69 | |
48 | 70 | module->handle = dlopen(module->path, RTLD_NOW | RTLD_GLOBAL); |
49 | 71 | if (module->handle == NULL) |
51 | 73 | |
52 | 74 | if (onload != NULL) { |
53 | 75 | module->onload = kore_strdup(onload); |
54 | module->ocb = dlsym(module->handle, onload); | |
76 | *(void **)&(module->ocb) = dlsym(module->handle, onload); | |
55 | 77 | if (module->ocb == NULL) |
56 | 78 | fatal("%s: onload '%s' not present", path, onload); |
57 | 79 | } |
62 | 84 | void |
63 | 85 | kore_module_onload(void) |
64 | 86 | { |
87 | #if !defined(KORE_SINGLE_BINARY) | |
65 | 88 | struct kore_module *module; |
66 | 89 | |
67 | 90 | TAILQ_FOREACH(module, &modules, list) { |
70 | 93 | |
71 | 94 | (void)module->ocb(KORE_MODULE_LOAD); |
72 | 95 | } |
96 | #endif | |
73 | 97 | } |
74 | 98 | |
75 | 99 | void |
76 | 100 | kore_module_reload(int cbs) |
77 | 101 | { |
102 | #if !defined(KORE_SINGLE_BINARY) | |
78 | 103 | struct stat st; |
79 | 104 | struct kore_domain *dom; |
80 | 105 | struct kore_module_handle *hdlr; |
107 | 132 | fatal("kore_module_reload(): %s", dlerror()); |
108 | 133 | |
109 | 134 | if (module->onload != NULL) { |
110 | module->ocb = dlsym(module->handle, module->onload); | |
135 | *(void **)&(module->ocb) = | |
136 | dlsym(module->handle, module->onload); | |
111 | 137 | if (module->ocb == NULL) { |
112 | 138 | fatal("%s: onload '%s' not present", |
113 | 139 | module->path, module->onload); |
129 | 155 | } |
130 | 156 | } |
131 | 157 | |
158 | #if !defined(KORE_NO_HTTP) | |
132 | 159 | kore_validator_reload(); |
160 | #endif | |
161 | #endif | |
133 | 162 | } |
134 | 163 | |
135 | 164 | int |
141 | 170 | return (1); |
142 | 171 | } |
143 | 172 | |
173 | #if !defined(KORE_NO_HTTP) | |
144 | 174 | int |
145 | 175 | kore_module_handler_new(const char *path, const char *domain, |
146 | 176 | const char *func, const char *auth, int type) |
182 | 212 | if (hdlr->type == HANDLER_TYPE_DYNAMIC) { |
183 | 213 | if (regcomp(&(hdlr->rctx), hdlr->path, |
184 | 214 | REG_EXTENDED | REG_NOSUB)) { |
185 | kore_mem_free(hdlr->func); | |
186 | kore_mem_free(hdlr->path); | |
187 | kore_mem_free(hdlr); | |
215 | kore_module_handler_free(hdlr); | |
188 | 216 | kore_debug("regcomp() on %s failed", path); |
189 | 217 | return (KORE_RESULT_ERROR); |
190 | 218 | } |
192 | 220 | |
193 | 221 | TAILQ_INSERT_TAIL(&(dom->handlers), hdlr, list); |
194 | 222 | return (KORE_RESULT_OK); |
223 | } | |
224 | ||
225 | void | |
226 | kore_module_handler_free(struct kore_module_handle *hdlr) | |
227 | { | |
228 | struct kore_handler_params *param; | |
229 | ||
230 | if (hdlr == NULL) | |
231 | return; | |
232 | ||
233 | if (hdlr->func != NULL) | |
234 | kore_free(hdlr->func); | |
235 | if (hdlr->path != NULL) | |
236 | kore_free(hdlr->path); | |
237 | if (hdlr->type == HANDLER_TYPE_DYNAMIC) | |
238 | regfree(&(hdlr->rctx)); | |
239 | ||
240 | /* Drop all validators associated with this handler */ | |
241 | while ((param = TAILQ_FIRST(&(hdlr->params))) != NULL) { | |
242 | TAILQ_REMOVE(&(hdlr->params), param, list); | |
243 | if (param->name != NULL) | |
244 | kore_free(param->name); | |
245 | kore_free(param); | |
246 | } | |
247 | ||
248 | kore_free(hdlr); | |
195 | 249 | } |
196 | 250 | |
197 | 251 | struct kore_module_handle * |
216 | 270 | return (NULL); |
217 | 271 | } |
218 | 272 | |
273 | #endif /* !KORE_NO_HTTP */ | |
274 | ||
219 | 275 | void * |
220 | 276 | kore_module_getsym(const char *symbol) |
221 | 277 | { |
0 | /* | |
1 | * Copyright (c) 2016 Joris Vink <joris@coders.se> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | #include <sys/socket.h> | |
17 | ||
18 | #include <signal.h> | |
19 | ||
20 | #include "kore.h" | |
21 | #include "http.h" | |
22 | ||
23 | struct msg_type { | |
24 | u_int8_t id; | |
25 | void (*cb)(struct kore_msg *, const void *); | |
26 | TAILQ_ENTRY(msg_type) list; | |
27 | }; | |
28 | ||
29 | TAILQ_HEAD(, msg_type) msg_types; | |
30 | ||
31 | static struct msg_type *msg_type_lookup(u_int8_t); | |
32 | static int msg_recv_packet(struct netbuf *); | |
33 | static int msg_recv_data(struct netbuf *); | |
34 | static void msg_disconnected_parent(struct connection *); | |
35 | static void msg_disconnected_worker(struct connection *); | |
36 | ||
37 | #if !defined(KORE_NO_HTTP) | |
38 | static void msg_type_accesslog(struct kore_msg *, const void *); | |
39 | static void msg_type_websocket(struct kore_msg *, const void *); | |
40 | #endif | |
41 | ||
42 | void | |
43 | kore_msg_init(void) | |
44 | { | |
45 | TAILQ_INIT(&msg_types); | |
46 | } | |
47 | ||
48 | void | |
49 | kore_msg_parent_init(void) | |
50 | { | |
51 | u_int8_t i; | |
52 | struct kore_worker *kw; | |
53 | ||
54 | for (i = 0; i < worker_count; i++) { | |
55 | kw = kore_worker_data(i); | |
56 | kore_msg_parent_add(kw); | |
57 | } | |
58 | ||
59 | #if !defined(KORE_NO_HTTP) | |
60 | kore_msg_register(KORE_MSG_ACCESSLOG, msg_type_accesslog); | |
61 | #endif | |
62 | } | |
63 | ||
64 | void | |
65 | kore_msg_parent_add(struct kore_worker *kw) | |
66 | { | |
67 | kw->msg[0] = kore_connection_new(NULL); | |
68 | kw->msg[0]->fd = kw->pipe[0]; | |
69 | kw->msg[0]->read = net_read; | |
70 | kw->msg[0]->write = net_write; | |
71 | kw->msg[0]->proto = CONN_PROTO_MSG; | |
72 | kw->msg[0]->state = CONN_STATE_ESTABLISHED; | |
73 | kw->msg[0]->hdlr_extra = &kw->id; | |
74 | kw->msg[0]->disconnect = msg_disconnected_worker; | |
75 | kw->msg[0]->handle = kore_connection_handle; | |
76 | ||
77 | TAILQ_INSERT_TAIL(&connections, kw->msg[0], list); | |
78 | kore_platform_event_all(kw->msg[0]->fd, kw->msg[0]); | |
79 | ||
80 | net_recv_queue(kw->msg[0], sizeof(struct kore_msg), 0, msg_recv_packet); | |
81 | } | |
82 | ||
83 | void | |
84 | kore_msg_parent_remove(struct kore_worker *kw) | |
85 | { | |
86 | kore_connection_disconnect(kw->msg[0]); | |
87 | kore_connection_prune(KORE_CONNECTION_PRUNE_DISCONNECT); | |
88 | (void)close(kw->pipe[1]); | |
89 | } | |
90 | ||
91 | void | |
92 | kore_msg_worker_init(void) | |
93 | { | |
94 | #if !defined(KORE_NO_HTTP) | |
95 | kore_msg_register(KORE_MSG_WEBSOCKET, msg_type_websocket); | |
96 | #endif | |
97 | ||
98 | worker->msg[1] = kore_connection_new(NULL); | |
99 | worker->msg[1]->fd = worker->pipe[1]; | |
100 | worker->msg[1]->read = net_read; | |
101 | worker->msg[1]->write = net_write; | |
102 | worker->msg[1]->proto = CONN_PROTO_MSG; | |
103 | worker->msg[1]->state = CONN_STATE_ESTABLISHED; | |
104 | worker->msg[1]->disconnect = msg_disconnected_parent; | |
105 | worker->msg[1]->handle = kore_connection_handle; | |
106 | ||
107 | TAILQ_INSERT_TAIL(&connections, worker->msg[1], list); | |
108 | kore_platform_event_all(worker->msg[1]->fd, worker->msg[1]); | |
109 | ||
110 | net_recv_queue(worker->msg[1], | |
111 | sizeof(struct kore_msg), 0, msg_recv_packet); | |
112 | } | |
113 | ||
114 | int | |
115 | kore_msg_register(u_int8_t id, void (*cb)(struct kore_msg *, const void *)) | |
116 | { | |
117 | struct msg_type *type; | |
118 | ||
119 | if ((type = msg_type_lookup(id)) != NULL) | |
120 | return (KORE_RESULT_ERROR); | |
121 | ||
122 | type = kore_malloc(sizeof(*type)); | |
123 | type->id = id; | |
124 | type->cb = cb; | |
125 | TAILQ_INSERT_TAIL(&msg_types, type, list); | |
126 | ||
127 | return (KORE_RESULT_OK); | |
128 | } | |
129 | ||
130 | void | |
131 | kore_msg_send(u_int16_t dst, u_int8_t id, const void *data, u_int32_t len) | |
132 | { | |
133 | struct kore_msg m; | |
134 | ||
135 | m.id = id; | |
136 | m.dst = dst; | |
137 | m.length = len; | |
138 | m.src = worker->id; | |
139 | ||
140 | net_send_queue(worker->msg[1], &m, sizeof(m)); | |
141 | net_send_queue(worker->msg[1], data, len); | |
142 | net_send_flush(worker->msg[1]); | |
143 | } | |
144 | ||
145 | static int | |
146 | msg_recv_packet(struct netbuf *nb) | |
147 | { | |
148 | struct kore_msg *msg = (struct kore_msg *)nb->buf; | |
149 | ||
150 | net_recv_expand(nb->owner, msg->length, msg_recv_data); | |
151 | return (KORE_RESULT_OK); | |
152 | } | |
153 | ||
154 | static int | |
155 | msg_recv_data(struct netbuf *nb) | |
156 | { | |
157 | struct connection *c; | |
158 | struct msg_type *type; | |
159 | u_int16_t destination; | |
160 | struct kore_msg *msg = (struct kore_msg *)nb->buf; | |
161 | ||
162 | if ((type = msg_type_lookup(msg->id)) != NULL) { | |
163 | if (worker == NULL && msg->dst != KORE_MSG_PARENT) | |
164 | fatal("received parent msg for non parent dst"); | |
165 | if (worker != NULL && msg->dst != worker->id) | |
166 | fatal("received message for incorrect worker"); | |
167 | ||
168 | type->cb(msg, nb->buf + sizeof(*msg)); | |
169 | } | |
170 | ||
171 | if (worker == NULL && type == NULL) { | |
172 | destination = msg->dst; | |
173 | TAILQ_FOREACH(c, &connections, list) { | |
174 | if (c == nb->owner) | |
175 | continue; | |
176 | if (c->proto != CONN_PROTO_MSG || c->hdlr_extra == NULL) | |
177 | continue; | |
178 | ||
179 | if (destination != KORE_MSG_WORKER_ALL && | |
180 | *(u_int8_t *)c->hdlr_extra != destination) | |
181 | continue; | |
182 | ||
183 | /* This allows the worker to receive the correct id. */ | |
184 | msg->dst = *(u_int8_t *)c->hdlr_extra; | |
185 | ||
186 | net_send_queue(c, nb->buf, nb->s_off); | |
187 | net_send_flush(c); | |
188 | } | |
189 | } | |
190 | ||
191 | net_recv_reset(nb->owner, sizeof(struct kore_msg), msg_recv_packet); | |
192 | return (KORE_RESULT_OK); | |
193 | } | |
194 | ||
195 | static void | |
196 | msg_disconnected_parent(struct connection *c) | |
197 | { | |
198 | kore_log(LOG_ERR, "parent gone, shutting down"); | |
199 | if (kill(worker->pid, SIGQUIT) == -1) | |
200 | kore_log(LOG_ERR, "failed to send SIGQUIT: %s", errno_s); | |
201 | } | |
202 | ||
203 | static void | |
204 | msg_disconnected_worker(struct connection *c) | |
205 | { | |
206 | c->hdlr_extra = NULL; | |
207 | } | |
208 | ||
209 | #if !defined(KORE_NO_HTTP) | |
210 | static void | |
211 | msg_type_accesslog(struct kore_msg *msg, const void *data) | |
212 | { | |
213 | if (kore_accesslog_write(data, msg->length) == -1) | |
214 | kore_log(LOG_WARNING, "failed to write to accesslog"); | |
215 | } | |
216 | ||
217 | static void | |
218 | msg_type_websocket(struct kore_msg *msg, const void *data) | |
219 | { | |
220 | struct connection *c; | |
221 | ||
222 | TAILQ_FOREACH(c, &connections, list) { | |
223 | if (c->proto == CONN_PROTO_WEBSOCKET) { | |
224 | net_send_queue(c, data, msg->length); | |
225 | net_send_flush(c); | |
226 | } | |
227 | } | |
228 | } | |
229 | #endif | |
230 | ||
231 | static struct msg_type * | |
232 | msg_type_lookup(u_int8_t id) | |
233 | { | |
234 | struct msg_type *type; | |
235 | ||
236 | TAILQ_FOREACH(type, &msg_types, list) { | |
237 | if (type->id == id) | |
238 | return (type); | |
239 | } | |
240 | ||
241 | return (NULL); | |
242 | } |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
36 | 36 | } |
37 | 37 | |
38 | 38 | void |
39 | net_send_queue(struct connection *c, void *data, u_int32_t len, | |
40 | struct spdy_stream *s, int before) | |
41 | { | |
42 | u_int8_t *d; | |
39 | net_cleanup(void) | |
40 | { | |
41 | kore_debug("net_cleanup()"); | |
42 | kore_pool_cleanup(&nb_pool); | |
43 | } | |
44 | ||
45 | void | |
46 | net_send_queue(struct connection *c, const void *data, size_t len) | |
47 | { | |
48 | const u_int8_t *d; | |
43 | 49 | struct netbuf *nb; |
44 | u_int32_t avail; | |
45 | ||
46 | kore_debug("net_send_queue(%p, %p, %d, %p, %d)", | |
47 | c, data, len, s, before); | |
50 | size_t avail; | |
51 | ||
52 | kore_debug("net_send_queue(%p, %p, %zu)", c, data, len); | |
48 | 53 | |
49 | 54 | d = data; |
50 | if (before == NETBUF_LAST_CHAIN) { | |
51 | nb = TAILQ_LAST(&(c->send_queue), netbuf_head); | |
52 | if (nb != NULL && !(nb->flags & NETBUF_IS_STREAM) && | |
53 | nb->stream == s && nb->b_len < nb->m_len) { | |
54 | avail = nb->m_len - nb->b_len; | |
55 | if (len < avail) { | |
56 | memcpy(nb->buf + nb->b_len, d, len); | |
57 | nb->b_len += len; | |
55 | nb = TAILQ_LAST(&(c->send_queue), netbuf_head); | |
56 | if (nb != NULL && !(nb->flags & NETBUF_IS_STREAM) && | |
57 | nb->b_len < nb->m_len) { | |
58 | avail = nb->m_len - nb->b_len; | |
59 | if (len < avail) { | |
60 | memcpy(nb->buf + nb->b_len, d, len); | |
61 | nb->b_len += len; | |
62 | return; | |
63 | } else { | |
64 | memcpy(nb->buf + nb->b_len, d, avail); | |
65 | nb->b_len += avail; | |
66 | ||
67 | len -= avail; | |
68 | d += avail; | |
69 | if (len == 0) | |
58 | 70 | return; |
59 | } else if (len > avail) { | |
60 | memcpy(nb->buf + nb->b_len, d, avail); | |
61 | nb->b_len += avail; | |
62 | ||
63 | len -= avail; | |
64 | d += avail; | |
65 | if (len == 0) | |
66 | return; | |
67 | } | |
68 | 71 | } |
69 | 72 | } |
70 | 73 | |
73 | 76 | nb->cb = NULL; |
74 | 77 | nb->owner = c; |
75 | 78 | nb->s_off = 0; |
76 | nb->stream = s; | |
77 | 79 | nb->b_len = len; |
78 | 80 | nb->type = NETBUF_SEND; |
79 | 81 | |
86 | 88 | if (len > 0) |
87 | 89 | memcpy(nb->buf, d, nb->b_len); |
88 | 90 | |
89 | if (before == NETBUF_BEFORE_CHAIN) { | |
90 | TAILQ_INSERT_BEFORE(c->snb, nb, list); | |
91 | } else { | |
92 | TAILQ_INSERT_TAIL(&(c->send_queue), nb, list); | |
93 | } | |
94 | } | |
95 | ||
96 | void | |
97 | net_send_stream(struct connection *c, void *data, u_int32_t len, | |
98 | struct spdy_stream *s, int (*cb)(struct netbuf *), struct netbuf **out) | |
91 | TAILQ_INSERT_TAIL(&(c->send_queue), nb, list); | |
92 | } | |
93 | ||
94 | void | |
95 | net_send_stream(struct connection *c, void *data, size_t len, | |
96 | int (*cb)(struct netbuf *), struct netbuf **out) | |
99 | 97 | { |
100 | 98 | struct netbuf *nb; |
101 | 99 | |
102 | kore_debug("net_send_stream(%p, %p, %d, %p)", c, data, len, s); | |
100 | kore_debug("net_send_stream(%p, %p, %zu)", c, data, len); | |
103 | 101 | |
104 | 102 | nb = kore_pool_get(&nb_pool); |
105 | 103 | nb->cb = cb; |
106 | 104 | nb->owner = c; |
107 | 105 | nb->s_off = 0; |
108 | 106 | nb->buf = data; |
109 | nb->stream = s; | |
110 | 107 | nb->b_len = len; |
111 | 108 | nb->m_len = nb->b_len; |
112 | 109 | nb->type = NETBUF_SEND; |
118 | 115 | } |
119 | 116 | |
120 | 117 | void |
121 | net_recv_reset(struct connection *c, u_int32_t len, int (*cb)(struct netbuf *)) | |
122 | { | |
123 | kore_debug("net_recv_reset(): %p %d", c, len); | |
118 | net_recv_reset(struct connection *c, size_t len, int (*cb)(struct netbuf *)) | |
119 | { | |
120 | kore_debug("net_recv_reset(): %p %zu", c, len); | |
124 | 121 | |
125 | 122 | if (c->rnb->type != NETBUF_RECV) |
126 | 123 | fatal("net_recv_reset(): wrong netbuf type"); |
133 | 130 | c->rnb->m_len < (NETBUF_SEND_PAYLOAD_MAX / 2)) |
134 | 131 | return; |
135 | 132 | |
136 | kore_mem_free(c->rnb->buf); | |
133 | kore_free(c->rnb->buf); | |
137 | 134 | c->rnb->m_len = len; |
138 | 135 | c->rnb->buf = kore_malloc(c->rnb->m_len); |
139 | 136 | } |
140 | 137 | |
141 | 138 | void |
142 | net_recv_queue(struct connection *c, u_int32_t len, int flags, | |
139 | net_recv_queue(struct connection *c, size_t len, int flags, | |
143 | 140 | int (*cb)(struct netbuf *)) |
144 | 141 | { |
145 | kore_debug("net_recv_queue(): %p %d %d", c, len, flags); | |
142 | kore_debug("net_recv_queue(): %p %zu %d", c, len, flags); | |
146 | 143 | |
147 | 144 | if (c->rnb != NULL) |
148 | 145 | fatal("net_recv_queue(): called incorrectly for %p", c); |
154 | 151 | c->rnb->b_len = len; |
155 | 152 | c->rnb->m_len = len; |
156 | 153 | c->rnb->extra = NULL; |
157 | c->rnb->stream = NULL; | |
158 | 154 | c->rnb->flags = flags; |
159 | 155 | c->rnb->type = NETBUF_RECV; |
160 | 156 | c->rnb->buf = kore_malloc(c->rnb->b_len); |
161 | 157 | } |
162 | 158 | |
163 | 159 | void |
164 | net_recv_expand(struct connection *c, u_int32_t len, int (*cb)(struct netbuf *)) | |
160 | net_recv_expand(struct connection *c, size_t len, int (*cb)(struct netbuf *)) | |
165 | 161 | { |
166 | 162 | kore_debug("net_recv_expand(): %p %d", c, len); |
167 | 163 | |
182 | 178 | |
183 | 179 | c->snb = TAILQ_FIRST(&(c->send_queue)); |
184 | 180 | if (c->snb->b_len != 0) { |
185 | if (c->snb->stream != NULL && | |
186 | (c->snb->stream->flags & SPDY_DATAFRAME_PRELUDE)) { | |
187 | if (!spdy_dataframe_begin(c)) { | |
188 | c->snb = NULL; | |
189 | return (KORE_RESULT_OK); | |
190 | } | |
191 | ||
192 | c->snb = TAILQ_FIRST(&(c->send_queue)); | |
193 | } | |
194 | ||
195 | 181 | smin = c->snb->b_len - c->snb->s_off; |
196 | if (c->snb->stream != NULL && | |
197 | c->snb->stream->frame_size > 0) { | |
198 | smin = MIN(smin, c->snb->stream->frame_size); | |
199 | } | |
200 | ||
201 | 182 | len = MIN(NETBUF_SEND_PAYLOAD_MAX, smin); |
202 | 183 | |
203 | 184 | if (!c->write(c, len, &r)) |
210 | 191 | |
211 | 192 | c->snb->s_off += (size_t)r; |
212 | 193 | c->snb->flags &= ~NETBUF_MUST_RESEND; |
213 | if (c->snb->stream != NULL) | |
214 | spdy_update_wsize(c, c->snb->stream, r); | |
215 | 194 | } |
216 | 195 | |
217 | 196 | if (c->snb->s_off == c->snb->b_len || |
248 | 227 | kore_debug("net_recv_flush(%p)", c); |
249 | 228 | |
250 | 229 | if (c->rnb == NULL) |
251 | fatal("net_recv_flush(): c->rnb == NULL"); | |
230 | return (KORE_RESULT_OK); | |
252 | 231 | |
253 | 232 | while (c->flags & CONN_READ_POSSIBLE) { |
254 | 233 | if (!c->read(c, &r)) |
274 | 253 | void |
275 | 254 | net_remove_netbuf(struct netbuf_head *list, struct netbuf *nb) |
276 | 255 | { |
277 | kore_debug("net_remove_netbuf(%p, %p, %p)", list, nb, nb->stream); | |
256 | kore_debug("net_remove_netbuf(%p, %p)", list, nb); | |
278 | 257 | |
279 | 258 | if (nb->type == NETBUF_RECV) |
280 | 259 | fatal("net_remove_netbuf(): cannot remove recv netbuf"); |
281 | 260 | |
282 | nb->stream = NULL; | |
283 | 261 | if (nb->flags & NETBUF_MUST_RESEND) { |
284 | 262 | kore_debug("retaining %p (MUST_RESEND)", nb); |
285 | 263 | nb->flags |= NETBUF_FORCE_REMOVE; |
287 | 265 | } |
288 | 266 | |
289 | 267 | if (!(nb->flags & NETBUF_IS_STREAM)) { |
290 | kore_mem_free(nb->buf); | |
268 | kore_free(nb->buf); | |
291 | 269 | } else if (nb->cb != NULL) { |
292 | 270 | (void)nb->cb(nb); |
293 | 271 | } |
296 | 274 | kore_pool_put(&nb_pool, nb); |
297 | 275 | } |
298 | 276 | |
299 | #if !defined(KORE_BENCHMARK) | |
277 | #if !defined(KORE_NO_TLS) | |
300 | 278 | int |
301 | 279 | net_write_ssl(struct connection *c, int len, int *written) |
302 | 280 | { |
314 | 292 | c->snb->flags |= NETBUF_MUST_RESEND; |
315 | 293 | c->flags &= ~CONN_WRITE_POSSIBLE; |
316 | 294 | return (KORE_RESULT_OK); |
295 | case SSL_ERROR_SYSCALL: | |
296 | switch (errno) { | |
297 | case EINTR: | |
298 | *written = 0; | |
299 | return (KORE_RESULT_OK); | |
300 | case EAGAIN: | |
301 | c->snb->flags |= NETBUF_MUST_RESEND; | |
302 | c->flags &= ~CONN_WRITE_POSSIBLE; | |
303 | return (KORE_RESULT_OK); | |
304 | default: | |
305 | break; | |
306 | } | |
307 | /* FALLTHROUGH */ | |
317 | 308 | default: |
318 | 309 | kore_debug("SSL_write(): %s", ssl_errno_s); |
319 | 310 | return (KORE_RESULT_ERROR); |
342 | 333 | case SSL_ERROR_WANT_WRITE: |
343 | 334 | c->flags &= ~CONN_READ_POSSIBLE; |
344 | 335 | return (KORE_RESULT_OK); |
336 | case SSL_ERROR_SYSCALL: | |
337 | switch (errno) { | |
338 | case EINTR: | |
339 | *bytes = 0; | |
340 | return (KORE_RESULT_OK); | |
341 | case EAGAIN: | |
342 | c->snb->flags |= NETBUF_MUST_RESEND; | |
343 | c->flags &= ~CONN_WRITE_POSSIBLE; | |
344 | return (KORE_RESULT_OK); | |
345 | default: | |
346 | break; | |
347 | } | |
348 | /* FALLTHROUGH */ | |
345 | 349 | default: |
346 | 350 | kore_debug("SSL_read(): %s", ssl_errno_s); |
347 | 351 | return (KORE_RESULT_ERROR); |
362 | 366 | if (r <= -1) { |
363 | 367 | switch (errno) { |
364 | 368 | case EINTR: |
369 | *written = 0; | |
370 | return (KORE_RESULT_OK); | |
365 | 371 | case EAGAIN: |
366 | 372 | c->flags &= ~CONN_WRITE_POSSIBLE; |
367 | 373 | return (KORE_RESULT_OK); |
385 | 391 | if (r <= 0) { |
386 | 392 | switch (errno) { |
387 | 393 | case EINTR: |
394 | *bytes = 0; | |
395 | return (KORE_RESULT_OK); | |
388 | 396 | case EAGAIN: |
389 | 397 | c->flags &= ~CONN_READ_POSSIBLE; |
390 | 398 | return (KORE_RESULT_OK); |
0 | 0 | /* |
1 | * Copyright (c) 2014 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2014-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
24 | 24 | #include "pgsql.h" |
25 | 25 | |
26 | 26 | struct pgsql_job { |
27 | char *query; | |
28 | 27 | struct http_request *req; |
29 | 28 | struct kore_pgsql *pgsql; |
30 | 29 | |
36 | 35 | TAILQ_ENTRY(pgsql_wait) list; |
37 | 36 | }; |
38 | 37 | |
39 | #define PGSQL_IS_BLOCKING 0 | |
40 | #define PGSQL_IS_ASYNC 1 | |
41 | ||
42 | 38 | #define PGSQL_CONN_MAX 2 |
43 | 39 | #define PGSQL_CONN_FREE 0x01 |
40 | #define PGSQL_LIST_INSERTED 0x0100 | |
44 | 41 | |
45 | 42 | static void pgsql_queue_wakeup(void); |
46 | static int pgsql_conn_create(struct kore_pgsql *); | |
43 | static void pgsql_set_error(struct kore_pgsql *, const char *); | |
47 | 44 | static void pgsql_queue_add(struct http_request *); |
48 | 45 | static void pgsql_conn_release(struct kore_pgsql *); |
49 | 46 | static void pgsql_conn_cleanup(struct pgsql_conn *); |
50 | static void pgsql_read_result(struct kore_pgsql *, int); | |
51 | static void pgsql_schedule(struct kore_pgsql *, struct http_request *); | |
52 | static int pgsql_prepare(struct kore_pgsql *, struct http_request *, | |
53 | const char *); | |
47 | static void pgsql_read_result(struct kore_pgsql *); | |
48 | static void pgsql_schedule(struct kore_pgsql *); | |
49 | ||
50 | static struct pgsql_conn *pgsql_conn_create(struct kore_pgsql *, | |
51 | struct pgsql_db *); | |
52 | static struct pgsql_conn *pgsql_conn_next(struct kore_pgsql *, | |
53 | struct pgsql_db *, | |
54 | struct http_request *); | |
54 | 55 | |
55 | 56 | static struct kore_pool pgsql_job_pool; |
56 | 57 | static struct kore_pool pgsql_wait_pool; |
57 | 58 | static TAILQ_HEAD(, pgsql_conn) pgsql_conn_free; |
58 | 59 | static TAILQ_HEAD(, pgsql_wait) pgsql_wait_queue; |
60 | static LIST_HEAD(, pgsql_db) pgsql_db_conn_strings; | |
59 | 61 | static u_int16_t pgsql_conn_count; |
60 | char *pgsql_conn_string = NULL; | |
61 | 62 | u_int16_t pgsql_conn_max = PGSQL_CONN_MAX; |
62 | 63 | |
63 | 64 | void |
66 | 67 | pgsql_conn_count = 0; |
67 | 68 | TAILQ_INIT(&pgsql_conn_free); |
68 | 69 | TAILQ_INIT(&pgsql_wait_queue); |
70 | LIST_INIT(&pgsql_db_conn_strings); | |
69 | 71 | |
70 | 72 | kore_pool_init(&pgsql_job_pool, "pgsql_job_pool", |
71 | 73 | sizeof(struct pgsql_job), 100); |
74 | 76 | } |
75 | 77 | |
76 | 78 | int |
77 | kore_pgsql_query(struct kore_pgsql *pgsql, struct http_request *req, | |
78 | const char *query) | |
79 | { | |
80 | if (!pgsql_prepare(pgsql, req, query)) | |
79 | kore_pgsql_query_init(struct kore_pgsql *pgsql, struct http_request *req, | |
80 | const char *dbname, int flags) | |
81 | { | |
82 | struct pgsql_db *db; | |
83 | ||
84 | memset(pgsql, 0, sizeof(*pgsql)); | |
85 | pgsql->flags = flags; | |
86 | pgsql->state = KORE_PGSQL_STATE_INIT; | |
87 | ||
88 | if ((req == NULL && (flags & KORE_PGSQL_ASYNC)) || | |
89 | ((flags & KORE_PGSQL_ASYNC) && (flags & KORE_PGSQL_SYNC))) { | |
90 | pgsql_set_error(pgsql, "invalid query init parameters"); | |
81 | 91 | return (KORE_RESULT_ERROR); |
82 | ||
83 | if (!PQsendQuery(pgsql->conn->db, query)) { | |
84 | pgsql_conn_cleanup(pgsql->conn); | |
92 | } | |
93 | ||
94 | db = NULL; | |
95 | LIST_FOREACH(db, &pgsql_db_conn_strings, rlist) { | |
96 | if (!strcmp(db->name, dbname)) | |
97 | break; | |
98 | } | |
99 | ||
100 | if (db == NULL) { | |
101 | pgsql_set_error(pgsql, "no database found"); | |
85 | 102 | return (KORE_RESULT_ERROR); |
86 | 103 | } |
87 | 104 | |
88 | pgsql_schedule(pgsql, req); | |
105 | if ((pgsql->conn = pgsql_conn_next(pgsql, db, req)) == NULL) | |
106 | return (KORE_RESULT_ERROR); | |
107 | ||
108 | if (pgsql->flags & KORE_PGSQL_ASYNC) { | |
109 | pgsql->conn->job = kore_pool_get(&pgsql_job_pool); | |
110 | pgsql->conn->job->req = req; | |
111 | pgsql->conn->job->pgsql = pgsql; | |
112 | ||
113 | http_request_sleep(req); | |
114 | pgsql->flags |= PGSQL_LIST_INSERTED; | |
115 | LIST_INSERT_HEAD(&(req->pgsqls), pgsql, rlist); | |
116 | } | |
117 | ||
89 | 118 | return (KORE_RESULT_OK); |
90 | 119 | } |
91 | 120 | |
92 | 121 | int |
93 | kore_pgsql_query_params(struct kore_pgsql *pgsql, struct http_request *req, | |
94 | const char *query, int result, u_int8_t count, ...) | |
122 | kore_pgsql_query(struct kore_pgsql *pgsql, const char *query) | |
123 | { | |
124 | if (pgsql->conn == NULL) { | |
125 | pgsql_set_error(pgsql, "no connection was set before query"); | |
126 | return (KORE_RESULT_ERROR); | |
127 | } | |
128 | ||
129 | if (pgsql->flags & KORE_PGSQL_SYNC) { | |
130 | pgsql->result = PQexec(pgsql->conn->db, query); | |
131 | ||
132 | if ((PQresultStatus(pgsql->result) != PGRES_TUPLES_OK) && | |
133 | (PQresultStatus(pgsql->result) != PGRES_COMMAND_OK)) { | |
134 | pgsql_set_error(pgsql, PQerrorMessage(pgsql->conn->db)); | |
135 | return (KORE_RESULT_ERROR); | |
136 | } | |
137 | ||
138 | pgsql->state = KORE_PGSQL_STATE_DONE; | |
139 | } else { | |
140 | if (!PQsendQuery(pgsql->conn->db, query)) { | |
141 | pgsql_set_error(pgsql, PQerrorMessage(pgsql->conn->db)); | |
142 | return (KORE_RESULT_ERROR); | |
143 | } | |
144 | ||
145 | pgsql_schedule(pgsql); | |
146 | } | |
147 | ||
148 | return (KORE_RESULT_OK); | |
149 | } | |
150 | ||
151 | int | |
152 | kore_pgsql_v_query_params(struct kore_pgsql *pgsql, | |
153 | const char *query, int result, u_int8_t count, va_list args) | |
95 | 154 | { |
96 | 155 | u_int8_t i; |
97 | va_list args; | |
98 | 156 | char **values; |
99 | int *lengths, *formats; | |
100 | ||
101 | if (!pgsql_prepare(pgsql, req, query)) | |
157 | int *lengths, *formats, ret; | |
158 | ||
159 | if (pgsql->conn == NULL) { | |
160 | pgsql_set_error(pgsql, "no connection was set before query"); | |
102 | 161 | return (KORE_RESULT_ERROR); |
162 | } | |
103 | 163 | |
104 | 164 | if (count > 0) { |
105 | va_start(args, count); | |
106 | ||
107 | 165 | lengths = kore_calloc(count, sizeof(int)); |
108 | 166 | formats = kore_calloc(count, sizeof(int)); |
109 | 167 | values = kore_calloc(count, sizeof(char *)); |
119 | 177 | values = NULL; |
120 | 178 | } |
121 | 179 | |
122 | if (!PQsendQueryParams(pgsql->conn->db, query, count, NULL, | |
123 | (const char * const *)values, lengths, formats, result)) { | |
124 | kore_mem_free(values); | |
125 | kore_mem_free(lengths); | |
126 | kore_mem_free(formats); | |
127 | pgsql_conn_cleanup(pgsql->conn); | |
128 | return (KORE_RESULT_ERROR); | |
129 | } | |
130 | ||
131 | kore_mem_free(values); | |
132 | kore_mem_free(lengths); | |
133 | kore_mem_free(formats); | |
134 | ||
135 | pgsql_schedule(pgsql, req); | |
180 | ret = KORE_RESULT_ERROR; | |
181 | ||
182 | if (pgsql->flags & KORE_PGSQL_SYNC) { | |
183 | pgsql->result = PQexecParams(pgsql->conn->db, query, count, | |
184 | NULL, (const char * const *)values, lengths, formats, | |
185 | result); | |
186 | ||
187 | if ((PQresultStatus(pgsql->result) != PGRES_TUPLES_OK) && | |
188 | (PQresultStatus(pgsql->result) != PGRES_COMMAND_OK)) { | |
189 | pgsql_set_error(pgsql, PQerrorMessage(pgsql->conn->db)); | |
190 | goto cleanup; | |
191 | } | |
192 | ||
193 | pgsql->state = KORE_PGSQL_STATE_DONE; | |
194 | } else { | |
195 | if (!PQsendQueryParams(pgsql->conn->db, query, count, NULL, | |
196 | (const char * const *)values, lengths, formats, result)) { | |
197 | pgsql_set_error(pgsql, PQerrorMessage(pgsql->conn->db)); | |
198 | goto cleanup; | |
199 | } | |
200 | ||
201 | pgsql_schedule(pgsql); | |
202 | } | |
203 | ||
204 | ret = KORE_RESULT_OK; | |
205 | ||
206 | cleanup: | |
207 | kore_free(values); | |
208 | kore_free(lengths); | |
209 | kore_free(formats); | |
210 | ||
211 | return (ret); | |
212 | } | |
213 | ||
214 | int | |
215 | kore_pgsql_query_params(struct kore_pgsql *pgsql, | |
216 | const char *query, int result, u_int8_t count, ...) | |
217 | { | |
218 | int ret; | |
219 | va_list args; | |
220 | ||
221 | if (count > 0) | |
222 | va_start(args, count); | |
223 | ||
224 | ret = kore_pgsql_v_query_params(pgsql, query, result, count, args); | |
225 | ||
226 | if (count > 0) | |
227 | va_end(args); | |
228 | ||
229 | return (ret); | |
230 | } | |
231 | ||
232 | int | |
233 | kore_pgsql_register(const char *dbname, const char *connstring) | |
234 | { | |
235 | struct pgsql_db *pgsqldb; | |
236 | ||
237 | LIST_FOREACH(pgsqldb, &pgsql_db_conn_strings, rlist) { | |
238 | if (!strcmp(pgsqldb->name, dbname)) | |
239 | return (KORE_RESULT_ERROR); | |
240 | } | |
241 | ||
242 | pgsqldb = kore_malloc(sizeof(*pgsqldb)); | |
243 | pgsqldb->name = kore_strdup(dbname); | |
244 | pgsqldb->conn_string = kore_strdup(connstring); | |
245 | LIST_INSERT_HEAD(&pgsql_db_conn_strings, pgsqldb, rlist); | |
246 | ||
136 | 247 | return (KORE_RESULT_OK); |
137 | 248 | } |
138 | 249 | |
156 | 267 | pgsql->state = KORE_PGSQL_STATE_ERROR; |
157 | 268 | pgsql->error = kore_strdup(PQerrorMessage(conn->db)); |
158 | 269 | } else { |
159 | pgsql_read_result(pgsql, PGSQL_IS_ASYNC); | |
270 | pgsql_read_result(pgsql); | |
160 | 271 | } |
161 | 272 | |
162 | 273 | if (pgsql->state == KORE_PGSQL_STATE_WAIT) { |
173 | 284 | req->owner, req, pgsql->state); |
174 | 285 | |
175 | 286 | if (pgsql->error) { |
176 | kore_mem_free(pgsql->error); | |
287 | kore_free(pgsql->error); | |
177 | 288 | pgsql->error = NULL; |
178 | 289 | } |
179 | 290 | |
208 | 319 | PQclear(pgsql->result); |
209 | 320 | |
210 | 321 | if (pgsql->error != NULL) |
211 | kore_mem_free(pgsql->error); | |
322 | kore_free(pgsql->error); | |
212 | 323 | |
213 | 324 | if (pgsql->conn != NULL) |
214 | 325 | pgsql_conn_release(pgsql); |
217 | 328 | pgsql->error = NULL; |
218 | 329 | pgsql->conn = NULL; |
219 | 330 | |
220 | LIST_REMOVE(pgsql, rlist); | |
331 | if (pgsql->flags & PGSQL_LIST_INSERTED) { | |
332 | LIST_REMOVE(pgsql, rlist); | |
333 | pgsql->flags &= ~PGSQL_LIST_INSERTED; | |
334 | } | |
221 | 335 | } |
222 | 336 | |
223 | 337 | void |
261 | 375 | } |
262 | 376 | } |
263 | 377 | |
264 | static int | |
265 | pgsql_prepare(struct kore_pgsql *pgsql, struct http_request *req, | |
266 | const char *query) | |
378 | static struct pgsql_conn * | |
379 | pgsql_conn_next(struct kore_pgsql *pgsql, struct pgsql_db *db, | |
380 | struct http_request *req) | |
267 | 381 | { |
268 | 382 | struct pgsql_conn *conn; |
269 | 383 | |
270 | pgsql->state = KORE_PGSQL_STATE_INIT; | |
271 | pgsql->result = NULL; | |
272 | pgsql->error = NULL; | |
273 | pgsql->conn = NULL; | |
274 | ||
275 | if (TAILQ_EMPTY(&pgsql_conn_free)) { | |
384 | conn = NULL; | |
385 | ||
386 | TAILQ_FOREACH(conn, &pgsql_conn_free, list) { | |
387 | if (!(conn->flags & PGSQL_CONN_FREE)) | |
388 | fatal("got a pgsql connection that was not free?"); | |
389 | if (!strcmp(conn->name, db->name)) | |
390 | break; | |
391 | } | |
392 | ||
393 | if (conn == NULL) { | |
276 | 394 | if (pgsql_conn_count >= pgsql_conn_max) { |
277 | pgsql_queue_add(req); | |
278 | return (KORE_RESULT_ERROR); | |
279 | } | |
280 | ||
281 | if (!pgsql_conn_create(pgsql)) | |
282 | return (KORE_RESULT_ERROR); | |
283 | } | |
284 | ||
285 | http_request_sleep(req); | |
286 | conn = TAILQ_FIRST(&pgsql_conn_free); | |
287 | if (!(conn->flags & PGSQL_CONN_FREE)) | |
288 | fatal("received a pgsql conn that was not free?"); | |
395 | if (pgsql->flags & KORE_PGSQL_ASYNC) { | |
396 | pgsql_queue_add(req); | |
397 | } else { | |
398 | pgsql_set_error(pgsql, | |
399 | "no available connection"); | |
400 | } | |
401 | ||
402 | return (NULL); | |
403 | } | |
404 | ||
405 | if ((conn = pgsql_conn_create(pgsql, db)) == NULL) | |
406 | return (NULL); | |
407 | } | |
289 | 408 | |
290 | 409 | conn->flags &= ~PGSQL_CONN_FREE; |
291 | 410 | TAILQ_REMOVE(&pgsql_conn_free, conn, list); |
292 | 411 | |
293 | pgsql->conn = conn; | |
294 | conn->job = kore_pool_get(&pgsql_job_pool); | |
295 | conn->job->query = kore_strdup(query); | |
296 | conn->job->pgsql = pgsql; | |
297 | conn->job->req = req; | |
298 | ||
299 | LIST_INSERT_HEAD(&(req->pgsqls), pgsql, rlist); | |
300 | return (KORE_RESULT_OK); | |
301 | } | |
302 | ||
303 | static void | |
304 | pgsql_schedule(struct kore_pgsql *pgsql, struct http_request *req) | |
412 | return (conn); | |
413 | } | |
414 | ||
415 | static void | |
416 | pgsql_set_error(struct kore_pgsql *pgsql, const char *msg) | |
417 | { | |
418 | if (pgsql->error != NULL) | |
419 | kore_free(pgsql->error); | |
420 | ||
421 | pgsql->error = kore_strdup(msg); | |
422 | pgsql->state = KORE_PGSQL_STATE_ERROR; | |
423 | } | |
424 | ||
425 | static void | |
426 | pgsql_schedule(struct kore_pgsql *pgsql) | |
305 | 427 | { |
306 | 428 | int fd; |
307 | 429 | |
346 | 468 | } |
347 | 469 | } |
348 | 470 | |
349 | static int | |
350 | pgsql_conn_create(struct kore_pgsql *pgsql) | |
471 | static struct pgsql_conn * | |
472 | pgsql_conn_create(struct kore_pgsql *pgsql, struct pgsql_db *db) | |
351 | 473 | { |
352 | 474 | struct pgsql_conn *conn; |
353 | 475 | |
354 | if (pgsql_conn_string == NULL) | |
476 | if (db == NULL || db->conn_string == NULL) | |
355 | 477 | fatal("pgsql_conn_create: no connection string"); |
356 | 478 | |
357 | 479 | pgsql_conn_count++; |
358 | 480 | conn = kore_malloc(sizeof(*conn)); |
359 | 481 | kore_debug("pgsql_conn_create(): %p", conn); |
360 | memset(conn, 0, sizeof(*conn)); | |
361 | ||
362 | conn->db = PQconnectdb(pgsql_conn_string); | |
482 | ||
483 | conn->db = PQconnectdb(db->conn_string); | |
363 | 484 | if (conn->db == NULL || (PQstatus(conn->db) != CONNECTION_OK)) { |
364 | pgsql->state = KORE_PGSQL_STATE_ERROR; | |
365 | pgsql->error = kore_strdup(PQerrorMessage(conn->db)); | |
485 | pgsql_set_error(pgsql, PQerrorMessage(conn->db)); | |
366 | 486 | pgsql_conn_cleanup(conn); |
367 | return (KORE_RESULT_ERROR); | |
487 | return (NULL); | |
368 | 488 | } |
369 | 489 | |
370 | 490 | conn->job = NULL; |
371 | 491 | conn->flags = PGSQL_CONN_FREE; |
372 | 492 | conn->type = KORE_TYPE_PGSQL_CONN; |
493 | conn->name = kore_strdup(db->name); | |
373 | 494 | TAILQ_INSERT_TAIL(&pgsql_conn_free, conn, list); |
374 | 495 | |
375 | return (KORE_RESULT_OK); | |
496 | return (conn); | |
376 | 497 | } |
377 | 498 | |
378 | 499 | static void |
383 | 504 | if (pgsql->conn == NULL) |
384 | 505 | return; |
385 | 506 | |
386 | kore_mem_free(pgsql->conn->job->query); | |
387 | kore_pool_put(&pgsql_job_pool, pgsql->conn->job); | |
507 | /* Async query cleanup */ | |
508 | if (pgsql->flags & KORE_PGSQL_ASYNC) { | |
509 | if (pgsql->conn != NULL) { | |
510 | fd = PQsocket(pgsql->conn->db); | |
511 | kore_platform_disable_read(fd); | |
512 | kore_pool_put(&pgsql_job_pool, pgsql->conn->job); | |
513 | } | |
514 | } | |
388 | 515 | |
389 | 516 | /* Drain just in case. */ |
390 | 517 | while (PQgetResult(pgsql->conn->db) != NULL) |
393 | 520 | pgsql->conn->job = NULL; |
394 | 521 | pgsql->conn->flags |= PGSQL_CONN_FREE; |
395 | 522 | TAILQ_INSERT_TAIL(&pgsql_conn_free, pgsql->conn, list); |
396 | ||
397 | fd = PQsocket(pgsql->conn->db); | |
398 | kore_platform_disable_read(fd); | |
399 | 523 | |
400 | 524 | pgsql->conn = NULL; |
401 | 525 | pgsql->state = KORE_PGSQL_STATE_COMPLETE; |
420 | 544 | http_request_wakeup(req); |
421 | 545 | |
422 | 546 | pgsql->conn = NULL; |
423 | pgsql->state = KORE_PGSQL_STATE_ERROR; | |
424 | pgsql->error = kore_strdup(PQerrorMessage(conn->db)); | |
425 | ||
426 | kore_mem_free(conn->job->query); | |
547 | pgsql_set_error(pgsql, PQerrorMessage(conn->db)); | |
548 | ||
427 | 549 | kore_pool_put(&pgsql_job_pool, conn->job); |
428 | 550 | conn->job = NULL; |
429 | 551 | } |
432 | 554 | PQfinish(conn->db); |
433 | 555 | |
434 | 556 | pgsql_conn_count--; |
435 | kore_mem_free(conn); | |
436 | } | |
437 | ||
438 | static void | |
439 | pgsql_read_result(struct kore_pgsql *pgsql, int async) | |
440 | { | |
441 | if (async) { | |
442 | if (PQisBusy(pgsql->conn->db)) { | |
443 | pgsql->state = KORE_PGSQL_STATE_WAIT; | |
444 | return; | |
445 | } | |
557 | kore_free(conn->name); | |
558 | kore_free(conn); | |
559 | } | |
560 | ||
561 | static void | |
562 | pgsql_read_result(struct kore_pgsql *pgsql) | |
563 | { | |
564 | if (PQisBusy(pgsql->conn->db)) { | |
565 | pgsql->state = KORE_PGSQL_STATE_WAIT; | |
566 | return; | |
446 | 567 | } |
447 | 568 | |
448 | 569 | pgsql->result = PQgetResult(pgsql->conn->db); |
469 | 590 | case PGRES_EMPTY_QUERY: |
470 | 591 | case PGRES_BAD_RESPONSE: |
471 | 592 | case PGRES_FATAL_ERROR: |
472 | pgsql->state = KORE_PGSQL_STATE_ERROR; | |
473 | pgsql->error = kore_strdup(PQresultErrorMessage(pgsql->result)); | |
474 | break; | |
475 | } | |
476 | } | |
593 | pgsql_set_error(pgsql, PQresultErrorMessage(pgsql->result)); | |
594 | break; | |
595 | } | |
596 | } |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
13 | 13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 14 | */ |
15 | 15 | |
16 | #include <sys/mman.h> | |
16 | 17 | #include <sys/queue.h> |
18 | ||
19 | #include <stdint.h> | |
17 | 20 | |
18 | 21 | #include "kore.h" |
19 | 22 | |
20 | 23 | #define POOL_ELEMENT_BUSY 0 |
21 | 24 | #define POOL_ELEMENT_FREE 1 |
22 | 25 | |
23 | static void pool_region_create(struct kore_pool *, u_int32_t); | |
26 | #if defined(KORE_USE_TASKS) | |
27 | static void pool_lock(struct kore_pool *); | |
28 | static void pool_unlock(struct kore_pool *); | |
29 | #endif | |
30 | ||
31 | static void pool_region_create(struct kore_pool *, size_t); | |
32 | static void pool_region_destroy(struct kore_pool *); | |
24 | 33 | |
25 | 34 | void |
26 | 35 | kore_pool_init(struct kore_pool *pool, const char *name, |
27 | u_int32_t len, u_int32_t elm) | |
28 | { | |
29 | kore_debug("kore_pool_init(%p, %s, %d, %d)", pool, name, len, elm); | |
30 | ||
36 | size_t len, size_t elm) | |
37 | { | |
38 | kore_debug("kore_pool_init(%p, %s, %zu, %zu)", pool, name, len, elm); | |
39 | ||
40 | if ((pool->name = strdup(name)) == NULL) | |
41 | fatal("kore_pool_init: strdup %s", errno_s); | |
42 | ||
43 | pool->lock = 0; | |
31 | 44 | pool->elms = 0; |
32 | 45 | pool->inuse = 0; |
33 | 46 | pool->elen = len; |
34 | pool->name = kore_strdup(name); | |
35 | 47 | pool->slen = pool->elen + sizeof(struct kore_pool_entry); |
36 | 48 | |
37 | 49 | LIST_INIT(&(pool->regions)); |
40 | 52 | pool_region_create(pool, elm); |
41 | 53 | } |
42 | 54 | |
55 | void | |
56 | kore_pool_cleanup(struct kore_pool *pool) | |
57 | { | |
58 | pool->lock = 0; | |
59 | pool->elms = 0; | |
60 | pool->inuse = 0; | |
61 | pool->elen = 0; | |
62 | pool->slen = 0; | |
63 | ||
64 | if (pool->name != NULL) { | |
65 | free(pool->name); | |
66 | pool->name = NULL; | |
67 | } | |
68 | ||
69 | pool_region_destroy(pool); | |
70 | } | |
71 | ||
43 | 72 | void * |
44 | 73 | kore_pool_get(struct kore_pool *pool) |
45 | 74 | { |
46 | 75 | u_int8_t *ptr; |
47 | 76 | struct kore_pool_entry *entry; |
77 | ||
78 | #if defined(KORE_USE_TASKS) | |
79 | pool_lock(pool); | |
80 | #endif | |
48 | 81 | |
49 | 82 | if (LIST_EMPTY(&(pool->freelist))) { |
50 | 83 | kore_log(LOG_NOTICE, "pool %s is exhausted (%d/%d)", |
51 | 84 | pool->name, pool->inuse, pool->elms); |
52 | ||
53 | 85 | pool_region_create(pool, pool->elms); |
54 | 86 | } |
55 | 87 | |
63 | 95 | |
64 | 96 | pool->inuse++; |
65 | 97 | |
66 | #if defined(KORE_PEDANTIC_MALLOC) | |
67 | explicit_bzero(ptr, pool->elen); | |
98 | #if defined(KORE_USE_TASKS) | |
99 | pool_unlock(pool); | |
68 | 100 | #endif |
69 | 101 | |
70 | 102 | return (ptr); |
75 | 107 | { |
76 | 108 | struct kore_pool_entry *entry; |
77 | 109 | |
78 | #if defined(KORE_PEDANTIC_MALLOC) | |
79 | explicit_bzero(ptr, pool->elen); | |
110 | #if defined(KORE_USE_TASKS) | |
111 | pool_lock(pool); | |
80 | 112 | #endif |
81 | 113 | |
82 | 114 | entry = (struct kore_pool_entry *) |
89 | 121 | LIST_INSERT_HEAD(&(pool->freelist), entry, list); |
90 | 122 | |
91 | 123 | pool->inuse--; |
92 | } | |
93 | ||
94 | static void | |
95 | pool_region_create(struct kore_pool *pool, u_int32_t elms) | |
96 | { | |
97 | u_int32_t i; | |
124 | ||
125 | #if defined(KORE_USE_TASKS) | |
126 | pool_unlock(pool); | |
127 | #endif | |
128 | } | |
129 | ||
130 | static void | |
131 | pool_region_create(struct kore_pool *pool, size_t elms) | |
132 | { | |
133 | size_t i; | |
98 | 134 | u_int8_t *p; |
99 | 135 | struct kore_pool_region *reg; |
100 | 136 | struct kore_pool_entry *entry; |
101 | 137 | |
102 | 138 | kore_debug("pool_region_create(%p, %d)", pool, elms); |
103 | 139 | |
104 | reg = kore_malloc(sizeof(struct kore_pool_region)); | |
140 | if ((reg = calloc(1, sizeof(struct kore_pool_region))) == NULL) | |
141 | fatal("pool_region_create: calloc: %s", errno_s); | |
142 | ||
105 | 143 | LIST_INSERT_HEAD(&(pool->regions), reg, list); |
106 | 144 | |
107 | reg->start = kore_malloc(elms * pool->slen); | |
145 | if (SIZE_MAX / elms < pool->slen) | |
146 | fatal("pool_region_create: overflow"); | |
147 | ||
148 | reg->length = elms * pool->slen; | |
149 | reg->start = mmap(NULL, reg->length, PROT_READ | PROT_WRITE, | |
150 | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); | |
151 | if (reg->start == NULL) | |
152 | fatal("mmap: %s", errno_s); | |
153 | ||
108 | 154 | p = (u_int8_t *)reg->start; |
109 | 155 | |
110 | 156 | for (i = 0; i < elms; i++) { |
118 | 164 | |
119 | 165 | pool->elms += elms; |
120 | 166 | } |
167 | ||
168 | static void | |
169 | pool_region_destroy(struct kore_pool *pool) | |
170 | { | |
171 | struct kore_pool_region *reg; | |
172 | ||
173 | kore_debug("pool_region_destroy(%p)", pool); | |
174 | ||
175 | /* Take care iterating when modifying list contents */ | |
176 | while (!LIST_EMPTY(&pool->regions)) { | |
177 | reg = LIST_FIRST(&pool->regions); | |
178 | LIST_REMOVE(reg, list); | |
179 | (void)munmap(reg->start, reg->length); | |
180 | free(reg); | |
181 | } | |
182 | ||
183 | /* Freelist references into the regions memory allocations */ | |
184 | LIST_INIT(&pool->freelist); | |
185 | pool->elms = 0; | |
186 | } | |
187 | ||
188 | #if defined(KORE_USE_TASKS) | |
189 | static void | |
190 | pool_lock(struct kore_pool *pool) | |
191 | { | |
192 | for (;;) { | |
193 | if (__sync_bool_compare_and_swap(&pool->lock, 0, 1)) | |
194 | break; | |
195 | } | |
196 | } | |
197 | ||
198 | static void | |
199 | pool_unlock(struct kore_pool *pool) | |
200 | { | |
201 | if (!__sync_bool_compare_and_swap(&pool->lock, 1, 0)) | |
202 | fatal("pool_unlock: failed to release %s", pool->name); | |
203 | } | |
204 | #endif |
0 | /* | |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | #include <sys/param.h> | |
17 | ||
18 | #include <limits.h> | |
19 | ||
20 | #include "spdy.h" | |
21 | #include "kore.h" | |
22 | #include "http.h" | |
23 | ||
24 | static int spdy_ctrl_frame_syn_stream(struct netbuf *); | |
25 | static int spdy_ctrl_frame_rst_stream(struct netbuf *); | |
26 | static int spdy_ctrl_frame_settings(struct netbuf *); | |
27 | static int spdy_ctrl_frame_ping(struct netbuf *); | |
28 | static int spdy_ctrl_frame_window(struct netbuf *); | |
29 | static int spdy_ctrl_frame_goaway(struct netbuf *); | |
30 | static int spdy_data_frame_recv(struct netbuf *); | |
31 | ||
32 | static void spdy_block_write(struct connection *); | |
33 | static void spdy_enable_write(struct connection *); | |
34 | ||
35 | static int spdy_zlib_inflate(struct connection *, u_int8_t *, | |
36 | size_t, u_int8_t **, u_int32_t *); | |
37 | static int spdy_zlib_deflate(struct connection *, u_int8_t *, | |
38 | size_t, u_int8_t **, u_int32_t *); | |
39 | ||
40 | u_int64_t spdy_idle_time = 120000; | |
41 | u_int32_t spdy_recv_wsize = SPDY_INIT_WSIZE; | |
42 | ||
43 | int | |
44 | spdy_frame_recv(struct netbuf *nb) | |
45 | { | |
46 | struct spdy_stream *s; | |
47 | struct spdy_ctrl_frame ctrl; | |
48 | struct spdy_data_frame data; | |
49 | int (*cb)(struct netbuf *), r; | |
50 | struct connection *c = (struct connection *)nb->owner; | |
51 | ||
52 | kore_debug("spdy_frame_recv(%p)", nb); | |
53 | ||
54 | if (SPDY_CONTROL_FRAME(net_read32(nb->buf))) { | |
55 | ctrl.version = net_read16(nb->buf) & 0x7fff; | |
56 | ctrl.type = net_read16(nb->buf + 2); | |
57 | ctrl.flags = *(u_int8_t *)(nb->buf + 4); | |
58 | ctrl.length = net_read32(nb->buf + 4) & 0xffffff; | |
59 | ||
60 | kore_debug("received control frame %d", ctrl.type); | |
61 | ||
62 | if ((int)ctrl.length < 0) { | |
63 | spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); | |
64 | return (KORE_RESULT_OK); | |
65 | } | |
66 | ||
67 | if (ctrl.version != 3) { | |
68 | kore_debug("protocol mismatch (recv version %u)", | |
69 | ctrl.version); | |
70 | ||
71 | spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); | |
72 | return (KORE_RESULT_OK); | |
73 | } | |
74 | ||
75 | switch (ctrl.type) { | |
76 | case SPDY_CTRL_FRAME_SYN_STREAM: | |
77 | cb = spdy_ctrl_frame_syn_stream; | |
78 | break; | |
79 | case SPDY_CTRL_FRAME_RST_STREAM: | |
80 | cb = spdy_ctrl_frame_rst_stream; | |
81 | break; | |
82 | case SPDY_CTRL_FRAME_SETTINGS: | |
83 | cb = spdy_ctrl_frame_settings; | |
84 | break; | |
85 | case SPDY_CTRL_FRAME_PING: | |
86 | cb = spdy_ctrl_frame_ping; | |
87 | break; | |
88 | case SPDY_CTRL_FRAME_WINDOW: | |
89 | cb = spdy_ctrl_frame_window; | |
90 | break; | |
91 | case SPDY_CTRL_FRAME_GOAWAY: | |
92 | cb = spdy_ctrl_frame_goaway; | |
93 | break; | |
94 | default: | |
95 | cb = NULL; | |
96 | break; | |
97 | } | |
98 | ||
99 | r = KORE_RESULT_OK; | |
100 | ||
101 | if (cb != NULL) { | |
102 | net_recv_expand(c, ctrl.length, cb); | |
103 | } else { | |
104 | kore_debug("no callback for type %u", ctrl.type); | |
105 | } | |
106 | } else { | |
107 | data.stream_id = net_read32(nb->buf) & ~(1 << 31); | |
108 | if ((s = spdy_stream_lookup(c, data.stream_id)) == NULL) { | |
109 | if (!(c->flags & SPDY_CONN_GOAWAY)) { | |
110 | kore_debug("recv dataframe for bad stream: %u", | |
111 | data.stream_id); | |
112 | r = KORE_RESULT_ERROR; | |
113 | } else { | |
114 | r = KORE_RESULT_OK; | |
115 | } | |
116 | } else if (s->flags & FLAG_FIN) { | |
117 | kore_debug("received data frame but FLAG_FIN was set"); | |
118 | r = KORE_RESULT_ERROR; | |
119 | } else { | |
120 | data.flags = *(u_int8_t *)(nb->buf + 4); | |
121 | data.length = net_read32(nb->buf + 4) & 0xffffff; | |
122 | if ((int)data.length < 0) { | |
123 | r = KORE_RESULT_ERROR; | |
124 | } else { | |
125 | r = KORE_RESULT_OK; | |
126 | net_recv_expand(c, data.length, | |
127 | spdy_data_frame_recv); | |
128 | } | |
129 | } | |
130 | } | |
131 | ||
132 | if (r != KORE_RESULT_OK) { | |
133 | r = KORE_RESULT_OK; | |
134 | spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); | |
135 | } | |
136 | ||
137 | return (r); | |
138 | } | |
139 | ||
140 | int | |
141 | spdy_dataframe_begin(struct connection *c) | |
142 | { | |
143 | struct spdy_stream *s = c->snb->stream; | |
144 | ||
145 | kore_debug("spdy_dataframe_begin(%p): s:%u fz:%d sz:%d wz:%d cwz:%d", | |
146 | c, s->stream_id, s->frame_size, s->send_size, s->send_wsize, | |
147 | c->spdy_send_wsize); | |
148 | ||
149 | if (s->frame_size != 0 || s->send_size == 0) { | |
150 | fatal("spdy_dataframe_begin(): s:%u fz:%d - sz:%d", | |
151 | s->stream_id, s->frame_size, s->send_size); | |
152 | } | |
153 | ||
154 | if ((int)s->send_wsize <= 0 || (int)c->spdy_send_wsize <= 0) { | |
155 | kore_debug("no space for new dataframe right now"); | |
156 | spdy_block_write(c); | |
157 | return (KORE_RESULT_ERROR); | |
158 | } | |
159 | ||
160 | s->frame_size = MIN(NETBUF_SEND_PAYLOAD_MAX, s->send_size); | |
161 | ||
162 | kore_debug("spdy_dataframe_begin(): %u: fz:%d wz:%d cwz:%d", | |
163 | s->stream_id, s->frame_size, s->send_size, c->spdy_send_wsize); | |
164 | ||
165 | s->flags &= ~SPDY_DATAFRAME_PRELUDE; | |
166 | spdy_frame_send(c, SPDY_DATA_FRAME, 0, s->frame_size, s, 0); | |
167 | ||
168 | return (KORE_RESULT_OK); | |
169 | } | |
170 | ||
171 | void | |
172 | spdy_frame_send(struct connection *c, u_int16_t type, u_int8_t flags, | |
173 | u_int32_t len, struct spdy_stream *s, u_int32_t misc) | |
174 | { | |
175 | u_int8_t nb[16]; | |
176 | u_int32_t length; | |
177 | ||
178 | kore_debug("spdy_frame_send(%p, %u, %u, %u, %p, %u)", | |
179 | c, type, flags, len, s, misc); | |
180 | ||
181 | switch (type) { | |
182 | case SPDY_CTRL_FRAME_SYN_REPLY: | |
183 | case SPDY_DATA_FRAME: | |
184 | if (s == NULL) | |
185 | fatal("spdy_frame_send(): stream is NULL for %d", type); | |
186 | break; | |
187 | } | |
188 | ||
189 | length = 0; | |
190 | memset(nb, 0, sizeof(nb)); | |
191 | switch (type) { | |
192 | case SPDY_CTRL_FRAME_PING: | |
193 | case SPDY_CTRL_FRAME_SYN_REPLY: | |
194 | net_write16(&nb[0], 3); | |
195 | nb[0] |= (1 << 7); | |
196 | net_write16(&nb[2], type); | |
197 | ||
198 | if (type != SPDY_CTRL_FRAME_PING) { | |
199 | net_write32(&nb[4], len + 4); | |
200 | nb[4] = flags; | |
201 | net_write32(&nb[8], s->stream_id); | |
202 | } else { | |
203 | net_write32(&nb[4], len); | |
204 | nb[4] = flags; | |
205 | net_write32(&nb[8], misc); | |
206 | } | |
207 | ||
208 | length = 12; | |
209 | break; | |
210 | case SPDY_CTRL_FRAME_GOAWAY: | |
211 | net_write16(&nb[0], 3); | |
212 | nb[0] |= (1 << 7); | |
213 | net_write16(&nb[2], type); | |
214 | net_write32(&nb[4], len); | |
215 | nb[4] = flags; | |
216 | length = 8; | |
217 | break; | |
218 | case SPDY_CTRL_FRAME_WINDOW: | |
219 | net_write16(&nb[0], 3); | |
220 | nb[0] |= (1 << 7); | |
221 | net_write16(&nb[2], type); | |
222 | net_write32(&nb[4], len); | |
223 | nb[4] = flags; | |
224 | net_write32(&nb[8], (s != NULL) ? s->stream_id : 0); | |
225 | net_write32(&nb[12], misc); | |
226 | length = 16; | |
227 | break; | |
228 | case SPDY_DATA_FRAME: | |
229 | net_write32(&nb[0], s->stream_id); | |
230 | nb[0] &= ~(1 << 7); | |
231 | net_write32(&nb[4], len); | |
232 | nb[4] = flags; | |
233 | length = 8; | |
234 | break; | |
235 | } | |
236 | ||
237 | if (type == SPDY_DATA_FRAME && !(flags & FLAG_FIN)) { | |
238 | net_send_queue(c, nb, length, NULL, NETBUF_BEFORE_CHAIN); | |
239 | } else { | |
240 | net_send_queue(c, nb, length, NULL, NETBUF_LAST_CHAIN); | |
241 | } | |
242 | } | |
243 | ||
244 | struct spdy_stream * | |
245 | spdy_stream_lookup(struct connection *c, u_int32_t id) | |
246 | { | |
247 | struct spdy_stream *s; | |
248 | ||
249 | TAILQ_FOREACH(s, &(c->spdy_streams), list) { | |
250 | if (s->stream_id == id) | |
251 | return (s); | |
252 | } | |
253 | ||
254 | return (NULL); | |
255 | } | |
256 | ||
257 | struct spdy_header_block * | |
258 | spdy_header_block_create(int delayed_alloc) | |
259 | { | |
260 | struct spdy_header_block *hblock; | |
261 | ||
262 | kore_debug("spdy_header_block_create()"); | |
263 | ||
264 | hblock = kore_malloc(sizeof(*hblock)); | |
265 | if (delayed_alloc == SPDY_HBLOCK_NORMAL) { | |
266 | hblock->header_block = kore_malloc(128); | |
267 | hblock->header_block_len = 128; | |
268 | hblock->header_offset = 4; | |
269 | } else { | |
270 | hblock->header_block = NULL; | |
271 | hblock->header_block_len = 0; | |
272 | hblock->header_offset = 0; | |
273 | } | |
274 | ||
275 | hblock->header_pairs = 0; | |
276 | ||
277 | return (hblock); | |
278 | } | |
279 | ||
280 | void | |
281 | spdy_header_block_add(struct spdy_header_block *hblock, char *name, char *value) | |
282 | { | |
283 | u_int8_t *p; | |
284 | u_int32_t nlen, vlen, tlen; | |
285 | ||
286 | kore_debug("spdy_header_block_add(%p, %s, %s)", hblock, name, value); | |
287 | ||
288 | nlen = strlen(name); | |
289 | vlen = strlen(value); | |
290 | ||
291 | tlen = nlen + 4 + vlen + 4; | |
292 | if ((tlen + hblock->header_offset) > hblock->header_block_len) { | |
293 | hblock->header_block_len += nlen + vlen + 128; | |
294 | hblock->header_block = kore_realloc(hblock->header_block, | |
295 | hblock->header_block_len); | |
296 | } | |
297 | ||
298 | p = hblock->header_block + hblock->header_offset; | |
299 | net_write32(p, nlen); | |
300 | memcpy((p + 4), (u_int8_t *)name, nlen); | |
301 | hblock->header_offset += 4 + nlen; | |
302 | ||
303 | p = hblock->header_block + hblock->header_offset; | |
304 | net_write32(p, vlen); | |
305 | memcpy((p + 4), (u_int8_t *)value, vlen); | |
306 | hblock->header_offset += 4 + vlen; | |
307 | ||
308 | hblock->header_pairs++; | |
309 | } | |
310 | ||
311 | u_int8_t * | |
312 | spdy_header_block_release(struct connection *c, | |
313 | struct spdy_header_block *hblock, u_int32_t *len) | |
314 | { | |
315 | u_int8_t *deflated; | |
316 | ||
317 | kore_debug("spdy_header_block_release(%p, %p)", hblock, len); | |
318 | ||
319 | net_write32(hblock->header_block, hblock->header_pairs); | |
320 | if (!spdy_zlib_deflate(c, hblock->header_block, hblock->header_offset, | |
321 | &deflated, len)) { | |
322 | kore_mem_free(hblock->header_block); | |
323 | kore_mem_free(hblock); | |
324 | return (NULL); | |
325 | } | |
326 | ||
327 | kore_mem_free(hblock->header_block); | |
328 | kore_mem_free(hblock); | |
329 | ||
330 | return (deflated); | |
331 | } | |
332 | ||
333 | int | |
334 | spdy_stream_get_header(struct spdy_header_block *s, | |
335 | const char *header, char **out) | |
336 | { | |
337 | char *cmp; | |
338 | u_int8_t *p, *end; | |
339 | u_int32_t i, nlen, vlen; | |
340 | ||
341 | kore_debug("spdy_stream_get_header(%p, %s) <%d>", s, header, | |
342 | s->header_pairs); | |
343 | ||
344 | p = s->header_block + 4; | |
345 | end = s->header_block + s->header_block_len; | |
346 | ||
347 | if (p >= end) { | |
348 | kore_debug("p >= end when looking for headers"); | |
349 | return (KORE_RESULT_ERROR); | |
350 | } | |
351 | ||
352 | for (i = 0; i < s->header_pairs; i++) { | |
353 | nlen = net_read32(p); | |
354 | if ((int)nlen < 0 || (p + nlen + 4) > end) { | |
355 | kore_debug("nlen out of bounds on %u (%u)", i, nlen); | |
356 | return (KORE_RESULT_ERROR); | |
357 | } | |
358 | ||
359 | vlen = net_read32(p + nlen + 4); | |
360 | if ((int)vlen < 0 || (p + nlen + vlen + 8) > end) { | |
361 | kore_debug("vlen out of bounds on %u (%u)", i, vlen); | |
362 | return (KORE_RESULT_ERROR); | |
363 | } | |
364 | ||
365 | cmp = (char *)(p + 4); | |
366 | if (!strncasecmp(cmp, header, nlen)) { | |
367 | cmp = (char *)(p + nlen + 8); | |
368 | *out = kore_malloc(vlen + 1); | |
369 | kore_strlcpy(*out, cmp, vlen + 1); | |
370 | return (KORE_RESULT_OK); | |
371 | } | |
372 | ||
373 | p += nlen + vlen + 8; | |
374 | } | |
375 | ||
376 | return (KORE_RESULT_ERROR); | |
377 | } | |
378 | ||
379 | void | |
380 | spdy_session_teardown(struct connection *c, u_int8_t err) | |
381 | { | |
382 | u_int8_t d[8]; | |
383 | ||
384 | kore_debug("spdy_session_teardown(%p, %u)", c, err); | |
385 | ||
386 | net_write32((u_int8_t *)&d[0], c->client_stream_id); | |
387 | net_write32((u_int8_t *)&d[4], err); | |
388 | ||
389 | spdy_frame_send(c, SPDY_CTRL_FRAME_GOAWAY, 0, 8, NULL, 0); | |
390 | net_send_queue(c, d, sizeof(d), NULL, NETBUF_LAST_CHAIN); | |
391 | ||
392 | c->flags &= ~CONN_READ_POSSIBLE; | |
393 | c->flags |= CONN_READ_BLOCK; | |
394 | ||
395 | net_send_flush(c); | |
396 | kore_connection_disconnect(c); | |
397 | } | |
398 | ||
399 | void | |
400 | spdy_update_wsize(struct connection *c, struct spdy_stream *s, u_int32_t len) | |
401 | { | |
402 | s->send_size -= len; | |
403 | s->frame_size -= len; | |
404 | s->send_wsize -= len; | |
405 | c->spdy_send_wsize -= len; | |
406 | ||
407 | kore_debug("spdy_update_wsize(): s:%u fz:%d sz:%d wz:%d cwz:%d", | |
408 | s->stream_id, s->frame_size, s->send_size, | |
409 | s->send_wsize, c->spdy_send_wsize); | |
410 | ||
411 | if (s->frame_size == 0 && s->send_size > 0) { | |
412 | kore_debug("spdy_update_wsize(): starting new data frame"); | |
413 | s->flags |= SPDY_DATAFRAME_PRELUDE; | |
414 | } | |
415 | ||
416 | if (s->send_size == 0 && !(s->flags & SPDY_NO_CLOSE)) { | |
417 | if (!(s->flags & SPDY_KORE_FIN)) { | |
418 | s->flags |= SPDY_KORE_FIN; | |
419 | kore_debug("sending final frame %u", s->stream_id); | |
420 | spdy_frame_send(c, SPDY_DATA_FRAME, FLAG_FIN, 0, s, 0); | |
421 | } | |
422 | ||
423 | if (s->flags & (SPDY_KORE_FIN | FLAG_FIN)) { | |
424 | spdy_stream_close(c, s, SPDY_KEEP_NETBUFS); | |
425 | return; | |
426 | } | |
427 | ||
428 | kore_debug("%u remains half open\n", s->stream_id); | |
429 | } | |
430 | ||
431 | if ((int)s->send_wsize <= 0 || (int)c->spdy_send_wsize <= 0) { | |
432 | kore_debug("flow control kicked in for %p:%p", c, s); | |
433 | spdy_block_write(c); | |
434 | } | |
435 | } | |
436 | ||
437 | void | |
438 | spdy_stream_close(struct connection *c, struct spdy_stream *s, int rb) | |
439 | { | |
440 | struct http_request *req; | |
441 | struct netbuf *nb, *nt; | |
442 | ||
443 | kore_debug("spdy_stream_close(%p, %p) <%d>", c, s, s->stream_id); | |
444 | ||
445 | if (s->onclose != NULL) | |
446 | s->onclose(c, s); | |
447 | ||
448 | if (rb) { | |
449 | for (nb = TAILQ_FIRST(&(c->send_queue)); nb != NULL; nb = nt) { | |
450 | nt = TAILQ_NEXT(nb, list); | |
451 | if (nb->stream == s) { | |
452 | kore_debug("spdy_stream_close: killing %p", nb); | |
453 | net_remove_netbuf(&(c->send_queue), nb); | |
454 | } | |
455 | } | |
456 | } | |
457 | ||
458 | TAILQ_REMOVE(&(c->spdy_streams), s, list); | |
459 | if (s->hblock != NULL) { | |
460 | if (s->hblock->header_block != NULL) | |
461 | kore_mem_free(s->hblock->header_block); | |
462 | kore_mem_free(s->hblock); | |
463 | } | |
464 | ||
465 | if (s->httpreq != NULL) { | |
466 | req = s->httpreq; | |
467 | req->stream = NULL; | |
468 | req->flags |= HTTP_REQUEST_DELETE; | |
469 | } | |
470 | ||
471 | kore_mem_free(s); | |
472 | } | |
473 | ||
474 | static int | |
475 | spdy_ctrl_frame_syn_stream(struct netbuf *nb) | |
476 | { | |
477 | struct spdy_stream *s; | |
478 | struct spdy_syn_stream syn; | |
479 | struct spdy_ctrl_frame ctrl; | |
480 | u_int8_t *src; | |
481 | char *host, *method, *path, *version; | |
482 | struct connection *c = (struct connection *)nb->owner; | |
483 | ||
484 | ctrl.version = net_read16(nb->buf) & 0x7fff; | |
485 | ctrl.type = net_read16(nb->buf + 2); | |
486 | ctrl.flags = *(u_int8_t *)(nb->buf + 4); | |
487 | ctrl.length = net_read32(nb->buf + 4) & 0xffffff; | |
488 | ||
489 | syn.stream_id = net_read32(nb->buf + 8); | |
490 | syn.assoc_stream_id = net_read32(nb->buf + 12); | |
491 | syn.prio = net_read16(nb->buf + 16) & 0xe000; | |
492 | syn.slot = net_read16(nb->buf + 16) & 0x7; | |
493 | ||
494 | kore_debug("spdy_ctrl_frame_syn_stream()"); | |
495 | kore_debug("stream_id: %u", syn.stream_id); | |
496 | kore_debug("length : %u", ctrl.length); | |
497 | ||
498 | if (c->spdy_send_wsize > 0 && (c->flags & CONN_WRITE_BLOCK)) | |
499 | spdy_enable_write(c); | |
500 | ||
501 | if ((int)ctrl.length < 0) { | |
502 | spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); | |
503 | return (KORE_RESULT_OK); | |
504 | } | |
505 | ||
506 | if ((syn.stream_id % 2) == 0 || syn.stream_id == 0) { | |
507 | kore_debug("client sent incorrect id for SPDY_SYN_STREAM (%u)", | |
508 | syn.stream_id); | |
509 | spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); | |
510 | return (KORE_RESULT_OK); | |
511 | } | |
512 | ||
513 | if (syn.stream_id < c->client_stream_id) { | |
514 | kore_debug("client sent incorrect id SPDY_SYN_STREAM (%u < %u)", | |
515 | syn.stream_id, c->client_stream_id); | |
516 | spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); | |
517 | return (KORE_RESULT_OK); | |
518 | } | |
519 | ||
520 | if ((s = spdy_stream_lookup(c, syn.stream_id)) != NULL) { | |
521 | kore_debug("duplicate SPDY_SYN_STREAM (%u)", syn.stream_id); | |
522 | spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); | |
523 | return (KORE_RESULT_OK); | |
524 | } | |
525 | ||
526 | s = kore_malloc(sizeof(*s)); | |
527 | s->send_size = 0; | |
528 | s->frame_size = 0; | |
529 | s->httpreq = NULL; | |
530 | s->onclose = NULL; | |
531 | s->prio = syn.prio; | |
532 | s->flags = ctrl.flags; | |
533 | s->recv_wsize = spdy_recv_wsize; | |
534 | s->send_wsize = c->wsize_initial; | |
535 | s->stream_id = syn.stream_id; | |
536 | s->hblock = spdy_header_block_create(SPDY_HBLOCK_DELAYED_ALLOC); | |
537 | ||
538 | src = (nb->buf + SPDY_FRAME_SIZE + SPDY_SYNFRAME_SIZE); | |
539 | kore_debug("compressed headers are %u bytes long", ctrl.length - 10); | |
540 | if (!spdy_zlib_inflate(c, src, (ctrl.length - SPDY_SYNFRAME_SIZE), | |
541 | &(s->hblock->header_block), &(s->hblock->header_block_len))) { | |
542 | kore_mem_free(s->hblock->header_block); | |
543 | kore_mem_free(s->hblock); | |
544 | kore_mem_free(s); | |
545 | spdy_session_teardown(c, SPDY_SESSION_ERROR_INTERNAL); | |
546 | return (KORE_RESULT_OK); | |
547 | } | |
548 | ||
549 | s->hblock->header_pairs = net_read32(s->hblock->header_block); | |
550 | if ((int)s->hblock->header_pairs < 0) { | |
551 | kore_mem_free(s->hblock->header_block); | |
552 | kore_mem_free(s->hblock); | |
553 | kore_mem_free(s); | |
554 | spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); | |
555 | return (KORE_RESULT_OK); | |
556 | } | |
557 | ||
558 | kore_debug("got %u headers", s->hblock->header_pairs); | |
559 | ||
560 | path = NULL; | |
561 | host = NULL; | |
562 | method = NULL; | |
563 | version = NULL; | |
564 | ||
565 | #define GET_HEADER(n, r) \ | |
566 | if (!spdy_stream_get_header(s->hblock, n, r)) { \ | |
567 | kore_mem_free(s->hblock->header_block); \ | |
568 | kore_mem_free(s->hblock); \ | |
569 | kore_mem_free(s); \ | |
570 | kore_debug("no such header: %s", n); \ | |
571 | if (path != NULL) \ | |
572 | kore_mem_free(path); \ | |
573 | if (host != NULL) \ | |
574 | kore_mem_free(host); \ | |
575 | if (method != NULL) \ | |
576 | kore_mem_free(method); \ | |
577 | if (version != NULL) \ | |
578 | kore_mem_free(version); \ | |
579 | spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); \ | |
580 | return (KORE_RESULT_OK); \ | |
581 | } | |
582 | ||
583 | GET_HEADER(":path", &path); | |
584 | GET_HEADER(":method", &method); | |
585 | GET_HEADER(":host", &host); | |
586 | GET_HEADER(":version", &version); | |
587 | ||
588 | c->client_stream_id = s->stream_id; | |
589 | TAILQ_INSERT_TAIL(&(c->spdy_streams), s, list); | |
590 | ||
591 | /* | |
592 | * We don't care so much for what http_request_new() tells us here, | |
593 | * we just have to clean up after passing our stuff to it. | |
594 | */ | |
595 | (void)http_request_new(c, s, host, method, path, version, | |
596 | (struct http_request **)&(s->httpreq)); | |
597 | ||
598 | kore_mem_free(path); | |
599 | kore_mem_free(method); | |
600 | kore_mem_free(host); | |
601 | kore_mem_free(version); | |
602 | net_recv_reset(c, SPDY_FRAME_SIZE, spdy_frame_recv); | |
603 | ||
604 | kore_debug("SPDY_SYN_STREAM: %u:%u:%u", s->stream_id, | |
605 | s->flags, s->prio); | |
606 | ||
607 | return (KORE_RESULT_OK); | |
608 | } | |
609 | ||
610 | static int | |
611 | spdy_ctrl_frame_rst_stream(struct netbuf *nb) | |
612 | { | |
613 | struct spdy_stream *s; | |
614 | u_int32_t stream_id; | |
615 | struct connection *c = (struct connection *)nb->owner; | |
616 | ||
617 | stream_id = net_read32(nb->buf + SPDY_FRAME_SIZE); | |
618 | if ((stream_id % 2) == 0) { | |
619 | kore_debug("received RST for non-client stream %u", stream_id); | |
620 | return (KORE_RESULT_ERROR); | |
621 | } | |
622 | ||
623 | if ((s = spdy_stream_lookup(c, stream_id)) == NULL) { | |
624 | kore_debug("received RST for unknown stream %u", stream_id); | |
625 | return (KORE_RESULT_ERROR); | |
626 | } | |
627 | ||
628 | spdy_stream_close(c, s, SPDY_REMOVE_NETBUFS); | |
629 | net_recv_reset(c, SPDY_FRAME_SIZE, spdy_frame_recv); | |
630 | ||
631 | return (KORE_RESULT_OK); | |
632 | } | |
633 | ||
634 | static int | |
635 | spdy_ctrl_frame_settings(struct netbuf *nb) | |
636 | { | |
637 | struct spdy_stream *s; | |
638 | u_int8_t *buf; | |
639 | u_int32_t ecount, i, id, val, length, diff; | |
640 | struct connection *c = (struct connection *)nb->owner; | |
641 | ||
642 | ecount = net_read32(nb->buf + SPDY_FRAME_SIZE); | |
643 | length = net_read32(nb->buf + 4) & 0xffffff; | |
644 | if ((int)ecount < 0 || (int)length < 0) { | |
645 | spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); | |
646 | return (KORE_RESULT_OK); | |
647 | } | |
648 | ||
649 | kore_debug("SPDY_SETTINGS: %u settings present", ecount); | |
650 | ||
651 | if (length != ((ecount * 8) + 4)) { | |
652 | kore_debug("ecount is not correct (%u != %u)", length, | |
653 | (ecount * 8) + 4); | |
654 | spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); | |
655 | return (KORE_RESULT_OK); | |
656 | } | |
657 | ||
658 | buf = nb->buf + SPDY_FRAME_SIZE + 4; | |
659 | for (i = 0; i < ecount; i++) { | |
660 | id = net_read32(buf) & 0xffffff; | |
661 | val = net_read32(buf + 4); | |
662 | ||
663 | if ((int)val < 0) { | |
664 | buf += 8; | |
665 | continue; | |
666 | } | |
667 | ||
668 | switch (id) { | |
669 | case SETTINGS_INITIAL_WINDOW_SIZE: | |
670 | diff = val - c->wsize_initial; | |
671 | c->wsize_initial = val; | |
672 | TAILQ_FOREACH(s, &(c->spdy_streams), list) | |
673 | s->send_wsize += diff; | |
674 | kore_debug("updated wsize with %d", diff); | |
675 | break; | |
676 | default: | |
677 | kore_debug("no handling for setting %u:%u", id, val); | |
678 | break; | |
679 | } | |
680 | ||
681 | buf += 8; | |
682 | } | |
683 | ||
684 | net_recv_reset(c, SPDY_FRAME_SIZE, spdy_frame_recv); | |
685 | ||
686 | return (KORE_RESULT_OK); | |
687 | } | |
688 | ||
689 | static int | |
690 | spdy_ctrl_frame_ping(struct netbuf *nb) | |
691 | { | |
692 | u_int32_t id; | |
693 | struct connection *c = (struct connection *)nb->owner; | |
694 | ||
695 | id = ntohl(*(u_int32_t *)(nb->buf + SPDY_FRAME_SIZE)); | |
696 | kore_debug("SPDY_PING: %u", id); | |
697 | ||
698 | /* XXX todo - check if we sent the ping. */ | |
699 | if ((id % 2) == 0) { | |
700 | kore_debug("received malformed client PING (%u)", id); | |
701 | spdy_session_teardown(c, SPDY_SESSION_ERROR_PROTOCOL); | |
702 | return (KORE_RESULT_OK); | |
703 | } | |
704 | ||
705 | spdy_frame_send(c, SPDY_CTRL_FRAME_PING, 0, 4, NULL, id); | |
706 | net_recv_reset(c, SPDY_FRAME_SIZE, spdy_frame_recv); | |
707 | ||
708 | return (KORE_RESULT_OK); | |
709 | } | |
710 | ||
711 | static int | |
712 | spdy_ctrl_frame_window(struct netbuf *nb) | |
713 | { | |
714 | int r; | |
715 | struct spdy_stream *s; | |
716 | u_int32_t stream_id, window_size; | |
717 | struct connection *c = (struct connection *)nb->owner; | |
718 | ||
719 | stream_id = net_read32(nb->buf + SPDY_FRAME_SIZE); | |
720 | window_size = net_read32(nb->buf + SPDY_FRAME_SIZE + 4); | |
721 | kore_debug("window_update: %u for %u", window_size, stream_id); | |
722 | ||
723 | r = KORE_RESULT_OK; | |
724 | if ((s = spdy_stream_lookup(c, stream_id)) != NULL) { | |
725 | s->send_wsize += window_size; | |
726 | if ((u_int64_t)s->send_wsize > SPDY_FLOW_WINDOW_MAX) { | |
727 | kore_debug("window_update: size too large"); | |
728 | return (KORE_RESULT_ERROR); | |
729 | } | |
730 | ||
731 | if (c->flags & CONN_WRITE_BLOCK && | |
732 | s->send_wsize > 0 && c->spdy_send_wsize > 0) { | |
733 | kore_debug("stream %u no longer blocked", s->stream_id); | |
734 | spdy_enable_write(c); | |
735 | r = net_send_flush(c); | |
736 | } | |
737 | } else { | |
738 | c->spdy_send_wsize += window_size; | |
739 | if ((u_int64_t)c->spdy_send_wsize > SPDY_FLOW_WINDOW_MAX) { | |
740 | kore_debug("window_update: size too large"); | |
741 | return (KORE_RESULT_ERROR); | |
742 | } | |
743 | ||
744 | if (c->flags & CONN_WRITE_BLOCK && c->spdy_send_wsize > 0) { | |
745 | kore_debug("session %p no longer blocked", c); | |
746 | spdy_enable_write(c); | |
747 | r = net_send_flush(c); | |
748 | } | |
749 | } | |
750 | ||
751 | net_recv_reset(c, SPDY_FRAME_SIZE, spdy_frame_recv); | |
752 | ||
753 | return (r); | |
754 | } | |
755 | ||
756 | static int | |
757 | spdy_ctrl_frame_goaway(struct netbuf *nb) | |
758 | { | |
759 | struct connection *c = (struct connection *)nb->owner; | |
760 | ||
761 | kore_debug("spdy_ctrl_frame_goaway(%p)", c); | |
762 | ||
763 | c->flags |= SPDY_CONN_GOAWAY; | |
764 | kore_connection_disconnect(c); | |
765 | ||
766 | return (KORE_RESULT_OK); | |
767 | } | |
768 | ||
769 | static int | |
770 | spdy_data_frame_recv(struct netbuf *nb) | |
771 | { | |
772 | struct spdy_stream *s; | |
773 | int err; | |
774 | struct http_request *req; | |
775 | struct spdy_data_frame data; | |
776 | char *content; | |
777 | struct connection *c = (struct connection *)nb->owner; | |
778 | ||
779 | data.stream_id = net_read32(nb->buf) & ~(1 << 31); | |
780 | data.flags = *(u_int8_t *)(nb->buf + 4); | |
781 | data.length = net_read32(nb->buf + 4) & 0xffffff; | |
782 | kore_debug("SPDY_SESSION_DATA: %u:%u:%u", data.stream_id, | |
783 | data.flags, data.length); | |
784 | ||
785 | if ((int)data.length < 0) | |
786 | return (KORE_RESULT_ERROR); | |
787 | ||
788 | if ((s = spdy_stream_lookup(c, data.stream_id)) == NULL) { | |
789 | kore_debug("session data for non-existant stream"); | |
790 | /* stream error */ | |
791 | return (KORE_RESULT_ERROR); | |
792 | } | |
793 | ||
794 | req = (struct http_request *)s->httpreq; | |
795 | if (req == NULL || !(req->flags & HTTP_REQUEST_EXPECT_BODY)) { | |
796 | kore_debug("data frame for non post received"); | |
797 | /* stream error */ | |
798 | return (KORE_RESULT_ERROR); | |
799 | } | |
800 | ||
801 | if (req->http_body == NULL) { | |
802 | if (!spdy_stream_get_header(s->hblock, | |
803 | "content-length", &content)) { | |
804 | kore_debug("no content-length found for body"); | |
805 | return (KORE_RESULT_ERROR); | |
806 | } | |
807 | ||
808 | s->post_size = kore_strtonum(content, 10, 0, LLONG_MAX, &err); | |
809 | if (err == KORE_RESULT_ERROR) { | |
810 | kore_debug("bad content-length: %s", content); | |
811 | kore_mem_free(content); | |
812 | return (KORE_RESULT_ERROR); | |
813 | } | |
814 | ||
815 | kore_mem_free(content); | |
816 | ||
817 | if (s->post_size == 0) { | |
818 | req->flags |= HTTP_REQUEST_COMPLETE; | |
819 | req->flags &= ~HTTP_REQUEST_EXPECT_BODY; | |
820 | net_recv_reset(c, SPDY_FRAME_SIZE, spdy_frame_recv); | |
821 | return (KORE_RESULT_OK); | |
822 | } | |
823 | ||
824 | if (s->post_size > http_body_max) { | |
825 | kore_log(LOG_NOTICE, "body data too large (%ld > %ld)", | |
826 | s->post_size, http_body_max); | |
827 | return (KORE_RESULT_ERROR); | |
828 | } | |
829 | ||
830 | req->http_body = kore_buf_create(s->post_size); | |
831 | } | |
832 | ||
833 | if ((req->http_body->offset + data.length) > s->post_size) { | |
834 | kore_debug("POST would grow too large"); | |
835 | return (KORE_RESULT_ERROR); | |
836 | } | |
837 | ||
838 | kore_buf_append(req->http_body, (nb->buf + SPDY_FRAME_SIZE), | |
839 | data.length); | |
840 | ||
841 | if (data.flags & FLAG_FIN || req->http_body->offset == s->post_size) { | |
842 | if (req->http_body->offset != s->post_size) { | |
843 | kore_debug("FLAG_FIN before all POST data received"); | |
844 | return (KORE_RESULT_ERROR); | |
845 | } | |
846 | ||
847 | s->post_size = 0; | |
848 | s->flags |= FLAG_FIN; | |
849 | req->flags |= HTTP_REQUEST_COMPLETE; | |
850 | req->flags &= ~HTTP_REQUEST_EXPECT_BODY; | |
851 | } | |
852 | ||
853 | net_recv_reset(c, SPDY_FRAME_SIZE, spdy_frame_recv); | |
854 | ||
855 | /* | |
856 | * XXX - This can be implemented better so we can stagger | |
857 | * window updates a bit and not constantly hit flow control. | |
858 | */ | |
859 | s->recv_wsize -= data.length; | |
860 | spdy_frame_send(c, SPDY_CTRL_FRAME_WINDOW, 0, 8, s, data.length); | |
861 | s->recv_wsize += data.length; | |
862 | ||
863 | c->spdy_recv_wsize -= data.length; | |
864 | spdy_frame_send(c, SPDY_CTRL_FRAME_WINDOW, 0, 8, NULL, data.length); | |
865 | c->spdy_recv_wsize += data.length; | |
866 | ||
867 | kore_debug("data frame recv: wz:%d cwz:%d", s->recv_wsize, | |
868 | c->spdy_recv_wsize); | |
869 | ||
870 | return (KORE_RESULT_OK); | |
871 | } | |
872 | ||
873 | static void | |
874 | spdy_block_write(struct connection *c) | |
875 | { | |
876 | kore_debug("spdy_block_write(%p)", c); | |
877 | ||
878 | c->flags |= CONN_WRITE_BLOCK; | |
879 | c->flags &= ~CONN_WRITE_POSSIBLE; | |
880 | } | |
881 | ||
882 | static void | |
883 | spdy_enable_write(struct connection *c) | |
884 | { | |
885 | kore_debug("spdy_enable_write(%p)", c); | |
886 | ||
887 | c->flags &= ~CONN_WRITE_BLOCK; | |
888 | c->flags |= CONN_WRITE_POSSIBLE; | |
889 | } | |
890 | ||
891 | static int | |
892 | spdy_zlib_inflate(struct connection *c, u_int8_t *src, size_t len, | |
893 | u_int8_t **dst, u_int32_t *olen) | |
894 | { | |
895 | size_t have; | |
896 | int r, ret; | |
897 | u_char inflate_buffer[SPDY_ZLIB_CHUNK]; | |
898 | ||
899 | kore_debug("spdy_zlib_inflate(%p, %p, %d)", c, src, len); | |
900 | ||
901 | if (c->inflate_started == 0) { | |
902 | c->z_inflate.avail_in = 0; | |
903 | c->z_inflate.next_in = Z_NULL; | |
904 | c->z_inflate.zalloc = Z_NULL; | |
905 | c->z_inflate.zfree = Z_NULL; | |
906 | if ((r = inflateInit(&(c->z_inflate))) != Z_OK) { | |
907 | kore_debug("inflateInit() failed: %d", r); | |
908 | return (KORE_RESULT_ERROR); | |
909 | } | |
910 | ||
911 | c->inflate_started = 1; | |
912 | } | |
913 | ||
914 | *olen = 0; | |
915 | *dst = NULL; | |
916 | ||
917 | ret = -1; | |
918 | c->z_inflate.avail_in = len; | |
919 | c->z_inflate.next_in = src; | |
920 | while (ret == -1) { | |
921 | c->z_inflate.avail_out = SPDY_ZLIB_CHUNK; | |
922 | c->z_inflate.next_out = inflate_buffer; | |
923 | ||
924 | r = inflate(&(c->z_inflate), Z_SYNC_FLUSH); | |
925 | switch (r) { | |
926 | case Z_NEED_DICT: | |
927 | r = inflateSetDictionary(&(c->z_inflate), | |
928 | SPDY_dictionary_txt, SPDY_ZLIB_DICT_SIZE); | |
929 | if (r != Z_OK) { | |
930 | inflateEnd(&(c->z_inflate)); | |
931 | kore_debug("inflateSetDictionary(): %d", r); | |
932 | return (KORE_RESULT_ERROR); | |
933 | } | |
934 | ||
935 | continue; | |
936 | case Z_BUF_ERROR: | |
937 | case Z_DATA_ERROR: | |
938 | case Z_MEM_ERROR: | |
939 | ret = KORE_RESULT_ERROR; | |
940 | kore_debug("inflate(): %d", r); | |
941 | break; | |
942 | case Z_OK: | |
943 | have = SPDY_ZLIB_CHUNK - c->z_inflate.avail_out; | |
944 | *olen += have; | |
945 | *dst = kore_realloc(*dst, *olen); | |
946 | memcpy((*dst) + (*olen - have), inflate_buffer, have); | |
947 | ||
948 | if (c->z_inflate.avail_in != 0 || | |
949 | c->z_inflate.avail_out == 0) | |
950 | break; | |
951 | /* FALLTHROUGH */ | |
952 | case Z_STREAM_END: | |
953 | ret = KORE_RESULT_OK; | |
954 | break; | |
955 | } | |
956 | } | |
957 | ||
958 | return (ret); | |
959 | } | |
960 | ||
961 | static int | |
962 | spdy_zlib_deflate(struct connection *c, u_int8_t *src, size_t len, | |
963 | u_int8_t **dst, u_int32_t *olen) | |
964 | { | |
965 | size_t have; | |
966 | int r, ret; | |
967 | u_char deflate_buffer[SPDY_ZLIB_CHUNK]; | |
968 | ||
969 | kore_debug("spdy_zlib_deflate(%p, %p, %d)", c, src, len); | |
970 | ||
971 | if (c->deflate_started == 0) { | |
972 | c->z_deflate.avail_in = 0; | |
973 | c->z_deflate.next_in = Z_NULL; | |
974 | c->z_deflate.zalloc = Z_NULL; | |
975 | c->z_deflate.zfree = Z_NULL; | |
976 | if ((r = deflateInit(&(c->z_deflate), -1)) != Z_OK) { | |
977 | kore_debug("deflateInit() failed: %d", r); | |
978 | return (KORE_RESULT_ERROR); | |
979 | } | |
980 | ||
981 | r = deflateSetDictionary(&(c->z_deflate), SPDY_dictionary_txt, | |
982 | SPDY_ZLIB_DICT_SIZE); | |
983 | if (r != Z_OK) { | |
984 | deflateEnd(&(c->z_deflate)); | |
985 | kore_debug("deflateSetDictionary(): %d", r); | |
986 | return (KORE_RESULT_ERROR); | |
987 | } | |
988 | ||
989 | c->deflate_started = 1; | |
990 | } | |
991 | ||
992 | *olen = 0; | |
993 | *dst = NULL; | |
994 | ||
995 | ret = -1; | |
996 | c->z_deflate.avail_in = len; | |
997 | c->z_deflate.next_in = src; | |
998 | while (ret == -1) { | |
999 | c->z_deflate.avail_out = SPDY_ZLIB_CHUNK; | |
1000 | c->z_deflate.next_out = deflate_buffer; | |
1001 | ||
1002 | r = deflate(&(c->z_deflate), Z_SYNC_FLUSH); | |
1003 | switch (r) { | |
1004 | case Z_BUF_ERROR: | |
1005 | case Z_DATA_ERROR: | |
1006 | case Z_MEM_ERROR: | |
1007 | ret = KORE_RESULT_ERROR; | |
1008 | kore_debug("deflate(): %d", r); | |
1009 | break; | |
1010 | case Z_OK: | |
1011 | have = SPDY_ZLIB_CHUNK - c->z_deflate.avail_out; | |
1012 | *olen += have; | |
1013 | *dst = kore_realloc(*dst, *olen); | |
1014 | memcpy((*dst) + (*olen - have), deflate_buffer, have); | |
1015 | ||
1016 | if (c->z_deflate.avail_in == 0 && | |
1017 | c->z_deflate.avail_out != 0) | |
1018 | ret = KORE_RESULT_OK; | |
1019 | break; | |
1020 | } | |
1021 | } | |
1022 | ||
1023 | return (ret); | |
1024 | } |
26 | 26 | #include "tasks.h" |
27 | 27 | |
28 | 28 | static u_int8_t threads; |
29 | static pthread_mutex_t task_thread_lock; | |
30 | ||
31 | 29 | static TAILQ_HEAD(, kore_task_thread) task_threads; |
30 | ||
31 | u_int16_t kore_task_threads = KORE_TASK_THREADS; | |
32 | 32 | |
33 | 33 | static void *task_thread(void *); |
34 | 34 | static void task_channel_read(int, void *, u_int32_t); |
47 | 47 | void |
48 | 48 | kore_task_init(void) |
49 | 49 | { |
50 | int r; | |
51 | ||
52 | 50 | threads = 0; |
53 | ||
54 | 51 | TAILQ_INIT(&task_threads); |
55 | if ((r = pthread_mutex_init(&task_thread_lock, NULL)) != 0) | |
56 | fatal("kore_task_init: pthread_mutex_init: %d", r); | |
57 | 52 | } |
58 | 53 | |
59 | 54 | void |
60 | 55 | kore_task_create(struct kore_task *t, int (*entry)(struct kore_task *)) |
61 | 56 | { |
57 | t->cb = NULL; | |
58 | #if !defined(KORE_NO_HTTP) | |
62 | 59 | t->req = NULL; |
60 | #endif | |
63 | 61 | t->entry = entry; |
64 | 62 | t->type = KORE_TYPE_TASK; |
65 | 63 | t->state = KORE_TASK_STATE_CREATED; |
75 | 73 | struct kore_task_thread *tt; |
76 | 74 | |
77 | 75 | kore_platform_schedule_read(t->fds[0], t); |
78 | ||
79 | pthread_mutex_lock(&task_thread_lock); | |
80 | if (TAILQ_EMPTY(&task_threads)) | |
76 | if (threads < kore_task_threads) { | |
77 | /* task_thread_spawn() will lock tt->lock for us. */ | |
81 | 78 | task_thread_spawn(&tt); |
82 | else | |
83 | tt = TAILQ_FIRST(&task_threads); | |
84 | ||
85 | pthread_mutex_unlock(&task_thread_lock); | |
86 | pthread_mutex_lock(&(tt->lock)); | |
79 | } else { | |
80 | /* Cycle task around. */ | |
81 | if ((tt = TAILQ_FIRST(&task_threads)) == NULL) | |
82 | fatal("no available tasks threads?"); | |
83 | pthread_mutex_lock(&(tt->lock)); | |
84 | TAILQ_REMOVE(&task_threads, tt, list); | |
85 | TAILQ_INSERT_TAIL(&task_threads, tt, list); | |
86 | } | |
87 | 87 | |
88 | 88 | t->thread = tt; |
89 | 89 | TAILQ_INSERT_TAIL(&(tt->tasks), t, list); |
92 | 92 | pthread_cond_signal(&(tt->cond)); |
93 | 93 | } |
94 | 94 | |
95 | #if !defined(KORE_NO_HTTP) | |
95 | 96 | void |
96 | 97 | kore_task_bind_request(struct kore_task *t, struct http_request *req) |
97 | 98 | { |
98 | 99 | kore_debug("kore_task_bind_request: %p bound to %p", req, t); |
100 | ||
101 | if (t->cb != NULL) | |
102 | fatal("cannot bind cbs and requests at the same time"); | |
99 | 103 | |
100 | 104 | t->req = req; |
101 | 105 | LIST_INSERT_HEAD(&(req->tasks), t, rlist); |
102 | 106 | |
103 | 107 | http_request_sleep(req); |
104 | 108 | } |
109 | #endif | |
110 | ||
111 | void | |
112 | kore_task_bind_callback(struct kore_task *t, void (*cb)(struct kore_task *)) | |
113 | { | |
114 | #if !defined(KORE_NO_HTTP) | |
115 | if (t->req != NULL) | |
116 | fatal("cannot bind requests and cbs at the same time"); | |
117 | #endif | |
118 | t->cb = cb; | |
119 | } | |
105 | 120 | |
106 | 121 | void |
107 | 122 | kore_task_destroy(struct kore_task *t) |
108 | 123 | { |
109 | 124 | kore_debug("kore_task_destroy: %p", t); |
110 | 125 | |
126 | #if !defined(KORE_NO_HTTP) | |
111 | 127 | if (t->req != NULL) { |
112 | 128 | t->req = NULL; |
113 | 129 | LIST_REMOVE(t, rlist); |
114 | 130 | } |
131 | #endif | |
115 | 132 | |
116 | 133 | pthread_rwlock_wrlock(&(t->lock)); |
117 | 134 | |
187 | 204 | { |
188 | 205 | kore_debug("kore_task_handle: %p, %d", t, finished); |
189 | 206 | |
207 | #if !defined(KORE_NO_HTTP) | |
190 | 208 | if (t->req != NULL) |
191 | 209 | http_request_wakeup(t->req); |
210 | #endif | |
192 | 211 | |
193 | 212 | if (finished) { |
194 | 213 | kore_platform_disable_read(t->fds[0]); |
195 | 214 | kore_task_set_state(t, KORE_TASK_STATE_FINISHED); |
215 | #if !defined(KORE_NO_HTTP) | |
196 | 216 | if (t->req != NULL) { |
197 | 217 | if (t->req->flags & HTTP_REQUEST_DELETE) |
198 | 218 | kore_task_destroy(t); |
199 | 219 | } |
200 | } | |
220 | #endif | |
221 | } | |
222 | ||
223 | if (t->cb != NULL) | |
224 | t->cb(t); | |
201 | 225 | } |
202 | 226 | |
203 | 227 | int |
292 | 316 | TAILQ_INIT(&(tt->tasks)); |
293 | 317 | pthread_cond_init(&(tt->cond), NULL); |
294 | 318 | pthread_mutex_init(&(tt->lock), NULL); |
319 | pthread_mutex_lock(&(tt->lock)); | |
320 | TAILQ_INSERT_TAIL(&task_threads, tt, list); | |
295 | 321 | |
296 | 322 | if (pthread_create(&(tt->tid), NULL, task_thread, tt) != 0) |
297 | 323 | fatal("pthread_create: %s", errno_s); |
308 | 334 | kore_debug("task_thread: #%d starting", tt->idx); |
309 | 335 | |
310 | 336 | pthread_mutex_lock(&(tt->lock)); |
311 | ||
312 | pthread_mutex_lock(&task_thread_lock); | |
313 | TAILQ_INSERT_TAIL(&task_threads, tt, list); | |
314 | pthread_mutex_unlock(&task_thread_lock); | |
315 | 337 | |
316 | 338 | for (;;) { |
317 | 339 | if (TAILQ_EMPTY(&(tt->tasks))) |
323 | 345 | TAILQ_REMOVE(&(tt->tasks), t, list); |
324 | 346 | pthread_mutex_unlock(&(tt->lock)); |
325 | 347 | |
326 | pthread_mutex_lock(&task_thread_lock); | |
327 | TAILQ_REMOVE(&task_threads, tt, list); | |
328 | pthread_mutex_unlock(&task_thread_lock); | |
329 | ||
330 | 348 | kore_debug("task_thread#%d: executing %p", tt->idx, t); |
331 | 349 | |
332 | 350 | kore_task_set_state(t, KORE_TASK_STATE_RUNNING); |
333 | 351 | kore_task_set_result(t, t->entry(t)); |
334 | 352 | kore_task_finish(t); |
335 | 353 | |
336 | pthread_mutex_lock(&task_thread_lock); | |
337 | TAILQ_INSERT_HEAD(&task_threads, tt, list); | |
338 | pthread_mutex_unlock(&task_thread_lock); | |
339 | ||
340 | 354 | pthread_mutex_lock(&(tt->lock)); |
341 | 355 | } |
342 | 356 |
0 | 0 | /* |
1 | * Copyright (c) 2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
27 | 27 | } |
28 | 28 | |
29 | 29 | struct kore_timer * |
30 | kore_timer_add(void (*cb)(void *, u_int64_t, u_int64_t), u_int64_t interval, | |
30 | kore_timer_add(void (*cb)(void *, u_int64_t), u_int64_t interval, | |
31 | 31 | void *arg, int flags) |
32 | 32 | { |
33 | 33 | struct kore_timer *timer, *t; |
55 | 55 | kore_timer_remove(struct kore_timer *timer) |
56 | 56 | { |
57 | 57 | TAILQ_REMOVE(&kore_timers, timer, list); |
58 | kore_mem_free(timer); | |
58 | kore_free(timer); | |
59 | 59 | } |
60 | 60 | |
61 | 61 | u_int64_t |
62 | 62 | kore_timer_run(u_int64_t now) |
63 | 63 | { |
64 | 64 | struct kore_timer *timer, *t; |
65 | u_int64_t next_timer, delta; | |
65 | u_int64_t next_timer; | |
66 | 66 | |
67 | 67 | next_timer = 100; |
68 | 68 | |
73 | 73 | } |
74 | 74 | |
75 | 75 | TAILQ_REMOVE(&kore_timers, timer, list); |
76 | delta = now - timer->nextrun; | |
77 | timer->cb(timer->arg, now, delta); | |
76 | timer->cb(timer->arg, now); | |
78 | 77 | |
79 | 78 | if (timer->flags & KORE_TIMER_ONESHOT) { |
80 | kore_mem_free(timer); | |
79 | kore_free(timer); | |
81 | 80 | } else { |
82 | timer->nextrun += timer->interval - delta; | |
81 | timer->nextrun = now + timer->interval; | |
83 | 82 | TAILQ_FOREACH(t, &kore_timers, list) { |
84 | 83 | if (t->nextrun > timer->nextrun) { |
85 | 84 | TAILQ_INSERT_BEFORE(t, timer, list); |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
15 | 15 | |
16 | 16 | #include <sys/time.h> |
17 | 17 | |
18 | #include <ctype.h> | |
19 | #include <stdio.h> | |
20 | #include <stdarg.h> | |
21 | #include <string.h> | |
22 | #include <stdlib.h> | |
23 | #include <time.h> | |
18 | 24 | #include <limits.h> |
19 | 25 | |
20 | 26 | #include "kore.h" |
56 | 62 | void |
57 | 63 | kore_log_init(void) |
58 | 64 | { |
65 | #if defined(KORE_SINGLE_BINARY) | |
66 | extern const char *__progname; | |
67 | const char *name = __progname; | |
68 | #else | |
69 | const char *name = "kore"; | |
70 | #endif | |
71 | ||
59 | 72 | if (!foreground) |
60 | openlog("kore", LOG_NDELAY | LOG_PID, LOG_DAEMON); | |
73 | openlog(name, LOG_NDELAY | LOG_PID, LOG_DAEMON); | |
61 | 74 | } |
62 | 75 | |
63 | 76 | void |
64 | 77 | kore_log(int prio, const char *fmt, ...) |
65 | 78 | { |
66 | 79 | va_list args; |
67 | char buf[2048]; | |
80 | char buf[2048], tmp[32]; | |
68 | 81 | |
69 | 82 | va_start(args, fmt); |
70 | 83 | (void)vsnprintf(buf, sizeof(buf), fmt, args); |
71 | 84 | va_end(args); |
72 | 85 | |
73 | 86 | if (worker != NULL) { |
87 | (void)snprintf(tmp, sizeof(tmp), "wrk %d", worker->id); | |
88 | #if !defined(KORE_NO_TLS) | |
89 | if (worker->id == KORE_WORKER_KEYMGR) | |
90 | (void)kore_strlcpy(tmp, "keymgr", sizeof(tmp)); | |
91 | #endif | |
74 | 92 | if (foreground) |
75 | printf("[wrk %d]: %s\n", worker->id, buf); | |
93 | printf("[%s]: %s\n", tmp, buf); | |
76 | 94 | else |
77 | syslog(prio, "[wrk %d]: %s", worker->id, buf); | |
95 | syslog(prio, "[%s]: %s", tmp, buf); | |
78 | 96 | } else { |
79 | 97 | if (foreground) |
80 | 98 | printf("[parent]: %s\n", buf); |
83 | 101 | } |
84 | 102 | } |
85 | 103 | |
86 | void | |
87 | kore_strlcpy(char *dst, const char *src, size_t len) | |
104 | size_t | |
105 | kore_strlcpy(char *dst, const char *src, const size_t len) | |
88 | 106 | { |
89 | 107 | char *d = dst; |
90 | 108 | const char *s = src; |
91 | 109 | const char *end = dst + len - 1; |
110 | ||
111 | if (len == 0) | |
112 | fatal("kore_strlcpy: len == 0"); | |
92 | 113 | |
93 | 114 | while ((*d = *s) != '\0') { |
94 | 115 | if (d == end) { |
99 | 120 | d++; |
100 | 121 | s++; |
101 | 122 | } |
123 | ||
124 | while (*s != '\0') | |
125 | s++; | |
126 | ||
127 | return (s - src); | |
102 | 128 | } |
103 | 129 | |
104 | 130 | int |
202 | 228 | int count; |
203 | 229 | char **ap; |
204 | 230 | |
231 | if (ele == 0) | |
232 | return (0); | |
233 | ||
205 | 234 | count = 0; |
206 | 235 | for (ap = out; ap < &out[ele - 1] && |
207 | 236 | (*ap = strsep(&input, delim)) != NULL;) { |
216 | 245 | } |
217 | 246 | |
218 | 247 | void |
219 | kore_strip_chars(char *in, char strip, char **out) | |
248 | kore_strip_chars(char *in, const char strip, char **out) | |
220 | 249 | { |
221 | 250 | u_int32_t len; |
222 | 251 | char *s, *p; |
311 | 340 | } |
312 | 341 | |
313 | 342 | out: |
314 | kore_mem_free(sdup); | |
343 | kore_free(sdup); | |
315 | 344 | return (t); |
316 | 345 | } |
317 | 346 | |
347 | 376 | } |
348 | 377 | |
349 | 378 | int |
350 | kore_base64_encode(u_int8_t *data, u_int32_t len, char **out) | |
351 | { | |
379 | kore_base64_encode(u_int8_t *data, size_t len, char **out) | |
380 | { | |
381 | u_int32_t b; | |
352 | 382 | struct kore_buf *res; |
383 | size_t plen, idx; | |
353 | 384 | u_int8_t n, *pdata; |
354 | 385 | int i, padding; |
355 | u_int32_t idx, b, plen; | |
356 | 386 | |
357 | 387 | if ((len % 3) != 0) { |
358 | 388 | padding = 3 - (len % 3); |
359 | 389 | plen = len + padding; |
390 | if (plen < len) | |
391 | fatal("plen wrapped"); | |
392 | ||
360 | 393 | pdata = kore_malloc(plen); |
361 | ||
362 | 394 | memcpy(pdata, data, len); |
363 | 395 | memset(pdata + len, 0, padding); |
364 | 396 | } else { |
367 | 399 | pdata = data; |
368 | 400 | } |
369 | 401 | |
370 | res = kore_buf_create(plen); | |
402 | res = kore_buf_alloc(plen); | |
371 | 403 | |
372 | 404 | i = 2; |
373 | 405 | b = 0; |
397 | 429 | kore_buf_append(res, (u_int8_t *)"=", 1); |
398 | 430 | |
399 | 431 | if (pdata != data) |
400 | kore_mem_free(pdata); | |
432 | kore_free(pdata); | |
401 | 433 | |
402 | 434 | pdata = kore_buf_release(res, &plen); |
435 | if ((plen + 1) < plen) | |
436 | fatal("plen wrapped"); | |
437 | ||
403 | 438 | *out = kore_malloc(plen + 1); |
404 | kore_strlcpy(*out, (char *)pdata, plen + 1); | |
405 | kore_mem_free(pdata); | |
439 | (void)kore_strlcpy(*out, (char *)pdata, plen + 1); | |
440 | kore_free(pdata); | |
406 | 441 | |
407 | 442 | return (KORE_RESULT_OK); |
408 | 443 | } |
409 | 444 | |
410 | 445 | int |
411 | kore_base64_decode(char *in, u_int8_t **out, u_int32_t *olen) | |
446 | kore_base64_decode(char *in, u_int8_t **out, size_t *olen) | |
412 | 447 | { |
413 | 448 | int i, c; |
414 | 449 | struct kore_buf *res; |
420 | 455 | d = 0; |
421 | 456 | c = 0; |
422 | 457 | len = strlen(in); |
423 | res = kore_buf_create(len); | |
458 | res = kore_buf_alloc(len); | |
424 | 459 | |
425 | 460 | for (idx = 0; idx < len; idx++) { |
426 | 461 | c = in[idx]; |
472 | 507 | } |
473 | 508 | |
474 | 509 | void * |
475 | kore_mem_find(void *src, size_t slen, void *needle, u_int32_t len) | |
476 | { | |
477 | u_int8_t *p, *end; | |
478 | ||
479 | end = (u_int8_t *)src + slen; | |
480 | for (p = src; p < end; p++) { | |
481 | if (*p != *(u_int8_t *)needle) | |
510 | kore_mem_find(void *src, size_t slen, void *needle, size_t len) | |
511 | { | |
512 | size_t pos; | |
513 | ||
514 | for (pos = 0; pos < slen; pos++) { | |
515 | if ( *((u_int8_t *)src + pos) != *(u_int8_t *)needle) | |
482 | 516 | continue; |
483 | 517 | |
484 | if ((end - p) < len) | |
518 | if ((slen - pos) < len) | |
485 | 519 | return (NULL); |
486 | 520 | |
487 | if (!memcmp(p, needle, len)) | |
488 | return (p); | |
521 | if (!memcmp((u_int8_t *)src + pos, needle, len)) | |
522 | return ((u_int8_t *)src + pos); | |
489 | 523 | } |
490 | 524 | |
491 | 525 | return (NULL); |
526 | } | |
527 | ||
528 | char * | |
529 | kore_text_trim(char *string, size_t len) | |
530 | { | |
531 | char *end; | |
532 | ||
533 | if (len == 0) | |
534 | return (string); | |
535 | ||
536 | end = (string + len) - 1; | |
537 | while (isspace(*string) && string < end) | |
538 | string++; | |
539 | ||
540 | while (isspace(*end) && end > string) | |
541 | *(end)-- = '\0'; | |
542 | ||
543 | return (string); | |
544 | } | |
545 | ||
546 | char * | |
547 | kore_read_line(FILE *fp, char *in, size_t len) | |
548 | { | |
549 | char *p, *t; | |
550 | ||
551 | if (fgets(in, len, fp) == NULL) | |
552 | return (NULL); | |
553 | ||
554 | p = in; | |
555 | in[strcspn(in, "\n")] = '\0'; | |
556 | ||
557 | while (isspace(*p)) | |
558 | p++; | |
559 | ||
560 | if (p[0] == '#' || p[0] == '\0') { | |
561 | p[0] = '\0'; | |
562 | return (p); | |
563 | } | |
564 | ||
565 | for (t = p; *t != '\0'; t++) { | |
566 | if (*t == '\t') | |
567 | *t = ' '; | |
568 | } | |
569 | ||
570 | return (p); | |
492 | 571 | } |
493 | 572 | |
494 | 573 | void |
495 | 574 | fatal(const char *fmt, ...) |
496 | 575 | { |
497 | va_list args; | |
498 | char buf[2048]; | |
576 | va_list args; | |
577 | char buf[2048]; | |
578 | extern const char *__progname; | |
499 | 579 | |
500 | 580 | va_start(args, fmt); |
501 | 581 | (void)vsnprintf(buf, sizeof(buf), fmt, args); |
504 | 584 | if (!foreground) |
505 | 585 | kore_log(LOG_ERR, "%s", buf); |
506 | 586 | |
507 | printf("kore: %s\n", buf); | |
587 | #if !defined(KORE_NO_TLS) | |
588 | if (worker != NULL && worker->id == KORE_WORKER_KEYMGR) | |
589 | kore_keymgr_cleanup(); | |
590 | #endif | |
591 | ||
592 | printf("%s: %s\n", __progname, buf); | |
508 | 593 | exit(1); |
509 | 594 | } |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
34 | 34 | switch (val->type) { |
35 | 35 | case KORE_VALIDATOR_TYPE_REGEX: |
36 | 36 | if (regcomp(&(val->rctx), arg, REG_EXTENDED | REG_NOSUB)) { |
37 | kore_mem_free(val); | |
37 | kore_free(val); | |
38 | 38 | kore_log(LOG_NOTICE, |
39 | 39 | "validator %s has bad regex %s", name, arg); |
40 | 40 | return (KORE_RESULT_ERROR); |
41 | 41 | } |
42 | 42 | break; |
43 | 43 | case KORE_VALIDATOR_TYPE_FUNCTION: |
44 | if ((val->func = kore_module_getsym(arg)) == NULL) { | |
45 | kore_mem_free(val); | |
44 | *(void **)(&val->func) = kore_module_getsym(arg); | |
45 | if (val->func == NULL) { | |
46 | kore_free(val); | |
46 | 47 | kore_log(LOG_NOTICE, |
47 | 48 | "validator %s has undefined callback %s", |
48 | 49 | name, arg); |
50 | 51 | } |
51 | 52 | break; |
52 | 53 | default: |
53 | kore_mem_free(val); | |
54 | kore_free(val); | |
54 | 55 | return (KORE_RESULT_ERROR); |
55 | 56 | } |
56 | 57 | |
111 | 112 | if (val->type != KORE_VALIDATOR_TYPE_FUNCTION) |
112 | 113 | continue; |
113 | 114 | |
114 | if ((val->func = kore_module_getsym(val->arg)) == NULL) | |
115 | *(void **)&(val->func) = kore_module_getsym(val->arg); | |
116 | if (val->func == NULL) | |
115 | 117 | fatal("no function for validator %s found", val->name); |
116 | 118 | } |
117 | 119 | } |
15 | 15 | |
16 | 16 | #include <sys/param.h> |
17 | 17 | |
18 | #include <openssl/sha.h> | |
19 | ||
18 | 20 | #include <limits.h> |
21 | #include <string.h> | |
19 | 22 | |
20 | 23 | #include "kore.h" |
21 | 24 | #include "http.h" |
34 | 37 | |
35 | 38 | #define WEBSOCKET_SERVER_RESPONSE "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" |
36 | 39 | |
37 | struct websocket_data { | |
38 | u_int8_t op; | |
39 | void *data; | |
40 | size_t len; | |
41 | }; | |
42 | 40 | |
43 | 41 | u_int64_t kore_websocket_timeout = 120000; |
44 | 42 | u_int64_t kore_websocket_maxframe = 16384; |
46 | 44 | static int websocket_recv_frame(struct netbuf *); |
47 | 45 | static int websocket_recv_opcode(struct netbuf *); |
48 | 46 | static void websocket_disconnect(struct connection *); |
49 | static void websocket_send_single(struct connection *, void *); | |
50 | void websocket_send(struct connection *, u_int8_t, void *, size_t); | |
47 | static void websocket_frame_build(struct kore_buf *, u_int8_t, | |
48 | const void *, size_t); | |
51 | 49 | |
52 | 50 | void |
53 | 51 | kore_websocket_handshake(struct http_request *req, struct kore_wscbs *wscbs) |
63 | 61 | } |
64 | 62 | |
65 | 63 | if (!http_request_header(req, "sec-websocket-version", &version)) { |
66 | kore_mem_free(key); | |
67 | 64 | http_response_header(req, "sec-websocket-version", "13"); |
68 | 65 | http_response(req, HTTP_STATUS_BAD_REQUEST, NULL, 0); |
69 | 66 | return; |
70 | 67 | } |
71 | 68 | |
72 | 69 | if (strcmp(version, "13")) { |
73 | kore_mem_free(key); | |
74 | kore_mem_free(version); | |
75 | 70 | http_response_header(req, "sec-websocket-version", "13"); |
76 | 71 | http_response(req, HTTP_STATUS_BAD_REQUEST, NULL, 0); |
77 | 72 | return; |
78 | 73 | } |
79 | 74 | |
80 | kore_mem_free(version); | |
81 | ||
82 | buf = kore_buf_create(128); | |
75 | buf = kore_buf_alloc(128); | |
83 | 76 | kore_buf_appendf(buf, "%s%s", key, WEBSOCKET_SERVER_RESPONSE); |
84 | kore_mem_free(key); | |
85 | 77 | |
86 | 78 | (void)SHA1_Init(&sctx); |
87 | 79 | (void)SHA1_Update(&sctx, buf->data, buf->offset); |
98 | 90 | http_response_header(req, "upgrade", "websocket"); |
99 | 91 | http_response_header(req, "connection", "upgrade"); |
100 | 92 | http_response_header(req, "sec-websocket-accept", base64); |
101 | kore_mem_free(base64); | |
93 | kore_free(base64); | |
102 | 94 | |
103 | 95 | kore_debug("%p: new websocket connection", req->owner); |
104 | 96 | |
97 | req->owner->proto = CONN_PROTO_WEBSOCKET; | |
105 | 98 | http_response(req, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, 0); |
106 | 99 | net_recv_reset(req->owner, WEBSOCKET_FRAME_HDR, websocket_recv_opcode); |
107 | 100 | |
108 | 101 | req->owner->disconnect = websocket_disconnect; |
109 | 102 | req->owner->rnb->flags &= ~NETBUF_CALL_CB_ALWAYS; |
110 | req->owner->proto = CONN_PROTO_WEBSOCKET; | |
111 | 103 | |
112 | 104 | req->owner->wscbs = wscbs; |
113 | 105 | req->owner->idle_timer.start = kore_time_ms(); |
118 | 110 | } |
119 | 111 | |
120 | 112 | void |
121 | kore_websocket_send(struct connection *c, u_int8_t op, void *data, size_t len) | |
113 | kore_websocket_send(struct connection *c, u_int8_t op, const void *data, | |
114 | size_t len) | |
115 | { | |
116 | struct kore_buf *frame; | |
117 | ||
118 | frame = kore_buf_alloc(len); | |
119 | websocket_frame_build(frame, op, data, len); | |
120 | net_send_queue(c, frame->data, frame->offset); | |
121 | kore_buf_free(frame); | |
122 | } | |
123 | ||
124 | void | |
125 | kore_websocket_broadcast(struct connection *src, u_int8_t op, const void *data, | |
126 | size_t len, int scope) | |
127 | { | |
128 | struct connection *c; | |
129 | struct kore_buf *frame; | |
130 | ||
131 | frame = kore_buf_alloc(len); | |
132 | websocket_frame_build(frame, op, data, len); | |
133 | ||
134 | TAILQ_FOREACH(c, &connections, list) { | |
135 | if (c != src && c->proto == CONN_PROTO_WEBSOCKET) { | |
136 | net_send_queue(c, frame->data, frame->offset); | |
137 | net_send_flush(c); | |
138 | } | |
139 | } | |
140 | ||
141 | if (scope == WEBSOCKET_BROADCAST_GLOBAL) { | |
142 | kore_msg_send(KORE_MSG_WORKER_ALL, | |
143 | KORE_MSG_WEBSOCKET, frame->data, frame->offset); | |
144 | } | |
145 | ||
146 | kore_buf_free(frame); | |
147 | } | |
148 | ||
149 | static void | |
150 | websocket_frame_build(struct kore_buf *frame, u_int8_t op, const void *data, | |
151 | size_t len) | |
122 | 152 | { |
123 | 153 | u_int8_t len_1; |
124 | 154 | u_int16_t len16; |
125 | 155 | u_int64_t len64; |
126 | struct kore_buf *frame; | |
127 | ||
128 | if (c->proto != CONN_PROTO_WEBSOCKET) | |
129 | fatal("kore_websocket_send(): to non websocket connection"); | |
130 | ||
131 | kore_debug("%p: sending %ld bytes", c, len); | |
132 | 156 | |
133 | 157 | if (len > WEBSOCKET_PAYLOAD_SINGLE) { |
134 | 158 | if (len < USHRT_MAX) |
139 | 163 | len_1 = len; |
140 | 164 | } |
141 | 165 | |
142 | frame = kore_buf_create(len); | |
143 | ||
144 | 166 | op |= (1 << 7); |
145 | 167 | kore_buf_append(frame, &op, sizeof(op)); |
146 | 168 | |
147 | 169 | len_1 &= ~(1 << 7); |
148 | 170 | kore_buf_append(frame, &len_1, sizeof(len_1)); |
149 | 171 | |
150 | if (len_1 != len) { | |
172 | if (len_1 > WEBSOCKET_PAYLOAD_SINGLE) { | |
151 | 173 | switch (len_1) { |
152 | 174 | case WEBSOCKET_PAYLOAD_EXTEND_1: |
153 | 175 | net_write16((u_int8_t *)&len16, len); |
161 | 183 | } |
162 | 184 | |
163 | 185 | kore_buf_append(frame, data, len); |
164 | net_send_queue(c, frame->data, frame->offset, NULL, NETBUF_LAST_CHAIN); | |
165 | kore_buf_free(frame); | |
166 | } | |
167 | ||
168 | void | |
169 | kore_websocket_broadcast(struct connection *c, u_int8_t op, void *data, | |
170 | size_t len, int scope) | |
171 | { | |
172 | struct websocket_data arg; | |
173 | ||
174 | arg.op = op; | |
175 | arg.len = len; | |
176 | arg.data = data; | |
177 | kore_worker_websocket_broadcast(c, websocket_send_single, &arg); | |
178 | ||
179 | if (scope == WEBSOCKET_BROADCAST_GLOBAL) | |
180 | fatal("kore_websocket_broadcast: no global scope yet"); | |
181 | } | |
182 | ||
183 | static void | |
184 | websocket_send_single(struct connection *c, void *args) | |
185 | { | |
186 | struct websocket_data *arg = args; | |
187 | ||
188 | kore_websocket_send(c, arg->op, arg->data, arg->len); | |
189 | net_send_flush(c); | |
190 | 186 | } |
191 | 187 | |
192 | 188 | static int |
0 | 0 | /* |
1 | * Copyright (c) 2013-2015 Joris Vink <joris@coders.se> | |
1 | * Copyright (c) 2013-2016 Joris Vink <joris@coders.se> | |
2 | 2 | * |
3 | 3 | * Permission to use, copy, modify, and distribute this software for any |
4 | 4 | * purpose with or without fee is hereby granted, provided that the above |
13 | 13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 14 | */ |
15 | 15 | |
16 | #include <sys/types.h> | |
16 | #include <sys/param.h> | |
17 | 17 | #include <sys/shm.h> |
18 | 18 | #include <sys/wait.h> |
19 | 19 | #include <sys/time.h> |
20 | 20 | #include <sys/resource.h> |
21 | #include <sys/socket.h> | |
21 | 22 | |
22 | 23 | #include <fcntl.h> |
23 | 24 | #include <grp.h> |
25 | 26 | #include <signal.h> |
26 | 27 | |
27 | 28 | #include "kore.h" |
29 | ||
30 | #if !defined(KORE_NO_HTTP) | |
28 | 31 | #include "http.h" |
32 | #endif | |
29 | 33 | |
30 | 34 | #if defined(KORE_USE_PGSQL) |
31 | 35 | #include "pgsql.h" |
41 | 45 | #define worker_debug(fmt, ...) |
42 | 46 | #endif |
43 | 47 | |
48 | #if !defined(WAIT_ANY) | |
49 | #define WAIT_ANY (-1) | |
50 | #endif | |
51 | ||
44 | 52 | #define KORE_SHM_KEY 15000 |
45 | 53 | #define WORKER_LOCK_TIMEOUT 500 |
46 | 54 | |
59 | 67 | static inline int kore_worker_acceptlock_obtain(void); |
60 | 68 | static inline void kore_worker_acceptlock_release(void); |
61 | 69 | |
62 | static struct connection_list disconnected; | |
63 | 70 | static struct kore_worker *kore_workers; |
64 | 71 | static int shm_accept_key; |
65 | 72 | static struct wlock *accept_lock; |
66 | 73 | |
67 | 74 | extern volatile sig_atomic_t sig_recv; |
68 | 75 | struct kore_worker *worker = NULL; |
69 | struct connection_list worker_clients; | |
70 | 76 | u_int8_t worker_set_affinity = 1; |
71 | 77 | u_int32_t worker_accept_threshold = 0; |
72 | 78 | u_int32_t worker_rlimit_nofiles = 1024; |
82 | 88 | if (worker_count == 0) |
83 | 89 | worker_count = 1; |
84 | 90 | |
91 | #if !defined(KORE_NO_TLS) | |
92 | /* account for the key manager. */ | |
93 | worker_count += 1; | |
94 | #endif | |
95 | ||
85 | 96 | len = sizeof(*accept_lock) + |
86 | 97 | (sizeof(struct kore_worker) * worker_count); |
87 | 98 | |
102 | 113 | kore_debug("kore_worker_init(): starting %d workers", worker_count); |
103 | 114 | |
104 | 115 | if (worker_count > cpu_count) { |
105 | kore_debug("kore_worker_init(): more workers then cpu's"); | |
116 | kore_debug("kore_worker_init(): more workers than cpu's"); | |
106 | 117 | } |
107 | 118 | |
108 | 119 | cpu = 0; |
124 | 135 | kw->has_lock = 0; |
125 | 136 | kw->active_hdlr = NULL; |
126 | 137 | |
138 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, kw->pipe) == -1) | |
139 | fatal("socketpair(): %s", errno_s); | |
140 | ||
141 | if (!kore_connection_nonblock(kw->pipe[0], 0) || | |
142 | !kore_connection_nonblock(kw->pipe[1], 0)) | |
143 | fatal("could not set pipe fds to nonblocking: %s", errno_s); | |
144 | ||
127 | 145 | kw->pid = fork(); |
128 | 146 | if (kw->pid == -1) |
129 | 147 | fatal("could not spawn worker child: %s", errno_s); |
133 | 151 | kore_worker_entry(kw); |
134 | 152 | /* NOTREACHED */ |
135 | 153 | } |
154 | } | |
155 | ||
156 | struct kore_worker * | |
157 | kore_worker_data(u_int8_t id) | |
158 | { | |
159 | if (id >= worker_count) | |
160 | fatal("id %u too large for worker count", id); | |
161 | ||
162 | return (WORKER(id)); | |
136 | 163 | } |
137 | 164 | |
138 | 165 | void |
177 | 204 | } |
178 | 205 | |
179 | 206 | void |
180 | kore_worker_entry(struct kore_worker *kw) | |
181 | { | |
182 | size_t fd; | |
207 | kore_worker_privdrop(void) | |
208 | { | |
209 | rlim_t fd; | |
183 | 210 | struct rlimit rl; |
184 | char buf[16]; | |
185 | struct connection *c, *cnext; | |
186 | int quit, had_lock, r; | |
187 | u_int64_t now, idle_check, next_lock, netwait; | |
188 | 211 | struct passwd *pw = NULL; |
189 | ||
190 | worker = kw; | |
191 | 212 | |
192 | 213 | /* Must happen before chroot. */ |
193 | 214 | if (skip_runas == 0) { |
227 | 248 | |
228 | 249 | if (skip_runas == 0) { |
229 | 250 | if (setgroups(1, &pw->pw_gid) || |
230 | #ifdef __MACH__ | |
251 | #if defined(__MACH__) || defined(NetBSD) | |
231 | 252 | setgid(pw->pw_gid) || setegid(pw->pw_gid) || |
232 | 253 | setuid(pw->pw_uid) || seteuid(pw->pw_uid)) |
233 | 254 | #else |
236 | 257 | #endif |
237 | 258 | fatal("cannot drop privileges"); |
238 | 259 | } |
260 | } | |
261 | ||
262 | void | |
263 | kore_worker_entry(struct kore_worker *kw) | |
264 | { | |
265 | char buf[16]; | |
266 | int quit, had_lock, r; | |
267 | u_int64_t now, idle_check, next_lock, netwait; | |
268 | #if defined(KORE_SINGLE_BINARY) | |
269 | void (*onload)(void); | |
270 | #endif | |
271 | ||
272 | worker = kw; | |
239 | 273 | |
240 | 274 | (void)snprintf(buf, sizeof(buf), "kore [wrk %d]", kw->id); |
275 | #if !defined(KORE_NO_TLS) | |
276 | if (kw->id == KORE_WORKER_KEYMGR) | |
277 | (void)snprintf(buf, sizeof(buf), "kore [keymgr]"); | |
278 | #endif | |
241 | 279 | kore_platform_proctitle(buf); |
242 | 280 | |
243 | 281 | if (worker_set_affinity == 1) |
248 | 286 | sig_recv = 0; |
249 | 287 | signal(SIGHUP, kore_signal); |
250 | 288 | signal(SIGQUIT, kore_signal); |
289 | signal(SIGTERM, kore_signal); | |
251 | 290 | signal(SIGPIPE, SIG_IGN); |
252 | 291 | |
253 | 292 | if (foreground) |
255 | 294 | else |
256 | 295 | signal(SIGINT, SIG_IGN); |
257 | 296 | |
297 | #if !defined(KORE_NO_TLS) | |
298 | if (kw->id == KORE_WORKER_KEYMGR) { | |
299 | kore_keymgr_run(); | |
300 | exit(0); | |
301 | } | |
302 | #endif | |
303 | ||
304 | kore_worker_privdrop(); | |
305 | ||
258 | 306 | net_init(); |
307 | #if !defined(KORE_NO_HTTP) | |
259 | 308 | http_init(); |
309 | kore_accesslog_worker_init(); | |
310 | #endif | |
260 | 311 | kore_timer_init(); |
261 | 312 | kore_connection_init(); |
262 | 313 | kore_domain_load_crl(); |
263 | TAILQ_INIT(&disconnected); | |
264 | TAILQ_INIT(&worker_clients); | |
314 | kore_domain_keymgr_init(); | |
265 | 315 | |
266 | 316 | quit = 0; |
267 | 317 | had_lock = 0; |
268 | 318 | next_lock = 0; |
269 | 319 | idle_check = 0; |
270 | 320 | kore_platform_event_init(); |
271 | kore_accesslog_worker_init(); | |
321 | kore_msg_worker_init(); | |
272 | 322 | |
273 | 323 | #if defined(KORE_USE_PGSQL) |
274 | 324 | kore_pgsql_init(); |
279 | 329 | #endif |
280 | 330 | |
281 | 331 | kore_log(LOG_NOTICE, "worker %d started (cpu#%d)", kw->id, kw->cpu); |
332 | ||
333 | #if defined(KORE_SINGLE_BINARY) | |
334 | *(void **)&(onload) = kore_module_getsym("kore_onload"); | |
335 | if (onload != NULL) | |
336 | onload(); | |
337 | #else | |
282 | 338 | kore_module_onload(); |
339 | #endif | |
283 | 340 | |
284 | 341 | for (;;) { |
285 | 342 | if (sig_recv != 0) { |
286 | if (sig_recv == SIGHUP) | |
343 | switch (sig_recv) { | |
344 | case SIGHUP: | |
345 | #if !defined(KORE_SINGLE_BINARY) | |
287 | 346 | kore_module_reload(1); |
288 | else if (sig_recv == SIGQUIT || sig_recv == SIGINT) | |
347 | #endif | |
348 | break; | |
349 | case SIGQUIT: | |
350 | case SIGINT: | |
351 | case SIGTERM: | |
289 | 352 | quit = 1; |
353 | break; | |
354 | default: | |
355 | break; | |
356 | } | |
290 | 357 | |
291 | 358 | sig_recv = 0; |
292 | 359 | } |
316 | 383 | next_lock = now + WORKER_LOCK_TIMEOUT; |
317 | 384 | } |
318 | 385 | |
386 | #if !defined(KORE_NO_HTTP) | |
319 | 387 | http_process(); |
388 | #endif | |
320 | 389 | |
321 | 390 | if ((now - idle_check) >= 10000) { |
322 | 391 | idle_check = now; |
323 | now = kore_time_ms(); | |
324 | TAILQ_FOREACH(c, &worker_clients, list) { | |
325 | if (c->proto == CONN_PROTO_SPDY && | |
326 | c->idle_timer.length == 0 && | |
327 | !(c->flags & CONN_WRITE_BLOCK) && | |
328 | !(c->flags & CONN_READ_BLOCK)) | |
329 | continue; | |
330 | if (!(c->flags & CONN_IDLE_TIMER_ACT)) | |
331 | continue; | |
332 | kore_connection_check_idletimer(now, c); | |
333 | } | |
334 | } | |
335 | ||
336 | for (c = TAILQ_FIRST(&disconnected); c != NULL; c = cnext) { | |
337 | cnext = TAILQ_NEXT(c, list); | |
338 | TAILQ_REMOVE(&disconnected, c, list); | |
339 | kore_connection_remove(c); | |
340 | } | |
341 | ||
342 | if (quit && http_request_count == 0) | |
392 | kore_connection_check_timeout(); | |
393 | } | |
394 | ||
395 | kore_connection_prune(KORE_CONNECTION_PRUNE_DISCONNECT); | |
396 | ||
397 | if (quit) | |
343 | 398 | break; |
344 | 399 | } |
345 | 400 | |
346 | for (c = TAILQ_FIRST(&worker_clients); c != NULL; c = cnext) { | |
347 | cnext = TAILQ_NEXT(c, list); | |
348 | net_send_flush(c); | |
349 | kore_connection_disconnect(c); | |
350 | } | |
351 | ||
352 | for (c = TAILQ_FIRST(&disconnected); c != NULL; c = cnext) { | |
353 | cnext = TAILQ_NEXT(c, list); | |
354 | net_send_flush(c); | |
355 | TAILQ_REMOVE(&disconnected, c, list); | |
356 | kore_connection_remove(c); | |
357 | } | |
401 | kore_platform_event_cleanup(); | |
402 | kore_connection_cleanup(); | |
403 | kore_domain_cleanup(); | |
404 | kore_module_cleanup(); | |
405 | #if !defined(KORE_NO_HTTP) | |
406 | http_cleanup(); | |
407 | #endif | |
408 | net_cleanup(); | |
358 | 409 | |
359 | 410 | kore_debug("worker %d shutting down", kw->id); |
360 | 411 | exit(0); |
361 | } | |
362 | ||
363 | void | |
364 | kore_worker_connection_add(struct connection *c) | |
365 | { | |
366 | TAILQ_INSERT_TAIL(&worker_clients, c, list); | |
367 | worker_active_connections++; | |
368 | } | |
369 | ||
370 | void | |
371 | kore_worker_connection_move(struct connection *c) | |
372 | { | |
373 | TAILQ_REMOVE(&worker_clients, c, list); | |
374 | TAILQ_INSERT_TAIL(&disconnected, c, list); | |
375 | } | |
376 | ||
377 | void | |
378 | kore_worker_connection_remove(struct connection *c) | |
379 | { | |
380 | worker_active_connections--; | |
381 | } | |
382 | ||
383 | void | |
384 | kore_worker_websocket_broadcast(struct connection *src, | |
385 | void (*cb)(struct connection *, void *), void *args) | |
386 | { | |
387 | struct connection *c; | |
388 | ||
389 | TAILQ_FOREACH(c, &worker_clients, list) { | |
390 | if (c != src && c->proto == CONN_PROTO_WEBSOCKET) | |
391 | cb(c, args); | |
392 | } | |
393 | 412 | } |
394 | 413 | |
395 | 414 | void |
434 | 453 | (kw->active_hdlr != NULL) ? kw->active_hdlr->func : |
435 | 454 | "none"); |
436 | 455 | |
456 | #if !defined(KORE_NO_TLS) | |
457 | if (id == KORE_WORKER_KEYMGR) { | |
458 | kore_log(LOG_CRIT, "keymgr gone, stopping"); | |
459 | kw->pid = 0; | |
460 | if (raise(SIGTERM) != 0) { | |
461 | kore_log(LOG_WARNING, | |
462 | "failed to raise SIGTERM signal"); | |
463 | } | |
464 | break; | |
465 | } | |
466 | #endif | |
467 | ||
437 | 468 | if (kw->pid == accept_lock->current) |
438 | 469 | worker_unlock(); |
439 | 470 | |
446 | 477 | } |
447 | 478 | |
448 | 479 | kore_log(LOG_NOTICE, "restarting worker %d", kw->id); |
480 | kore_msg_parent_remove(kw); | |
449 | 481 | kore_worker_spawn(kw->id, kw->cpu); |
482 | kore_msg_parent_add(kw); | |
450 | 483 | } else { |
451 | 484 | kore_log(LOG_NOTICE, |
452 | 485 | "worker %d (pid: %d) signaled us (%d)", |
0 | /* | |
1 | * Copyright (c) 2013 Joris Vink <joris@coders.se> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | const unsigned char SPDY_dictionary_txt[] = { | |
17 | 0x00, 0x00, 0x00, 0x07, 0x6f, 0x70, 0x74, 0x69, // - - - - o p t i | |
18 | 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x04, 0x68, // o n s - - - - h | |
19 | 0x65, 0x61, 0x64, 0x00, 0x00, 0x00, 0x04, 0x70, // e a d - - - - p | |
20 | 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x70, // o s t - - - - p | |
21 | 0x75, 0x74, 0x00, 0x00, 0x00, 0x06, 0x64, 0x65, // u t - - - - d e | |
22 | 0x6c, 0x65, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, // l e t e - - - - | |
23 | 0x74, 0x72, 0x61, 0x63, 0x65, 0x00, 0x00, 0x00, // t r a c e - - - | |
24 | 0x06, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x00, // - a c c e p t - | |
25 | 0x00, 0x00, 0x0e, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p | |
26 | 0x74, 0x2d, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // t - c h a r s e | |
27 | 0x74, 0x00, 0x00, 0x00, 0x0f, 0x61, 0x63, 0x63, // t - - - - a c c | |
28 | 0x65, 0x70, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e p t - e n c o | |
29 | 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x0f, // d i n g - - - - | |
30 | 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x6c, // a c c e p t - l | |
31 | 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x00, // a n g u a g e - | |
32 | 0x00, 0x00, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x70, // - - - a c c e p | |
33 | 0x74, 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x73, // t - r a n g e s | |
34 | 0x00, 0x00, 0x00, 0x03, 0x61, 0x67, 0x65, 0x00, // - - - - a g e - | |
35 | 0x00, 0x00, 0x05, 0x61, 0x6c, 0x6c, 0x6f, 0x77, // - - - a l l o w | |
36 | 0x00, 0x00, 0x00, 0x0d, 0x61, 0x75, 0x74, 0x68, // - - - - a u t h | |
37 | 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, // o r i z a t i o | |
38 | 0x6e, 0x00, 0x00, 0x00, 0x0d, 0x63, 0x61, 0x63, // n - - - - c a c | |
39 | 0x68, 0x65, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, // h e - c o n t r | |
40 | 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x0a, 0x63, 0x6f, // o l - - - - c o | |
41 | 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, // n n e c t i o n | |
42 | 0x00, 0x00, 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t | |
43 | 0x65, 0x6e, 0x74, 0x2d, 0x62, 0x61, 0x73, 0x65, // e n t - b a s e | |
44 | 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t | |
45 | 0x65, 0x6e, 0x74, 0x2d, 0x65, 0x6e, 0x63, 0x6f, // e n t - e n c o | |
46 | 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, // d i n g - - - - | |
47 | 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, // c o n t e n t - | |
48 | 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, // l a n g u a g e | |
49 | 0x00, 0x00, 0x00, 0x0e, 0x63, 0x6f, 0x6e, 0x74, // - - - - c o n t | |
50 | 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x65, 0x6e, 0x67, // e n t - l e n g | |
51 | 0x74, 0x68, 0x00, 0x00, 0x00, 0x10, 0x63, 0x6f, // t h - - - - c o | |
52 | 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x6c, 0x6f, // n t e n t - l o | |
53 | 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, // c a t i o n - - | |
54 | 0x00, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n | |
55 | 0x74, 0x2d, 0x6d, 0x64, 0x35, 0x00, 0x00, 0x00, // t - m d 5 - - - | |
56 | 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, // - c o n t e n t | |
57 | 0x2d, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, // - r a n g e - - | |
58 | 0x00, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, // - - c o n t e n | |
59 | 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, // t - t y p e - - | |
60 | 0x00, 0x04, 0x64, 0x61, 0x74, 0x65, 0x00, 0x00, // - - d a t e - - | |
61 | 0x00, 0x04, 0x65, 0x74, 0x61, 0x67, 0x00, 0x00, // - - e t a g - - | |
62 | 0x00, 0x06, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, // - - e x p e c t | |
63 | 0x00, 0x00, 0x00, 0x07, 0x65, 0x78, 0x70, 0x69, // - - - - e x p i | |
64 | 0x72, 0x65, 0x73, 0x00, 0x00, 0x00, 0x04, 0x66, // r e s - - - - f | |
65 | 0x72, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x68, // r o m - - - - h | |
66 | 0x6f, 0x73, 0x74, 0x00, 0x00, 0x00, 0x08, 0x69, // o s t - - - - i | |
67 | 0x66, 0x2d, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, // f - m a t c h - | |
68 | 0x00, 0x00, 0x11, 0x69, 0x66, 0x2d, 0x6d, 0x6f, // - - - i f - m o | |
69 | 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x2d, 0x73, // d i f i e d - s | |
70 | 0x69, 0x6e, 0x63, 0x65, 0x00, 0x00, 0x00, 0x0d, // i n c e - - - - | |
71 | 0x69, 0x66, 0x2d, 0x6e, 0x6f, 0x6e, 0x65, 0x2d, // i f - n o n e - | |
72 | 0x6d, 0x61, 0x74, 0x63, 0x68, 0x00, 0x00, 0x00, // m a t c h - - - | |
73 | 0x08, 0x69, 0x66, 0x2d, 0x72, 0x61, 0x6e, 0x67, // - i f - r a n g | |
74 | 0x65, 0x00, 0x00, 0x00, 0x13, 0x69, 0x66, 0x2d, // e - - - - i f - | |
75 | 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, // u n m o d i f i | |
76 | 0x65, 0x64, 0x2d, 0x73, 0x69, 0x6e, 0x63, 0x65, // e d - s i n c e | |
77 | 0x00, 0x00, 0x00, 0x0d, 0x6c, 0x61, 0x73, 0x74, // - - - - l a s t | |
78 | 0x2d, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, // - m o d i f i e | |
79 | 0x64, 0x00, 0x00, 0x00, 0x08, 0x6c, 0x6f, 0x63, // d - - - - l o c | |
80 | 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, // a t i o n - - - | |
81 | 0x0c, 0x6d, 0x61, 0x78, 0x2d, 0x66, 0x6f, 0x72, // - m a x - f o r | |
82 | 0x77, 0x61, 0x72, 0x64, 0x73, 0x00, 0x00, 0x00, // w a r d s - - - | |
83 | 0x06, 0x70, 0x72, 0x61, 0x67, 0x6d, 0x61, 0x00, // - p r a g m a - | |
84 | 0x00, 0x00, 0x12, 0x70, 0x72, 0x6f, 0x78, 0x79, // - - - p r o x y | |
85 | 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, // - a u t h e n t | |
86 | 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, 0x00, // i c a t e - - - | |
87 | 0x13, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2d, 0x61, // - p r o x y - a | |
88 | 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, // u t h o r i z a | |
89 | 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x05, // t i o n - - - - | |
90 | 0x72, 0x61, 0x6e, 0x67, 0x65, 0x00, 0x00, 0x00, // r a n g e - - - | |
91 | 0x07, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x72, // - r e f e r e r | |
92 | 0x00, 0x00, 0x00, 0x0b, 0x72, 0x65, 0x74, 0x72, // - - - - r e t r | |
93 | 0x79, 0x2d, 0x61, 0x66, 0x74, 0x65, 0x72, 0x00, // y - a f t e r - | |
94 | 0x00, 0x00, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, // - - - s e r v e | |
95 | 0x72, 0x00, 0x00, 0x00, 0x02, 0x74, 0x65, 0x00, // r - - - - t e - | |
96 | 0x00, 0x00, 0x07, 0x74, 0x72, 0x61, 0x69, 0x6c, // - - - t r a i l | |
97 | 0x65, 0x72, 0x00, 0x00, 0x00, 0x11, 0x74, 0x72, // e r - - - - t r | |
98 | 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x2d, 0x65, // a n s f e r - e | |
99 | 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, // n c o d i n g - | |
100 | 0x00, 0x00, 0x07, 0x75, 0x70, 0x67, 0x72, 0x61, // - - - u p g r a | |
101 | 0x64, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x75, 0x73, // d e - - - - u s | |
102 | 0x65, 0x72, 0x2d, 0x61, 0x67, 0x65, 0x6e, 0x74, // e r - a g e n t | |
103 | 0x00, 0x00, 0x00, 0x04, 0x76, 0x61, 0x72, 0x79, // - - - - v a r y | |
104 | 0x00, 0x00, 0x00, 0x03, 0x76, 0x69, 0x61, 0x00, // - - - - v i a - | |
105 | 0x00, 0x00, 0x07, 0x77, 0x61, 0x72, 0x6e, 0x69, // - - - w a r n i | |
106 | 0x6e, 0x67, 0x00, 0x00, 0x00, 0x10, 0x77, 0x77, // n g - - - - w w | |
107 | 0x77, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, // w - a u t h e n | |
108 | 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x00, 0x00, // t i c a t e - - | |
109 | 0x00, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, // - - m e t h o d | |
110 | 0x00, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, // - - - - g e t - | |
111 | 0x00, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, // - - - s t a t u | |
112 | 0x73, 0x00, 0x00, 0x00, 0x06, 0x32, 0x30, 0x30, // s - - - - 2 0 0 | |
113 | 0x20, 0x4f, 0x4b, 0x00, 0x00, 0x00, 0x07, 0x76, // - O K - - - - v | |
114 | 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x00, // e r s i o n - - | |
115 | 0x00, 0x08, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, // - - H T T P - 1 | |
116 | 0x2e, 0x31, 0x00, 0x00, 0x00, 0x03, 0x75, 0x72, // - 1 - - - - u r | |
117 | 0x6c, 0x00, 0x00, 0x00, 0x06, 0x70, 0x75, 0x62, // l - - - - p u b | |
118 | 0x6c, 0x69, 0x63, 0x00, 0x00, 0x00, 0x0a, 0x73, // l i c - - - - s | |
119 | 0x65, 0x74, 0x2d, 0x63, 0x6f, 0x6f, 0x6b, 0x69, // e t - c o o k i | |
120 | 0x65, 0x00, 0x00, 0x00, 0x0a, 0x6b, 0x65, 0x65, // e - - - - k e e | |
121 | 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x00, // p - a l i v e - | |
122 | 0x00, 0x00, 0x06, 0x6f, 0x72, 0x69, 0x67, 0x69, // - - - o r i g i | |
123 | 0x6e, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x32, // n 1 0 0 1 0 1 2 | |
124 | 0x30, 0x31, 0x32, 0x30, 0x32, 0x32, 0x30, 0x35, // 0 1 2 0 2 2 0 5 | |
125 | 0x32, 0x30, 0x36, 0x33, 0x30, 0x30, 0x33, 0x30, // 2 0 6 3 0 0 3 0 | |
126 | 0x32, 0x33, 0x30, 0x33, 0x33, 0x30, 0x34, 0x33, // 2 3 0 3 3 0 4 3 | |
127 | 0x30, 0x35, 0x33, 0x30, 0x36, 0x33, 0x30, 0x37, // 0 5 3 0 6 3 0 7 | |
128 | 0x34, 0x30, 0x32, 0x34, 0x30, 0x35, 0x34, 0x30, // 4 0 2 4 0 5 4 0 | |
129 | 0x36, 0x34, 0x30, 0x37, 0x34, 0x30, 0x38, 0x34, // 6 4 0 7 4 0 8 4 | |
130 | 0x30, 0x39, 0x34, 0x31, 0x30, 0x34, 0x31, 0x31, // 0 9 4 1 0 4 1 1 | |
131 | 0x34, 0x31, 0x32, 0x34, 0x31, 0x33, 0x34, 0x31, // 4 1 2 4 1 3 4 1 | |
132 | 0x34, 0x34, 0x31, 0x35, 0x34, 0x31, 0x36, 0x34, // 4 4 1 5 4 1 6 4 | |
133 | 0x31, 0x37, 0x35, 0x30, 0x32, 0x35, 0x30, 0x34, // 1 7 5 0 2 5 0 4 | |
134 | 0x35, 0x30, 0x35, 0x32, 0x30, 0x33, 0x20, 0x4e, // 5 0 5 2 0 3 - N | |
135 | 0x6f, 0x6e, 0x2d, 0x41, 0x75, 0x74, 0x68, 0x6f, // o n - A u t h o | |
136 | 0x72, 0x69, 0x74, 0x61, 0x74, 0x69, 0x76, 0x65, // r i t a t i v e | |
137 | 0x20, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, // - I n f o r m a | |
138 | 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x30, 0x34, 0x20, // t i o n 2 0 4 - | |
139 | 0x4e, 0x6f, 0x20, 0x43, 0x6f, 0x6e, 0x74, 0x65, // N o - C o n t e | |
140 | 0x6e, 0x74, 0x33, 0x30, 0x31, 0x20, 0x4d, 0x6f, // n t 3 0 1 - M o | |
141 | 0x76, 0x65, 0x64, 0x20, 0x50, 0x65, 0x72, 0x6d, // v e d - P e r m | |
142 | 0x61, 0x6e, 0x65, 0x6e, 0x74, 0x6c, 0x79, 0x34, // a n e n t l y 4 | |
143 | 0x30, 0x30, 0x20, 0x42, 0x61, 0x64, 0x20, 0x52, // 0 0 - B a d - R | |
144 | 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x34, 0x30, // e q u e s t 4 0 | |
145 | 0x31, 0x20, 0x55, 0x6e, 0x61, 0x75, 0x74, 0x68, // 1 - U n a u t h | |
146 | 0x6f, 0x72, 0x69, 0x7a, 0x65, 0x64, 0x34, 0x30, // o r i z e d 4 0 | |
147 | 0x33, 0x20, 0x46, 0x6f, 0x72, 0x62, 0x69, 0x64, // 3 - F o r b i d | |
148 | 0x64, 0x65, 0x6e, 0x34, 0x30, 0x34, 0x20, 0x4e, // d e n 4 0 4 - N | |
149 | 0x6f, 0x74, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, // o t - F o u n d | |
150 | 0x35, 0x30, 0x30, 0x20, 0x49, 0x6e, 0x74, 0x65, // 5 0 0 - I n t e | |
151 | 0x72, 0x6e, 0x61, 0x6c, 0x20, 0x53, 0x65, 0x72, // r n a l - S e r | |
152 | 0x76, 0x65, 0x72, 0x20, 0x45, 0x72, 0x72, 0x6f, // v e r - E r r o | |
153 | 0x72, 0x35, 0x30, 0x31, 0x20, 0x4e, 0x6f, 0x74, // r 5 0 1 - N o t | |
154 | 0x20, 0x49, 0x6d, 0x70, 0x6c, 0x65, 0x6d, 0x65, // - I m p l e m e | |
155 | 0x6e, 0x74, 0x65, 0x64, 0x35, 0x30, 0x33, 0x20, // n t e d 5 0 3 - | |
156 | 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x20, // S e r v i c e - | |
157 | 0x55, 0x6e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, // U n a v a i l a | |
158 | 0x62, 0x6c, 0x65, 0x4a, 0x61, 0x6e, 0x20, 0x46, // b l e J a n - F | |
159 | 0x65, 0x62, 0x20, 0x4d, 0x61, 0x72, 0x20, 0x41, // e b - M a r - A | |
160 | 0x70, 0x72, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x4a, // p r - M a y - J | |
161 | 0x75, 0x6e, 0x20, 0x4a, 0x75, 0x6c, 0x20, 0x41, // u n - J u l - A | |
162 | 0x75, 0x67, 0x20, 0x53, 0x65, 0x70, 0x74, 0x20, // u g - S e p t - | |
163 | 0x4f, 0x63, 0x74, 0x20, 0x4e, 0x6f, 0x76, 0x20, // O c t - N o v - | |
164 | 0x44, 0x65, 0x63, 0x20, 0x30, 0x30, 0x3a, 0x30, // D e c - 0 0 - 0 | |
165 | 0x30, 0x3a, 0x30, 0x30, 0x20, 0x4d, 0x6f, 0x6e, // 0 - 0 0 - M o n | |
166 | 0x2c, 0x20, 0x54, 0x75, 0x65, 0x2c, 0x20, 0x57, // - - T u e - - W | |
167 | 0x65, 0x64, 0x2c, 0x20, 0x54, 0x68, 0x75, 0x2c, // e d - - T h u - | |
168 | 0x20, 0x46, 0x72, 0x69, 0x2c, 0x20, 0x53, 0x61, // - F r i - - S a | |
169 | 0x74, 0x2c, 0x20, 0x53, 0x75, 0x6e, 0x2c, 0x20, // t - - S u n - - | |
170 | 0x47, 0x4d, 0x54, 0x63, 0x68, 0x75, 0x6e, 0x6b, // G M T c h u n k | |
171 | 0x65, 0x64, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, // e d - t e x t - | |
172 | 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x69, 0x6d, 0x61, // h t m l - i m a | |
173 | 0x67, 0x65, 0x2f, 0x70, 0x6e, 0x67, 0x2c, 0x69, // g e - p n g - i | |
174 | 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x6a, 0x70, 0x67, // m a g e - j p g | |
175 | 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x2f, 0x67, // - i m a g e - g | |
176 | 0x69, 0x66, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // i f - a p p l i | |
177 | 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x | |
178 | 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, // m l - a p p l i | |
179 | 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, // c a t i o n - x | |
180 | 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, // h t m l - x m l | |
181 | 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, // - t e x t - p l | |
182 | 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, // a i n - t e x t | |
183 | 0x2f, 0x6a, 0x61, 0x76, 0x61, 0x73, 0x63, 0x72, // - j a v a s c r | |
184 | 0x69, 0x70, 0x74, 0x2c, 0x70, 0x75, 0x62, 0x6c, // i p t - p u b l | |
185 | 0x69, 0x63, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, // i c p r i v a t | |
186 | 0x65, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, 0x65, // e m a x - a g e | |
187 | 0x3d, 0x67, 0x7a, 0x69, 0x70, 0x2c, 0x64, 0x65, // - g z i p - d e | |
188 | 0x66, 0x6c, 0x61, 0x74, 0x65, 0x2c, 0x73, 0x64, // f l a t e - s d | |
189 | 0x63, 0x68, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, // c h c h a r s e | |
190 | 0x74, 0x3d, 0x75, 0x74, 0x66, 0x2d, 0x38, 0x63, // t - u t f - 8 c | |
191 | 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x69, // h a r s e t - i | |
192 | 0x73, 0x6f, 0x2d, 0x38, 0x38, 0x35, 0x39, 0x2d, // s o - 8 8 5 9 - | |
193 | 0x31, 0x2c, 0x75, 0x74, 0x66, 0x2d, 0x2c, 0x2a, // 1 - u t f - - - | |
194 | 0x2c, 0x65, 0x6e, 0x71, 0x3d, 0x30, 0x2e // - e n q - 0 - | |
195 | }; |