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