1 /* Copyright 2021, Martin Bays <mbays@sdf.org>
2 * SPDX-License-Identifier: GPL-3.0-or-later */
7 #include <sys/socket.h>
14 /* Decode percent-escapes in place */
15 static void urldecode(char *p
) {
23 if (sscanf(p
, "%2hhx", w
) == 1) {
36 #define MAX_REQUEST_SZ 4096
37 /* Parse SCGI request with null body from fd s and place gemini-relevant
38 * header values in request_info.
39 * Return false on failure. */
40 static bool parse_SCGI(int s
, respond_cb respond
, void *respond_object
)
42 Request_Info request_info
= { };
43 char buf
[MAX_REQUEST_SZ
];
46 int r2
= read(s
, buf
+ r
, MAX_REQUEST_SZ
- r
);
54 if (sscanf(buf
, "%u:", &sz
) == 1) {
56 snprintf(buf2
, 16, "%u:,", sz
);
57 const int full_sz
= sz
+ strlen(buf2
);
59 if (full_sz
> MAX_REQUEST_SZ
) {
60 fprintf(stderr
, "Bad request: Oversized, %u\n", sz
);
69 fprintf(stderr
, "Bad request: non-empty body.\n");
75 const char* end
= &buf
[r
-1];
79 } else if (buf
[r
-1] != ',' || buf
[r
-2] != 0) {
80 fprintf(stderr
, "Bad request: improperly terminated header.\n");
85 while (*p
!= ':') p
++;
93 fprintf(stderr
, "Bad request: key with no value.\n");
101 if (strcmp(key
, "QUERY_STRING") == 0) {
103 request_info
.query_string_decoded
= val
;
104 } else if (strcmp(key
, "SCRIPT_PATH") == 0) {
105 request_info
.script_path
= val
;
106 } else if (strcmp(key
, "PATH_INFO") == 0) {
107 request_info
.path_info
= val
;
108 } else if (strcmp(key
, "SERVER_NAME") == 0) {
109 request_info
.server_name
= val
;
110 } else if (strcmp(key
, "SERVER_PORT") == 0) {
111 request_info
.server_port
= val
;
112 } else if (strcmp(key
, "REMOTE_ADDR") == 0) {
113 request_info
.remote_addr
= val
;
114 } else if (strcmp(key
, "TLS_CLIENT_ISSUER") == 0) {
115 request_info
.tls_client_issuer
= val
;
116 } else if (strcmp(key
, "TLS_CLIENT_ISSUER_CN") == 0) {
117 request_info
.tls_client_issuer_cn
= val
;
118 } else if (strcmp(key
, "TLS_CLIENT_SUBJECT") == 0) {
119 request_info
.tls_client_subject
= val
;
120 } else if (strcmp(key
, "TLS_CLIENT_SUBJECT_CN") == 0) {
121 request_info
.tls_client_subject_cn
= val
;
122 } else if (strcmp(key
, "TLS_CLIENT_HASH") == 0) {
123 if (strncmp(val
, "SHA256:", 7) == 0) val
+= 7;
124 request_info
.tls_client_hash
= val
;
129 respond(respond_object
, &request_info
, s
);
134 void runSCGI(const char *socket_path
, respond_cb respond
, void *respond_object
)
136 // Unix socket gubbins based on
137 // https://beej.us/guide/bgipc/html/multi/unixsock.html
139 const int s
= socket(AF_UNIX
, SOCK_STREAM
, 0);
145 struct sockaddr_un local
;
146 local
.sun_family
= AF_UNIX
;
147 strcpy(local
.sun_path
, socket_path
);
148 unlink(local
.sun_path
);
149 if (bind(s
, (struct sockaddr
*)&local
,
150 strlen(local
.sun_path
) + sizeof(local
.sun_family
)) == -1) {
155 if (listen(s
, 20) == -1) {
161 const int s2
= accept(s
, NULL
, NULL
);
167 parse_SCGI(s2
, respond
, respond_object
);