summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Christian Pohle2021-11-16 03:35:13 +0100
committerMax Christian Pohle2021-11-16 03:35:13 +0100
commite2ca5eb8dde31ea31b7fa1014d087829e4146bac (patch)
tree79ddc9068ce9add59f5903d127f16d2c03314f23
parent89944e70b8e100e39ef65190ad1816dafde326eb (diff)
downloadohmycgi-e2ca5eb8dde31ea31b7fa1014d087829e4146bac.tar.bz2
ohmycgi-e2ca5eb8dde31ea31b7fa1014d087829e4146bac.zip
Basic functionality is finally there
We can: * skip HTTP status header * read the HTTP headers * find a boundary of multipart form data and if there was a boundary * parse further 'chunks', separated by this boundary, each with a HTTP header All of that was tested with text fields and binary data. This server implementation is insecure. Please do not use it for anything connected to the internet. It is simple though and can serve local daemons. TODO: callback to a parser function.
-rw-r--r--Makefile12
-rw-r--r--main.c336
2 files changed, 290 insertions, 58 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1f8a372
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,12 @@
1
2test: main
3 ./main
4
5main: clean
6
7clean:
8 rm -f ./main
9
10%:
11 $(CC) -pthread -g -o $@ $@.c
12
diff --git a/main.c b/main.c
index 6613df0..69ac01e 100644
--- a/main.c
+++ b/main.c
@@ -1,3 +1,5 @@
1#include <asm-generic/errno-base.h>
2#include <asm-generic/errno.h>
1#include <netinet/in.h> 3#include <netinet/in.h>
2#include <stdio.h> 4#include <stdio.h>
3#include <stdio_ext.h> 5#include <stdio_ext.h>
@@ -16,10 +18,7 @@
16#include <pthread.h> 18#include <pthread.h>
17 19
18 20
19 21void send_answer(FILE * f) {
20void send_answer(void * new_socket) {
21 FILE * f = fdopen((size_t) new_socket, "w");
22 puts("> sending answer...");
23 fputs("HTTP/1.0 200 OK\n", f); 22 fputs("HTTP/1.0 200 OK\n", f);
24 fputs("content-type: text/html\n\n", f); 23 fputs("content-type: text/html\n\n", f);
25 fputs("<html>", f); 24 fputs("<html>", f);
@@ -34,13 +33,256 @@ void send_answer(void * new_socket) {
34 fputs("<input type=\"file\" name=\"okay\" value=\"ok\" />", f); 33 fputs("<input type=\"file\" name=\"okay\" value=\"ok\" />", f);
35 fputs("<input type=\"submit\" name=\"okay\" value=\"ok\" />", f); 34 fputs("<input type=\"submit\" name=\"okay\" value=\"ok\" />", f);
36 fputs("</form>", f); 35 fputs("</form>", f);
37 fputs("</html>\n\0", f); 36 fputs("</html>", f);
38 fflush(f); 37}
38
39void * next_customer(size_t new_socket) {
40
41 char * output_buffer = NULL;
42 size_t output_buffer_length = 0;
43 FILE * output = open_memstream(&output_buffer, &output_buffer_length);
44
45 const int read_buffer_length = 10;
46 char read_buffer[read_buffer_length];
47
48 puts("\n\n########################################## Content Reader");
49 FILE * q = fdopen((size_t) new_socket, "r");
50 printf("fd : %p\n", q);
51
52 for(size_t size = -2 ; ; size = fread(read_buffer, read_buffer_length, 1, q)) {
53 if(-2 == size || (-1 == size && EWOULDBLOCK == errno)) {
54 usleep(1000);
55 continue;
56 } else if (1 != size) { // I expect one nmemb of data
57 break;
58 }
59
60 fwrite(read_buffer, read_buffer_length, 1, output);
61 fwrite(read_buffer, read_buffer_length, 1, stdout);
62 }
63 fflush(output);
64
65
66 puts("\n\n########################################## Content Parser");
67 typedef struct {
68 const char * name;
69 const char * value;
70 } NameValue;
71
72 NameValue namevalue[] = {
73 {"Content-Type", NULL},
74 {"Content-Length", NULL}
75 };
76
77 // token parser...
78 const char * boundary = NULL;
79 size_t boundary_length = 0;
80 char * search = NULL;
81 char * key = output_buffer;
82
83 char * saveptr = NULL;
84 char * content_start = NULL;
85 char * content_end = NULL;
86
87 while(1) {
88
89 printf("NEXT ITERATION with search '%s'\n", search);
90
91 for(char *
92 token = strtok_r(NULL == saveptr ? output_buffer : saveptr, NULL == search ? "\n\r" : search, &saveptr);
93 token != NULL;
94 token = strtok_r(NULL, search, &saveptr))
95 {
96
97 if(!search) { // first round: HTTP status code
98 puts("FIRST ITERATION");
99 printf("HTTP sanity check: %s\n", token);
100 search = ":";
101 continue;
102 }
103
104 if(!key) {
105 search = "\r\n"; // we found a key and expect the value to be between here and EOL
106 key = &token[strspn(token, search)]; // also remove leading spaces
107
108 if(0 == strncmp(token, "\n\r\n", 3)
109 || 0 == strncmp(token, "\n\n", 2)) {
110 search = "\r\n";
111 break;
112 }
113 } else { // value
114
115 token += strspn(token, ": ");
116 const char * value = token;
117
118 const char pattern[] = "multipart/form-data; boundary=";
119 if(!boundary && strstr(key, "Content-Type") && strncmp(value, pattern, sizeof(pattern))) {
120 boundary = &value[sizeof(pattern) + strspn(&value[sizeof(pattern)], "-")];
121 boundary_length = strlen(boundary);
122 printf("> [!] boundary detected '%s' ", boundary);
123 }
124
125 for(int i=0; i < sizeof(namevalue) / sizeof(NameValue); i++) {
126 // printf("next name: %s\n", namevalue[i].name);
127 if(NULL == namevalue[i].value && 0 == strcasecmp(namevalue[i].name, key)) {
128 namevalue[i].value = value;
129 break;
130 }
131 }
132
133 printf("> [%ld] \"%s\" :: \"%s\"\n", key - output_buffer, key, value); fflush(stdout);
134
135 search = ":";
136 key = NULL;
137 }
138 }
139
140 if(!key)
141 break;
142 // printf("We are now here: %ld\n", key - output_buffer);
143 printf("We are now here: %s\n", key);
144
145 // jump over the content...
146 content_start = key;
147 while((key = memchr(key, '\n', output_buffer_length - (key - output_buffer)))) {
148 if(!key) {
149 warnx("out at %p\n", key);
150 break;
151 } else {
152 key++;
153 }
154
155 if(key[0] == '-') {
156 if(0 == strncmp(&key[strspn(key, "-")], boundary, boundary_length)) {
157 puts("GEIL1");
158
159 if(key[-1] == '\n') key--;
160 if(key[-1] == '\r') key--;
161
162 content_end = key;
163 key += strspn(key, "\r\n-");
164 key += boundary_length;
165 key += strspn(key, "\r\n");
166 saveptr = key;
167 key = NULL;
168 search = ":";
169 break;
170 }
171 }
172 }
173
174 printf("content is %p - %p = %ld in size\n", content_end, content_start, content_end - content_start);
175 if(content_end - content_start > 1000) {
176 FILE * f_w = fopen("/tmp/test.gif", "w");
177 fwrite(content_start, content_end - content_start, 1, f_w);
178 fclose(f_w);
179 puts("FILE WRITTEN");
180 }
181
182 // printf("We have gone to %ld\n", key - output_buffer); fflush(stdout);
183 if(key && key[0] == '\0') // final boundary reached?
184 break;
185
186 // usleep(100000);
187 }
188
189
190 puts("> sending answer...");
191 FILE * f_w = fdopen((size_t) new_socket, "w");
192 send_answer(f_w);
193
194 fclose(f_w);
195 fclose(q);
39 puts("> answer sent."); 196 puts("> answer sent.");
40 fclose(f); 197
198 printf("still in knowledge of out boundary: %s\n", namevalue[0].value);
199
200 return NULL;
41} 201}
42 202
43 203
204
205int serve(int server_fd)
206{
207 struct sockaddr_in address;
208 socklen_t address_len = sizeof(address);
209 warnx("waiting for connections on server file descriptor %d", server_fd);
210
211 size_t new_socket = -1;
212 while(-1 !=
213 (new_socket = accept(server_fd,
214 (struct sockaddr*) &address,
215 &address_len)))
216 {
217 warnx("> Client %ld is connected via port %d", new_socket, address.sin_port);
218
219 // set non blocking mode...
220 fcntl((size_t) new_socket, F_SETFL,
221 fcntl((size_t) new_socket, F_GETFL) | O_NONBLOCK);
222
223 next_customer(new_socket);
224
225 /*
226 if(fork()) {
227 close(new_socket);
228 } else {
229 close(server_fd); // give the server free
230 next_customer(new_socket);
231 shutdown(new_socket, SHUT_RDWR);
232 exit(0);
233 }
234 */
235
236 // pthread_t thread_id;
237 // pthread_create(&thread_id, NULL, next_customer, (void*) new_socket);
238 // pthread_join(thread_id, NULL);
239 }
240
241 err(errno, "error serving");
242}
243
244#define PORT 8080
245int main(int argc, char const *argv[])
246{
247 int server_fd = -1, opt = 1;
248
249 struct sockaddr_in address = {
250 .sin_family = AF_INET,
251 .sin_addr.s_addr = INADDR_ANY,
252 .sin_port = htons(PORT)
253 };
254
255 // I <3 C
256 0 == (server_fd = socket(AF_INET, SOCK_STREAM, 0))
257 ? err(errno, NULL)
258 : setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))
259 ? err(errno, "setsockopt failed on socket with fileno %d", server_fd)
260 : bind(server_fd, (struct sockaddr*) &address, sizeof(address))
261 ? err(errno, NULL)
262 : listen(server_fd, SOMAXCONN)
263 ? err(errno, NULL)
264 : serve(server_fd)
265 ? err(errno, NULL)
266 : exit(EXIT_SUCCESS)
267 ;
268 return EXIT_FAILURE;
269}
270
271// void * next_customer_old(int new_socket)
272// {
273// char *hello = "Hello from server";
274// char buffer[1024] = {0};
275// int valread = read(new_socket, buffer, 1024);
276// printf("%s %d\n", buffer, valread);
277//
278// send(new_socket, hello, strlen(hello), 0);
279//
280// printf("Hello message sent\n");
281// return NULL;
282// }
283
284// IDEA ...
285//
44// #define FOREACH_HEADER(HEADER) \ 286// #define FOREACH_HEADER(HEADER) \
45// HEADER("Content-Type: multipart/form-data; boundary=") 287// HEADER("Content-Type: multipart/form-data; boundary=")
46// #define GENERATE_HEADERS(H) sizeof(H), H 288// #define GENERATE_HEADERS(H) sizeof(H), H
@@ -53,10 +295,37 @@ void send_answer(void * new_socket) {
53// const length_value headers[] = { 295// const length_value headers[] = {
54// FOREACH_HEADER(GENERATE_HEADERS) 296// FOREACH_HEADER(GENERATE_HEADERS)
55// }; 297// };
298//
56 299
57 300
301// READ WITH recv ...
302 /*
303 for(size_t size = -1 ; ; (size = recv(new_socket, read_buffer, read_buffer_length, 0))) {
304 if(-1 == size) {
305 usleep(10000);
306 printf("size: %ld\n", size);
307 if(EWOULDBLOCK == errno){
308 continue;
309 } else if (size == 0 || size < read_buffer_length) {
310 break;
311 }
312 } else {
313 read_buffer[size] = '\0';
314
315 fwrite(read_buffer, size, 1, output);
316 fwrite(read_buffer, size, 1, stdout);
317
318 // if(output_buffer_length > 4
319 // && (strspn(&output_buffer[output_buffer_length - 2], "\n") == 2
320 // || strspn(&output_buffer[output_buffer_length - 4], "\r\n") == 4))
321 // break;
322 }
323 }*/
324
325
326// fully working implementation...
327/*
58void * next_customer(void * new_socket) { 328void * next_customer(void * new_socket) {
59 if (new_socket == 0) return NULL;
60 329
61 const int MAX_HEADER_LINE_LENGTH = 1024; 330 const int MAX_HEADER_LINE_LENGTH = 1024;
62 const int MAX_BOUNDARY_LENGTH = 64; 331 const int MAX_BOUNDARY_LENGTH = 64;
@@ -151,56 +420,7 @@ void * next_customer(void * new_socket) {
151 send_answer(new_socket); 420 send_answer(new_socket);
152 puts("> answer sent."); 421 puts("> answer sent.");
153 422
154
155 puts("> file closed.");
156 //close((size_t) new_socket);
157 // pthread_exit(EXIT_SUCCESS);
158 return NULL; 423 return NULL;
159} 424}
160 425*/
161void serve(int server_fd)
162{
163 struct sockaddr_in address;
164 int addrlen = sizeof(address);
165 warn("waiting for connections on %d", server_fd);
166
167 for(size_t new_socket = 0
168 ; 1 ; new_socket=accept(server_fd, (struct sockaddr*) &address, (socklen_t*) &addrlen))
169 {
170 warn("next: %ld\n", new_socket);
171 // set non blocking mode...
172 fcntl((size_t) new_socket, F_SETFL,
173 fcntl((size_t) new_socket, F_GETFL) | O_NONBLOCK);
174
175 next_customer((void*) new_socket);
176 // pthread_t thread_id;
177 // pthread_create(&thread_id, NULL, next_customer, (void*) new_socket);
178 // pthread_join(thread_id, NULL);
179 }
180}
181
182#define PORT 8080
183int main(int argc, char const *argv[])
184{
185 int server_fd;
186 int opt = 1;
187
188 struct sockaddr_in address;
189 address.sin_family = AF_INET;
190 address.sin_addr.s_addr = INADDR_ANY;
191 address.sin_port = htons(PORT);
192
193 0 == (server_fd = socket(AF_INET, SOCK_STREAM, 0))
194 ? err(errno, NULL)
195 : setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))
196 ? err(errno, "setsockopt failed on socket with fileno %d", server_fd)
197 : bind(server_fd, (struct sockaddr*) &address, sizeof(address))
198 ? err(errno, NULL)
199 : listen(server_fd, SOMAXCONN)
200 ? err(errno, NULL)
201 : serve(server_fd);
202
203 return 0;
204}
205
206// vim: shiftwidth=2 tabstop=2 number 426// vim: shiftwidth=2 tabstop=2 number
..