1 /*
2 http.c
3 Copyright (C) 1999 Lars Brinkhoff. See COPYING for terms and conditions.
4
5 bug alert: parse_header() doesn't handle header fields that are extended
6 over multiple lines.
7 */
8
9 #include <time.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include "http.h"
15 #include "common.h"
16
17 static inline ssize_t
http_method(int fd,Http_destination * dest,Http_method method,ssize_t length)18 http_method (int fd, Http_destination *dest,
19 Http_method method, ssize_t length)
20 {
21 char str[1024]; /* FIXME: possible buffer overflow */
22 Http_request *request;
23 ssize_t n;
24
25 if (fd == -1)
26 {
27 log_error ("http_method: fd == -1");
28 return -1;
29 }
30
31 n = 0;
32 if (dest->proxy_name != NULL)
33 n = sprintf (str, "http://%s:%d", dest->host_name, dest->host_port);
34 sprintf (str + n, "/index.html?crap=%ld", time (NULL));
35
36 request = http_create_request (method, str, 1, 1);
37 if (request == NULL)
38 return -1;
39
40 sprintf (str, "%s:%d", dest->host_name, dest->host_port);
41 http_add_header (&request->header, "Host", str);
42
43 if (length >= 0)
44 {
45 sprintf (str, "%d", length);
46 http_add_header (&request->header, "Content-Length", str);
47 }
48
49 http_add_header (&request->header, "Connection", "close");
50
51 if (dest->proxy_authorization)
52 {
53 http_add_header (&request->header,
54 "Proxy-Authorization",
55 dest->proxy_authorization);
56 }
57
58 if (dest->user_agent)
59 {
60 http_add_header (&request->header,
61 "User-Agent",
62 dest->user_agent);
63 }
64
65 n = http_write_request (fd, request);
66 http_destroy_request (request);
67 return n;
68 }
69
70 ssize_t
http_get(int fd,Http_destination * dest)71 http_get (int fd, Http_destination *dest)
72 {
73 return http_method (fd, dest, HTTP_GET, -1);
74 }
75
76 ssize_t
http_put(int fd,Http_destination * dest,size_t length)77 http_put (int fd, Http_destination *dest, size_t length)
78 {
79 return http_method (fd, dest, HTTP_PUT, (ssize_t)length);
80 }
81
82 ssize_t
http_post(int fd,Http_destination * dest,size_t length)83 http_post (int fd, Http_destination *dest, size_t length)
84 {
85 return http_method (fd, dest, HTTP_POST, (ssize_t)length);
86 }
87
88 int
http_error_to_errno(int err)89 http_error_to_errno (int err)
90 {
91 /* Error codes taken from RFC2068. */
92 switch (err)
93 {
94 case -1: /* system error */
95 return errno;
96 case -200: /* OK */
97 case -201: /* Created */
98 case -202: /* Accepted */
99 case -203: /* Non-Authoritative Information */
100 case -204: /* No Content */
101 case -205: /* Reset Content */
102 case -206: /* Partial Content */
103 return 0;
104 case -400: /* Bad Request */
105 log_error ("http_error_to_errno: 400 bad request");
106 return EIO;
107 case -401: /* Unauthorized */
108 log_error ("http_error_to_errno: 401 unauthorized");
109 return EACCES;
110 case -403: /* Forbidden */
111 log_error ("http_error_to_errno: 403 forbidden");
112 return EACCES;
113 case -404: /* Not Found */
114 log_error ("http_error_to_errno: 404 not found");
115 return ENOENT;
116 case -411: /* Length Required */
117 log_error ("http_error_to_errno: 411 length required");
118 return EIO;
119 case -413: /* Request Entity Too Large */
120 log_error ("http_error_to_errno: 413 request entity too large");
121 return EIO;
122 case -505: /* HTTP Version Not Supported */
123 log_error ("http_error_to_errno: 413 HTTP version not supported");
124 return EIO;
125 case -100: /* Continue */
126 case -101: /* Switching Protocols */
127 case -300: /* Multiple Choices */
128 case -301: /* Moved Permanently */
129 case -302: /* Moved Temporarily */
130 case -303: /* See Other */
131 case -304: /* Not Modified */
132 case -305: /* Use Proxy */
133 case -402: /* Payment Required */
134 case -405: /* Method Not Allowed */
135 case -406: /* Not Acceptable */
136 case -407: /* Proxy Autentication Required */
137 case -408: /* Request Timeout */
138 case -409: /* Conflict */
139 case -410: /* Gone */
140 case -412: /* Precondition Failed */
141 case -414: /* Request-URI Too Long */
142 case -415: /* Unsupported Media Type */
143 case -500: /* Internal Server Error */
144 case -501: /* Not Implemented */
145 case -502: /* Bad Gateway */
146 case -503: /* Service Unavailable */
147 case -504: /* Gateway Timeout */
148 log_error ("http_error_to_errno: HTTP error %d", err);
149 return EIO;
150 default:
151 log_error ("http_error_to_errno: unknown error %d", err);
152 return EIO;
153 }
154 }
155
156 static Http_method
http_string_to_method(const char * method,size_t n)157 http_string_to_method (const char *method, size_t n)
158 {
159 if (strncmp (method, "GET", n) == 0)
160 return HTTP_GET;
161 if (strncmp (method, "PUT", n) == 0)
162 return HTTP_PUT;
163 if (strncmp (method, "POST", n) == 0)
164 return HTTP_POST;
165 if (strncmp (method, "OPTIONS", n) == 0)
166 return HTTP_OPTIONS;
167 if (strncmp (method, "HEAD", n) == 0)
168 return HTTP_HEAD;
169 if (strncmp (method, "DELETE", n) == 0)
170 return HTTP_DELETE;
171 if (strncmp (method, "TRACE", n) == 0)
172 return HTTP_TRACE;
173 return -1;
174 }
175
176 static const char *
http_method_to_string(Http_method method)177 http_method_to_string (Http_method method)
178 {
179 switch (method)
180 {
181 case HTTP_GET: return "GET";
182 case HTTP_PUT: return "PUT";
183 case HTTP_POST: return "POST";
184 case HTTP_OPTIONS: return "OPTIONS";
185 case HTTP_HEAD: return "HEAD";
186 case HTTP_DELETE: return "DELETE";
187 case HTTP_TRACE: return "TRACE";
188 }
189 return "(uknown)";
190 }
191
192 static ssize_t
read_until(int fd,int ch,unsigned char ** data)193 read_until (int fd, int ch, unsigned char **data)
194 {
195 unsigned char *buf, *buf2;
196 ssize_t n, len, buf_size;
197
198 *data = NULL;
199
200 buf_size = 100;
201 buf = malloc (buf_size);
202 if (buf == NULL)
203 {
204 log_error ("read_until: out of memory");
205 return -1;
206 }
207
208 len = 0;
209 while ((n = read_all (fd, buf + len, 1)) == 1)
210 {
211 if (buf[len++] == ch)
212 break;
213 if (len + 1 == buf_size)
214 {
215 buf_size *= 2;
216 buf2 = realloc (buf, buf_size);
217 if (buf2 == NULL)
218 {
219 log_error ("read_until: realloc failed");
220 free (buf);
221 return -1;
222 }
223 buf = buf2;
224 }
225 }
226 if (n <= 0)
227 {
228 free (buf);
229 if (n == 0)
230 log_error ("read_until: closed");
231 else
232 log_error ("read_until: read error: %s", strerror (errno));
233 return n;
234 }
235
236 /* Shrink to minimum size + 1 in case someone wants to add a NUL. */
237 buf2 = realloc (buf, len + 1);
238 if (buf2 == NULL)
239 log_error ("read_until: realloc: shrink failed"); /* not fatal */
240 else
241 buf = buf2;
242
243 *data = buf;
244 return len;
245 }
246
247 static inline Http_header *
http_alloc_header(const char * name,const char * value)248 http_alloc_header (const char *name, const char *value)
249 {
250 Http_header *header;
251
252 header = malloc (sizeof (Http_header));
253 if (header == NULL)
254 return NULL;
255
256 header->name = header->value = NULL;
257 header->name = strdup (name);
258 header->value = strdup (value);
259 if (name == NULL || value == NULL)
260 {
261 if (name == NULL)
262 free ((char *)name);
263 if (value == NULL)
264 free ((char *)value);
265 free (header);
266 return NULL;
267 }
268
269 return header;
270 }
271
272 Http_header *
http_add_header(Http_header ** header,const char * name,const char * value)273 http_add_header (Http_header **header, const char *name, const char *value)
274 {
275 Http_header *new_header;
276
277 new_header = http_alloc_header (name, value);
278 if (new_header == NULL)
279 return NULL;
280
281 new_header->next = NULL;
282 while (*header)
283 header = &(*header)->next;
284 *header = new_header;
285
286 return new_header;
287 }
288
289 static ssize_t
parse_header(int fd,Http_header ** header)290 parse_header (int fd, Http_header **header)
291 {
292 unsigned char buf[2];
293 unsigned char *data;
294 Http_header *h;
295 size_t len;
296 ssize_t n;
297
298 *header = NULL;
299
300 n = read_all (fd, buf, 2);
301 if (n <= 0)
302 return n;
303 if (buf[0] == '\r' && buf[1] == '\n')
304 return n;
305
306 h = malloc (sizeof (Http_header));
307 if (h == NULL)
308 {
309 log_error ("parse_header: malloc failed");
310 return -1;
311 }
312 *header = h;
313 h->name = NULL;
314 h->value = NULL;
315
316 n = read_until (fd, ':', &data);
317 if (n <= 0)
318 return n;
319 data = realloc (data, n + 2);
320 if (data == NULL)
321 {
322 log_error ("parse_header: realloc failed");
323 return -1;
324 }
325 memmove (data + 2, data, n);
326 memcpy (data, buf, 2);
327 n += 2;
328 data[n - 1] = 0;
329 h->name = data;
330 len = n;
331
332 n = read_until (fd, '\r', &data);
333 if (n <= 0)
334 return n;
335 data[n - 1] = 0;
336 h->value = data;
337 len += n;
338
339 n = read_until (fd, '\n', &data);
340 if (n <= 0)
341 return n;
342 free (data);
343 if (n != 1)
344 {
345 log_error ("parse_header: invalid line ending");
346 return -1;
347 }
348 len += n;
349
350 log_verbose ("parse_header: %s:%s", h->name, h->value);
351
352 n = parse_header (fd, &h->next);
353 if (n <= 0)
354 return n;
355 len += n;
356
357 return len;
358 }
359
360 static ssize_t
http_write_header(int fd,Http_header * header)361 http_write_header (int fd, Http_header *header)
362 {
363 ssize_t n = 0, m;
364
365 if (header == NULL)
366 return write_all (fd, "\r\n", 2);
367
368 m = write_all (fd, (void *)header->name, strlen (header->name));
369 if (m == -1)
370 {
371 return -1;
372 }
373 n += m;
374
375 m = write_all (fd, ": ", 2);
376 if (m == -1)
377 {
378 return -1;
379 }
380 n += m;
381
382 m = write_all (fd, (void *)header->value, strlen (header->value));
383 if (m == -1)
384 {
385 return -1;
386 }
387 n += m;
388
389 m = write_all (fd, "\r\n", 2);
390 if (m == -1)
391 {
392 return -1;
393 }
394 n += m;
395
396 m = http_write_header (fd, header->next);
397 if (m == -1)
398 {
399 return -1;
400 }
401 n += m;
402
403 return n;
404 }
405
406 static void
http_destroy_header(Http_header * header)407 http_destroy_header (Http_header *header)
408 {
409 if (header == NULL)
410 return;
411
412 http_destroy_header (header->next);
413
414 if (header->name)
415 free ((char *)header->name);
416 if (header->value)
417 free ((char *)header->value);
418 free (header);
419 }
420
421 static inline Http_response *
http_allocate_response(const char * status_message)422 http_allocate_response (const char *status_message)
423 {
424 Http_response *response;
425
426 response = malloc (sizeof (Http_response));
427 if (response == NULL)
428 return NULL;
429
430 response->status_message = strdup (status_message);
431 if (response->status_message == NULL)
432 {
433 free (response);
434 return NULL;
435 }
436
437 return response;
438 }
439
440 Http_response *
http_create_response(int major_version,int minor_version,int status_code,const char * status_message)441 http_create_response (int major_version,
442 int minor_version,
443 int status_code,
444 const char *status_message)
445 {
446 Http_response *response;
447
448 response = http_allocate_response (status_message);
449 if (response == NULL)
450 return NULL;
451
452 response->major_version = major_version;
453 response->minor_version = minor_version;
454 response->status_code = status_code;
455 response->header = NULL;
456
457 return response;
458 }
459
460 ssize_t
http_parse_response(int fd,Http_response ** response_)461 http_parse_response (int fd, Http_response **response_)
462 {
463 Http_response *response;
464 unsigned char *data;
465 size_t len;
466 ssize_t n;
467
468 *response_ = NULL;
469
470 response = malloc (sizeof (Http_response));
471 if (response == NULL)
472 {
473 log_error ("http_parse_response: out of memory");
474 return -1;
475 }
476
477 response->major_version = -1;
478 response->minor_version = -1;
479 response->status_code = -1;
480 response->status_message = NULL;
481 response->header = NULL;
482
483 n = read_until (fd, '/', &data);
484 if (n <= 0)
485 {
486 free (response);
487 return n;
488 }
489 else if (n != 5 || memcmp (data, "HTTP", 4) != 0)
490 {
491 log_error ("http_parse_response: expected \"HTTP\"");
492 free (data);
493 free (response);
494 return -1;
495 }
496 free (data);
497 len = n;
498
499 n = read_until (fd, '.', &data);
500 if (n <= 0)
501 {
502 free (response);
503 return n;
504 }
505 data[n - 1] = 0;
506 response->major_version = atoi (data);
507 log_verbose ("http_parse_response: major version = %d",
508 response->major_version);
509 free (data);
510 len += n;
511
512 n = read_until (fd, ' ', &data);
513 if (n <= 0)
514 {
515 free (response);
516 return n;
517 }
518 data[n - 1] = 0;
519 response->minor_version = atoi (data);
520 log_verbose ("http_parse_response: minor version = %d",
521 response->minor_version);
522 free (data);
523 len += n;
524
525 n = read_until (fd, ' ', &data);
526 if (n <= 0)
527 {
528 free (response);
529 return n;
530 }
531 data[n - 1] = 0;
532 response->status_code = atoi (data);
533 log_verbose ("http_parse_response: status code = %d",
534 response->status_code);
535 free (data);
536 len += n;
537
538 n = read_until (fd, '\r', &data);
539 if (n <= 0)
540 {
541 free (response);
542 return n;
543 }
544 data[n - 1] = 0;
545 response->status_message = data;
546 log_verbose ("http_parse_response: status message = \"%s\"",
547 response->status_message);
548 len += n;
549
550 n = read_until (fd, '\n', &data);
551 if (n <= 0)
552 {
553 http_destroy_response (response);
554 return n;
555 }
556 free (data);
557 if (n != 1)
558 {
559 log_error ("http_parse_request: invalid line ending");
560 http_destroy_response (response);
561 return -1;
562 }
563 len += n;
564
565 n = parse_header (fd, &response->header);
566 if (n <= 0)
567 {
568 http_destroy_response (response);
569 return n;
570 }
571 len += n;
572
573 *response_ = response;
574 return len;
575 }
576
577 void
http_destroy_response(Http_response * response)578 http_destroy_response (Http_response *response)
579 {
580 if (response->status_message)
581 free ((char *)response->status_message);
582 http_destroy_header (response->header);
583 free (response);
584 }
585
586 static inline Http_request *
http_allocate_request(const char * uri)587 http_allocate_request (const char *uri)
588 {
589 Http_request *request;
590
591 request = malloc (sizeof (Http_request));
592 if (request == NULL)
593 return NULL;
594
595 request->uri = strdup (uri);
596 if (request->uri == NULL)
597 {
598 free (request);
599 return NULL;
600 }
601
602 return request;
603 }
604
605 Http_request *
http_create_request(Http_method method,const char * uri,int major_version,int minor_version)606 http_create_request (Http_method method,
607 const char *uri,
608 int major_version,
609 int minor_version)
610 {
611 Http_request *request;
612
613 request = http_allocate_request (uri);
614 if (request == NULL)
615 return NULL;
616
617 request->method = method;
618 request->major_version = major_version;
619 request->minor_version = minor_version;
620 request->header = NULL;
621
622 return request;
623 }
624
625 ssize_t
http_parse_request(int fd,Http_request ** request_)626 http_parse_request (int fd, Http_request **request_)
627 {
628 Http_request *request;
629 unsigned char *data;
630 size_t len;
631 ssize_t n;
632
633 *request_ = NULL;
634
635 request = malloc (sizeof (Http_request));
636 if (request == NULL)
637 {
638 log_error ("http_parse_request: out of memory");
639 return -1;
640 }
641
642 request->method = -1;
643 request->uri = NULL;
644 request->major_version = -1;
645 request->minor_version = -1;
646 request->header = NULL;
647
648 n = read_until (fd, ' ', &data);
649 if (n <= 0)
650 {
651 free (request);
652 return n;
653 }
654 request->method = http_string_to_method (data, n - 1);
655 if (request->method == -1)
656 {
657 log_error ("http_parse_request: expected an HTTP method");
658 free (data);
659 free (request);
660 return -1;
661 }
662 data[n - 1] = 0;
663 log_verbose ("http_parse_request: method = \"%s\"", data);
664 free (data);
665 len = n;
666
667 n = read_until (fd, ' ', &data);
668 if (n <= 0)
669 {
670 free (request);
671 return n;
672 }
673 data[n - 1] = 0;
674 request->uri = data;
675 len += n;
676 log_verbose ("http_parse_request: uri = \"%s\"", request->uri);
677
678 n = read_until (fd, '/', &data);
679 if (n <= 0)
680 {
681 http_destroy_request (request);
682 return n;
683 }
684 else if (n != 5 || memcmp (data, "HTTP", 4) != 0)
685 {
686 log_error ("http_parse_request: expected \"HTTP\"");
687 free (data);
688 http_destroy_request (request);
689 return -1;
690 }
691 free (data);
692 len = n;
693
694 n = read_until (fd, '.', &data);
695 if (n <= 0)
696 {
697 http_destroy_request (request);
698 return n;
699 }
700 data[n - 1] = 0;
701 request->major_version = atoi (data);
702 log_verbose ("http_parse_request: major version = %d",
703 request->major_version);
704 free (data);
705 len += n;
706
707 n = read_until (fd, '\r', &data);
708 if (n <= 0)
709 {
710 http_destroy_request (request);
711 return n;
712 }
713 data[n - 1] = 0;
714 request->minor_version = atoi (data);
715 log_verbose ("http_parse_request: minor version = %d",
716 request->minor_version);
717 free (data);
718 len += n;
719
720 n = read_until (fd, '\n', &data);
721 if (n <= 0)
722 {
723 http_destroy_request (request);
724 return n;
725 }
726 free (data);
727 if (n != 1)
728 {
729 log_error ("http_parse_request: invalid line ending");
730 http_destroy_request (request);
731 return -1;
732 }
733 len += n;
734
735 n = parse_header (fd, &request->header);
736 if (n <= 0)
737 {
738 http_destroy_request (request);
739 return n;
740 }
741 len += n;
742
743 *request_ = request;
744 return len;
745 }
746
747 ssize_t
http_write_request(int fd,Http_request * request)748 http_write_request (int fd, Http_request *request)
749 {
750 char str[1024]; /* FIXME: buffer overflow */
751 ssize_t n = 0;
752 size_t m;
753
754 m = sprintf (str, "%s %s HTTP/%d.%d\r\n",
755 http_method_to_string (request->method),
756 request->uri,
757 request->major_version,
758 request->minor_version);
759 m = write_all (fd, str, m);
760 log_verbose ("http_write_request: %s", str);
761 if (m == -1)
762 {
763 log_error ("http_write_request: write error: %s", strerror (errno));
764 return -1;
765 }
766 n += m;
767
768 m = http_write_header (fd, request->header);
769 if (m == -1)
770 {
771 return -1;
772 }
773 n += m;
774
775 return n;
776 }
777
778 void
http_destroy_request(Http_request * request)779 http_destroy_request (Http_request *request)
780 {
781 if (request->uri)
782 free ((char *)request->uri);
783 http_destroy_header (request->header);
784 free (request);
785 }
786
787 static Http_header *
http_header_find(Http_header * header,const char * name)788 http_header_find (Http_header *header, const char *name)
789 {
790 if (header == NULL)
791 return NULL;
792
793 if (strcmp (header->name, name) == 0)
794 return header;
795
796 return http_header_find (header->next, name);
797 }
798
799 const char *
http_header_get(Http_header * header,const char * name)800 http_header_get (Http_header *header, const char *name)
801 {
802 Http_header *h;
803
804 h = http_header_find (header, name);
805 if (h == NULL)
806 return NULL;
807
808 return h->value;
809 }
810
811 #if 0
812 void
813 http_header_set (Http_header **header, const char *name, const char *value)
814 {
815 Http_header *h;
816 size_t n;
817 char *v;
818
819 n = strlen (value);
820 v = malloc (n + 1);
821 if (v == NULL)
822 fail;
823 memcpy (v, value, n + 1);
824
825 h = http_header_find (*header, name);
826 if (h == NULL)
827 {
828 Http_header *h2;
829
830 h2 = malloc (sizeof (Http_header));
831 if (h2 == NULL)
832 fail;
833
834 n = strlen (name);
835 h2->name = malloc (strlen (name) + 1);
836 if (h2->name == NULL)
837 fail;
838 memcpy (h2->name, name, n + 1);
839
840 h2->value = v;
841
842 h2->next = *header;
843
844 *header = h2;
845
846 return NULL;
847 }
848 else
849 {
850 free (h->value);
851 h->value = v;
852 }
853 }
854 #endif
855