2 * rs-serve - (c) 2013 Niklas E. Cathor
4 * This program is distributed in the hope that it will be useful,
5 * but WITHOUT ANY WARRANTY; without even the implied warranty of
6 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7 * GNU Affero General Public License for more details.
9 * You should have received a copy of the GNU Affero General Public License
10 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 #define ASSERT(cond, desc) { \
17 perror(#desc " failed"); \
21 #define ASSERT_NOT_NULL(var, desc) ASSERT((var) != NULL, desc)
22 #define ASSERT_ZERO(var, desc) ASSERT((var) == 0, desc)
23 #define ASSERT_NOT_EQ(a, b, desc) ASSERT((a) != (b), desc)
25 unsigned int request_count;
27 static const char * method_strmap[] = {
46 void handle_signal(evutil_socket_t fd, short events, void *arg) {
47 struct signalfd_siginfo siginfo;
48 if(read(fd, &siginfo, sizeof(siginfo)) < 0) {
49 log_error("Failed to read signal: %s", strerror(errno));
52 switch(siginfo.ssi_signo) {
54 log_info("SIGINT caught, exiting.");
58 log_info("SIGTERM caught, exiting.");
62 log_error("Unhandled signal caught: %s", strsignal(siginfo.ssi_signo));
66 struct event_base *rs_event_base = NULL;
68 void log_event_base_message(int severity, const char *msg) {
69 char *format = "(from libevent) %s";
72 log_debug(format, msg);
75 log_info(format, msg);
78 log_error(format, msg);
81 log_warn(format, msg);
86 static evhtp_res finish_request(evhtp_request_t *req, void *arg) {
88 log_info("[rc=%d] %s %s -> %d (fini: %d)", request_count, method_strmap[req->method], req->uri->path->full, req->status, req->finished);
92 static void handle_storage(evhtp_request_t *req, void *arg) {
94 log_info("[rc=%d] (start) %s %s", request_count, method_strmap[req->method], req->uri->path->full, req->status, req->finished);
95 dispatch_storage(req, arg);
98 static void handle_static(evhtp_request_t *req, void *arg) {
99 log_info("static URI path \"%s\"", req->uri->path->full);
100 if (req->method != htp_method_GET) {
101 req->status = EVHTP_RES_METHNALLOWED;
102 } else if ((req->uri->path->full [0] != '/') || strstr (req->uri->path->full, "/..")) {
103 req->status = EVHTP_RES_SERVERR;
105 char *path = malloc (RS_STATIC_DIR_LEN + strlen (req->uri->path->full) + 10 + 1);
107 log_error("malloc() failed");
108 req->status = EVHTP_RES_SERVERR;
111 char *mime_type = NULL;
112 int free_mime_type = 0;
114 sprintf (path, "%s%s", RS_STATIC_DIR, req->uri->path->full);
115 if (path [strlen (path) - 1] == '/') {
116 strcat (path, "index.html");
118 log_debug("static filename \"%s\"", path);
120 // mime type is either passed in ... (such as for directory listings)
121 if(mime_type == NULL) {
122 // ... or detected based on xattr
123 mime_type = content_type_from_xattr(path);
124 if(mime_type == NULL) {
126 // ... or guessed by libmagic
127 log_debug("mime type not given, detecting...");
128 mime_type = magic_file(magic_cookie, path);
130 if(mime_type == NULL) {
131 // ... or defaulted to "application/octet-stream"
132 log_error("magic failed: %s", magic_error(magic_cookie));
133 mime_type = "application/octet-stream; charset=binary";
136 // xattr detected mime type and allocated memory for it
142 log_info("setting Content-Type of %s: %s", req->uri->path->full, mime_type);
143 ADD_RESP_HEADER_CP(req, "Content-Type", mime_type);
147 free((char*)mime_type);
150 fh = open (path, O_RDONLY);
153 req->status = EVHTP_RES_NOTFOUND;
157 while ((rdlen = read (fh, buf, 1024)) > 0) {
158 evbuffer_add (req->buffer_out, buf, rdlen);
161 req->status = EVHTP_RES_OK;
165 evhtp_send_reply(req, req->status);
168 static int dummy_ssl_verify_callback(int ok, X509_STORE_CTX * x509_store) {
172 static int dummy_check_issued_cb(X509_STORE_CTX * ctx, X509 * x, X509 * issuer) {
177 magic_t magic_cookie;
179 int main(int argc, char **argv) {
181 init_config(argc, argv);
183 open_authorizations("r");
187 /** OPEN MAGIC DATABASE **/
189 magic_cookie = magic_open(MAGIC_MIME);
190 if(magic_load(magic_cookie, RS_MAGIC_DATABASE) != 0) {
191 log_error("Failed to load magic database: %s", magic_error(magic_cookie));
195 log_info("starting process: main");
197 if(prctl(PR_SET_NAME, "rs-serve [main]", 0, 0, 0) != 0) {
198 log_error("Failed to set process name: %s", strerror(errno));
201 /** SETUP EVENT BASE **/
203 rs_event_base = event_base_new();
204 ASSERT_NOT_NULL(rs_event_base, "event_base_new()");
205 log_debug("libevent method: %s", event_base_get_method(rs_event_base));
206 event_set_log_callback(log_event_base_message);
208 // TODO: add error cb to base
211 /** SETUP LISTENER **/
213 struct sockaddr_in6 sin;
214 memset(&sin, 0, sizeof(struct sockaddr_in6));
215 sin.sin6_family = AF_INET6;
216 //ALREADY_DONE// sin.sin6_addr.s_addr = htonl(0);
217 sin.sin6_port = htons(RS_PORT);
219 evhtp_t *server = evhtp_new(rs_event_base, NULL);
222 evhtp_ssl_cfg_t ssl_config = {
223 .pemfile = RS_SSL_CERT_PATH,
224 .privfile = RS_SSL_KEY_PATH,
225 .cafile = RS_SSL_CA_PATH,
228 .ciphers = "RC4+RSA:HIGH:+MEDIUM:+LOW",
229 .ssl_opts = SSL_OP_NO_SSLv2,
230 .ssl_ctx_timeout = 60*60*48,
231 .verify_peer = SSL_VERIFY_PEER,
233 .x509_verify_cb = dummy_ssl_verify_callback,
234 .x509_chk_issued_cb = dummy_check_issued_cb,
235 .scache_type = evhtp_ssl_scache_type_internal,
237 .scache_timeout = 1024,
244 if(evhtp_ssl_init(server, &ssl_config) != 0) {
245 log_error("evhtp_ssl_init() failed");
252 evhtp_callback_cb webfinger_cb = (RS_WEBFINGER_ENABLED ?
253 handle_webfinger : reject_webfinger);
254 evhtp_set_cb(server, "/.well-known/webfinger", webfinger_cb, NULL);
255 // support legacy webfinger clients (we don't support XRD though):
256 evhtp_set_cb(server, "/.well-known/host-meta", webfinger_cb, NULL);
257 evhtp_set_cb(server, "/.well-known/host-meta.json", webfinger_cb, NULL);
261 evhtp_callback_t *storage_cb = evhtp_set_regex_cb(server, "^/storage/([^/]+/[^/]+)/.*$", handle_storage, NULL);
263 evhtp_set_hook(&storage_cb->hooks, evhtp_hook_on_request_fini, finish_request, NULL);
267 evhtp_set_gencb(server, handle_static, NULL);
269 if(evhtp_bind_sockaddr(server, (struct sockaddr*)&sin, sizeof(sin), 1024) != 0) {
270 log_error("evhtp_bind_sockaddr() failed: %s", strerror(errno));
274 /** SETUP SIGNALS **/
277 sigemptyset(&sigmask);
278 sigaddset(&sigmask, SIGINT);
279 sigaddset(&sigmask, SIGTERM);
280 sigaddset(&sigmask, SIGCHLD);
281 ASSERT_ZERO(sigprocmask(SIG_BLOCK, &sigmask, NULL), "sigprocmask()");
282 int sfd = signalfd(-1, &sigmask, SFD_NONBLOCK);
283 ASSERT_NOT_EQ(sfd, -1, "signalfd()");
285 struct event *signal_event = event_new(rs_event_base, sfd, EV_READ | EV_PERSIST,
286 handle_signal, NULL);
287 event_add(signal_event, NULL);
289 /** RUN EVENT LOOP **/
294 event_reinit(rs_event_base);
296 if(RS_LOG_FILE == stdout) {
297 log_warn("No --log-file option given. Future output will be lost.");
298 freopen("/dev/null", "r", stdout);
299 freopen("/dev/null", "r", stderr);
302 return event_base_dispatch(rs_event_base);
304 printf("rs-serve detached with pid %d\n", pid);
306 fprintf(RS_PID_FILE, "%d", pid);
313 fprintf(RS_PID_FILE, "%d", getpid());
316 return event_base_dispatch(rs_event_base);