Blob


1 /*
2 * Copyright (c) 2021 Matthias Schmidt <xhr@giessen.ccc.de>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <sys/socket.h>
18 #include <sys/stat.h>
20 #include <err.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <unistd.h>
27 #include "log.h"
28 #include "twind.h"
30 static void
31 generate_meta(int status_code, char *meta_response_string, const char *mime)
32 {
33 switch(status_code) {
34 case STATUS_INPUT:
35 snprintf(meta_response_string, 1024, "%d Present input\r\n", status_code);
36 break;
37 case STATUS_SENSITIVE_INPUT:
38 snprintf(meta_response_string, 1024, "%d Present sensitive input\r\n",
39 status_code);
40 break;
41 case STATUS_SUCCESS:
42 if (mime == NULL)
43 /* Could not deducte mime type, so send text/gemini as default */
44 snprintf(meta_response_string, 1024, "%d text/gemini\r\n", status_code);
45 else
46 snprintf(meta_response_string, 1024, "%d %s\r\n", status_code, mime);
47 break;
48 case STATUS_REDIRECT_TEMP:
49 snprintf(meta_response_string, 1024, "%d Temporary redirect\r\n",
50 status_code);
51 break;
52 case STATUS_REDIRECT_PERM:
53 snprintf(meta_response_string, 1024, "%d Permanent redirect\r\n",
54 status_code);
55 break;
56 case STATUS_TEMP_UNAVAILABLE:
57 snprintf(meta_response_string, 1024, "%d Temporary failure\r\n",
58 status_code);
59 break;
60 case STATUS_SERVER_UNAVAILABLE:
61 snprintf(meta_response_string, 1024, "%d Server unavailable\r\n",
62 status_code);
63 break;
64 case STATUS_CGI_ERROR:
65 snprintf(meta_response_string, 1024, "%d CGI Error\r\n", status_code);
66 break;
67 case STATUS_PROXY_ERROR:
68 snprintf(meta_response_string, 1024, "%d Proxy error\r\n", status_code);
69 break;
70 case STATUS_SLOW_DOWN:
71 snprintf(meta_response_string, 1024, "%d Slow down\r\n", status_code);
72 break;
73 case STATUS_PERM_FAILURE:
74 snprintf(meta_response_string, 1024, "%d Permanent failure\r\n", status_code);
75 break;
76 case STATUS_NOT_FOUND:
77 snprintf(meta_response_string, 1024, "%d Resource not found\r\n",
78 status_code);
79 break;
80 case STATUS_GONE:
81 snprintf(meta_response_string, 1024, "%d Resource is gone\r\n", status_code);
82 break;
83 case STATUS_PROXY_REQUEST_REFUSED:
84 snprintf(meta_response_string, 1024, "%d Proxy request refused\r\n",
85 status_code);
86 break;
87 case STATUS_BAD_REQUEST:
88 snprintf(meta_response_string, 1024, "%d Bad Request\r\n", status_code);
89 break;
90 case STATUS_CLIENT_CERT_REQUIRED:
91 snprintf(meta_response_string, 1024, "%d Client Certificate Required\r\n",
92 status_code);
93 break;
94 case STATUS_CERT_NOT_AUTHORIZED:
95 snprintf(meta_response_string, 1024, "%d Certificate not authorized\r\n",
96 status_code);
97 break;
98 case STATUS_CERT_NOT_VALID:
99 snprintf(meta_response_string, 1024, "%d Certificate not valid\r\n",
100 status_code);
101 break;
102 default:
103 snprintf(meta_response_string, 1024, "%d Unkown status code\r\n",
104 status_code);
105 break;
109 int
110 send_non_success_response(SSL *ssl_peer, int status_code)
112 char meta[1024];
114 memset(meta, 0, sizeof(meta));
116 generate_meta(status_code, meta, NULL);
118 log_debug("Send non success response to client: %d", status_code);
120 if (SSL_write(ssl_peer, meta, strlen(meta)) <= 0) {
121 log_warn("Could not send response to client");
122 return -1;
125 return 0;
128 int
129 send_response(SSL *ssl_peer, int status_code, const char *gemini_file_path,
130 const char *mime)
132 char meta[1024];
133 char buffer[1024];
134 int fd = -1, len;
136 // <STATUS><SPACE><META><CR><LF>
138 memset(meta, 0, sizeof(meta));
139 memset(buffer, 0, sizeof(buffer));
141 generate_meta(status_code, meta, mime);
143 if (SSL_write(ssl_peer, meta, strlen(meta)) <= 0) {
144 log_warn("Could not send response to client");
145 return -1;
148 /* Close connection and do not send a response if status code is not
149 * a SUCCESS code
150 */
151 if (status_code < 30 && status_code >= 20) {
152 fd = open(gemini_file_path, O_RDONLY);
153 if (fd == -1) {
154 log_warn("Cannot open requested file");
155 goto out;
158 while ((len = read(fd, buffer, sizeof(buffer)-1)) > 0) {
159 if (SSL_write(ssl_peer, buffer, len) <= 0) {
160 log_warn("Could not send response to client");
161 return -1;
166 out:
167 close(fd);
169 return 0;
172 int
173 check_gemini_file(const char *gemini_file_path)
175 struct stat sb;
177 if (stat(gemini_file_path, &sb) == -1) {
178 log_warn("Cannot open requested file");
179 return -1;
182 if ((sb.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
183 log_warn("Cannot read requested file");
184 return -1;
187 if ((sb.st_mode & S_IFMT) == S_IFDIR)
188 return 1;
190 return 0;