diff options
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | main.c | 336 |
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 | |||
2 | test: main | ||
3 | ./main | ||
4 | |||
5 | main: clean | ||
6 | |||
7 | clean: | ||
8 | rm -f ./main | ||
9 | |||
10 | %: | ||
11 | $(CC) -pthread -g -o $@ $@.c | ||
12 | |||
@@ -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 | 21 | void send_answer(FILE * f) { | |
20 | void 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 | |||
39 | void * 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 | |||
205 | int 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 | ||
245 | int 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 | /* | ||
58 | void * next_customer(void * new_socket) { | 328 | void * 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 | */ | |
161 | void 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 | ||
183 | int 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 |