1 /*	$Id: http.c,v 1.13 2020/01/11 17:37:19 sthen Exp $ */
2 /*
3  * Copyright (c) 2016 Kristaps Dzonsons <kristaps@bsd.lv>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 
21 #include <arpa/inet.h>
22 #include <netinet/in.h>
23 
24 #include <ctype.h>
25 #include <err.h>
26 #include <limits.h>
27 #include <netdb.h>
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <tls.h>
33 #include <unistd.h>
34 
35 #include "http.h"
36 #include <tls.h>
37 
38 /*
39  * A buffer for transferring HTTP/S data.
40  */
41 struct	httpxfer {
42 	char		*hbuf;    /* header transfer buffer */
43 	size_t		 hbufsz;  /* header buffer size */
44 	int		 headok;  /* header has been parsed */
45 	char		*bbuf;    /* body transfer buffer */
46 	size_t		 bbufsz;  /* body buffer size */
47 	int		 bodyok;  /* body has been parsed */
48 	char		*headbuf; /* lookaside buffer for headers */
49 	struct httphead	*head;    /* parsed headers */
50 	size_t		 headsz;  /* number of headers */
51 };
52 
53 /*
54  * An HTTP/S connection object.
55  */
56 struct	http {
57 	int		   fd;     /* connected socket */
58 	short		   port;   /* port number */
59 	struct source	   src;    /* endpoint (raw) host */
60 	char		  *path;   /* path to request */
61 	char		  *host;   /* name of endpoint host */
62 	struct tls	  *ctx;    /* if TLS */
63 	writefp		   writer; /* write function */
64 	readfp		   reader; /* read function */
65 };
66 
67 struct tls_config *tlscfg;
68 
69 static ssize_t
dosysread(char * buf,size_t sz,const struct http * http)70 dosysread(char *buf, size_t sz, const struct http *http)
71 {
72 	ssize_t	 rc;
73 
74 	rc = read(http->fd, buf, sz);
75 	if (rc == -1)
76 		warn("%s: read", http->src.ip);
77 	return rc;
78 }
79 
80 static ssize_t
dosyswrite(const void * buf,size_t sz,const struct http * http)81 dosyswrite(const void *buf, size_t sz, const struct http *http)
82 {
83 	ssize_t	 rc;
84 
85 	rc = write(http->fd, buf, sz);
86 	if (rc == -1)
87 		warn("%s: write", http->src.ip);
88 	return rc;
89 }
90 
91 static ssize_t
dotlsread(char * buf,size_t sz,const struct http * http)92 dotlsread(char *buf, size_t sz, const struct http *http)
93 {
94 	ssize_t	 rc;
95 
96 	do {
97 		rc = tls_read(http->ctx, buf, sz);
98 	} while (rc == TLS_WANT_POLLIN || rc == TLS_WANT_POLLOUT);
99 
100 	if (rc == -1)
101 		warnx("%s: tls_read: %s", http->src.ip,
102 		    tls_error(http->ctx));
103 	return rc;
104 }
105 
106 static ssize_t
dotlswrite(const void * buf,size_t sz,const struct http * http)107 dotlswrite(const void *buf, size_t sz, const struct http *http)
108 {
109 	ssize_t	 rc;
110 
111 	do {
112 		rc = tls_write(http->ctx, buf, sz);
113 	} while (rc == TLS_WANT_POLLIN || rc == TLS_WANT_POLLOUT);
114 
115 	if (rc == -1)
116 		warnx("%s: tls_write: %s", http->src.ip,
117 		    tls_error(http->ctx));
118 	return rc;
119 }
120 
121 int
http_init()122 http_init()
123 {
124 	if (tlscfg != NULL)
125 		return 0;
126 
127 	if (tls_init() == -1) {
128 		warn("tls_init");
129 		goto err;
130 	}
131 
132 	tlscfg = tls_config_new();
133 	if (tlscfg == NULL) {
134 		warn("tls_config_new");
135 		goto err;
136 	}
137 
138 	if (tls_config_set_ca_file(tlscfg, tls_default_ca_cert_file()) == -1) {
139 		warn("tls_config_set_ca_file: %s", tls_config_error(tlscfg));
140 		goto err;
141 	}
142 
143 	return 0;
144 
145  err:
146 	tls_config_free(tlscfg);
147 	tlscfg = NULL;
148 
149 	return -1;
150 }
151 
152 static ssize_t
http_read(char * buf,size_t sz,const struct http * http)153 http_read(char *buf, size_t sz, const struct http *http)
154 {
155 	ssize_t	 ssz, xfer;
156 
157 	xfer = 0;
158 	do {
159 		if ((ssz = http->reader(buf, sz, http)) < 0)
160 			return -1;
161 		if (ssz == 0)
162 			break;
163 		xfer += ssz;
164 		sz -= ssz;
165 		buf += ssz;
166 	} while (ssz > 0 && sz > 0);
167 
168 	return xfer;
169 }
170 
171 static int
http_write(const char * buf,size_t sz,const struct http * http)172 http_write(const char *buf, size_t sz, const struct http *http)
173 {
174 	ssize_t	 ssz, xfer;
175 
176 	xfer = sz;
177 	while (sz > 0) {
178 		if ((ssz = http->writer(buf, sz, http)) < 0)
179 			return -1;
180 		sz -= ssz;
181 		buf += (size_t)ssz;
182 	}
183 	return xfer;
184 }
185 
186 void
http_disconnect(struct http * http)187 http_disconnect(struct http *http)
188 {
189 	int rc;
190 
191 	if (http->ctx != NULL) {
192 		/* TLS connection. */
193 		do {
194 			rc = tls_close(http->ctx);
195 		} while (rc == TLS_WANT_POLLIN || rc == TLS_WANT_POLLOUT);
196 
197 		if (rc < 0)
198 			warnx("%s: tls_close: %s", http->src.ip,
199 			    tls_error(http->ctx));
200 
201 		tls_free(http->ctx);
202 	}
203 	if (http->fd != -1) {
204 		if (close(http->fd) == -1)
205 			warn("%s: close", http->src.ip);
206 	}
207 
208 	http->fd = -1;
209 	http->ctx = NULL;
210 }
211 
212 void
http_free(struct http * http)213 http_free(struct http *http)
214 {
215 
216 	if (http == NULL)
217 		return;
218 	http_disconnect(http);
219 	free(http->host);
220 	free(http->path);
221 	free(http->src.ip);
222 	free(http);
223 }
224 
225 struct http *
http_alloc(const struct source * addrs,size_t addrsz,const char * host,short port,const char * path)226 http_alloc(const struct source *addrs, size_t addrsz,
227     const char *host, short port, const char *path)
228 {
229 	struct sockaddr_storage ss;
230 	int		 family, fd, c;
231 	socklen_t	 len;
232 	size_t		 cur, i = 0;
233 	struct http	*http;
234 
235 	/* Do this while we still have addresses to connect. */
236 again:
237 	if (i == addrsz)
238 		return NULL;
239 	cur = i++;
240 
241 	/* Convert to PF_INET or PF_INET6 address from string. */
242 
243 	memset(&ss, 0, sizeof(struct sockaddr_storage));
244 
245 	if (addrs[cur].family == 4) {
246 		family = PF_INET;
247 		((struct sockaddr_in *)&ss)->sin_family = AF_INET;
248 		((struct sockaddr_in *)&ss)->sin_port = htons(port);
249 		c = inet_pton(AF_INET, addrs[cur].ip,
250 		    &((struct sockaddr_in *)&ss)->sin_addr);
251 		len = sizeof(struct sockaddr_in);
252 	} else if (addrs[cur].family == 6) {
253 		family = PF_INET6;
254 		((struct sockaddr_in6 *)&ss)->sin6_family = AF_INET6;
255 		((struct sockaddr_in6 *)&ss)->sin6_port = htons(port);
256 		c = inet_pton(AF_INET6, addrs[cur].ip,
257 		    &((struct sockaddr_in6 *)&ss)->sin6_addr);
258 		len = sizeof(struct sockaddr_in6);
259 	} else {
260 		warnx("%s: unknown family", addrs[cur].ip);
261 		goto again;
262 	}
263 
264 	if (c < 0) {
265 		warn("%s: inet_ntop", addrs[cur].ip);
266 		goto again;
267 	} else if (c == 0) {
268 		warnx("%s: inet_ntop", addrs[cur].ip);
269 		goto again;
270 	}
271 
272 	/* Create socket and connect. */
273 
274 	fd = socket(family, SOCK_STREAM, 0);
275 	if (fd == -1) {
276 		warn("%s: socket", addrs[cur].ip);
277 		goto again;
278 	} else if (connect(fd, (struct sockaddr *)&ss, len) == -1) {
279 		warn("%s: connect", addrs[cur].ip);
280 		close(fd);
281 		goto again;
282 	}
283 
284 	/* Allocate the communicator. */
285 
286 	http = calloc(1, sizeof(struct http));
287 	if (http == NULL) {
288 		warn("calloc");
289 		close(fd);
290 		return NULL;
291 	}
292 	http->fd = fd;
293 	http->port = port;
294 	http->src.family = addrs[cur].family;
295 	http->src.ip = strdup(addrs[cur].ip);
296 	http->host = strdup(host);
297 	http->path = strdup(path);
298 	if (http->src.ip == NULL || http->host == NULL || http->path == NULL) {
299 		warn("strdup");
300 		goto err;
301 	}
302 
303 	/* If necessary, do our TLS setup. */
304 
305 	if (port != 443) {
306 		http->writer = dosyswrite;
307 		http->reader = dosysread;
308 		return http;
309 	}
310 
311 	http->writer = dotlswrite;
312 	http->reader = dotlsread;
313 
314 	if ((http->ctx = tls_client()) == NULL) {
315 		warn("tls_client");
316 		goto err;
317 	} else if (tls_configure(http->ctx, tlscfg) == -1) {
318 		warnx("%s: tls_configure: %s",
319 			http->src.ip, tls_error(http->ctx));
320 		goto err;
321 	}
322 
323 	if (tls_connect_socket(http->ctx, http->fd, http->host) != 0) {
324 		warnx("%s: tls_connect_socket: %s, %s", http->src.ip,
325 		    http->host, tls_error(http->ctx));
326 		goto err;
327 	}
328 
329 	return http;
330 err:
331 	http_free(http);
332 	return NULL;
333 }
334 
335 struct httpxfer *
http_open(const struct http * http,const void * p,size_t psz)336 http_open(const struct http *http, const void *p, size_t psz)
337 {
338 	char		*req;
339 	int		 c;
340 	struct httpxfer	*trans;
341 
342 	if (p == NULL) {
343 		c = asprintf(&req,
344 		    "GET %s HTTP/1.0\r\n"
345 		    "Host: %s\r\n"
346 		    "\r\n",
347 		    http->path, http->host);
348 	} else {
349 		c = asprintf(&req,
350 		    "POST %s HTTP/1.0\r\n"
351 		    "Host: %s\r\n"
352 		    "Content-Type: application/ocsp-request\r\n"
353 		    "Content-Length: %zu\r\n"
354 		    "\r\n",
355 		    http->path, http->host, psz);
356 	}
357 	if (c == -1) {
358 		warn("asprintf");
359 		return NULL;
360 	} else if (!http_write(req, c, http)) {
361 		free(req);
362 		return NULL;
363 	} else if (p != NULL && !http_write(p, psz, http)) {
364 		free(req);
365 		return NULL;
366 	}
367 
368 	free(req);
369 
370 	trans = calloc(1, sizeof(struct httpxfer));
371 	if (trans == NULL)
372 		warn("calloc");
373 	return trans;
374 }
375 
376 void
http_close(struct httpxfer * x)377 http_close(struct httpxfer *x)
378 {
379 
380 	if (x == NULL)
381 		return;
382 	free(x->hbuf);
383 	free(x->bbuf);
384 	free(x->headbuf);
385 	free(x->head);
386 	free(x);
387 }
388 
389 /*
390  * Read the HTTP body from the wire.
391  * If invoked multiple times, this will return the same pointer with the
392  * same data (or NULL, if the original invocation returned NULL).
393  * Returns NULL if read or allocation errors occur.
394  * You must not free the returned pointer.
395  */
396 char *
http_body_read(const struct http * http,struct httpxfer * trans,size_t * sz)397 http_body_read(const struct http *http, struct httpxfer *trans, size_t *sz)
398 {
399 	char		 buf[BUFSIZ];
400 	ssize_t		 ssz;
401 	void		*pp;
402 	size_t		 szp;
403 
404 	if (sz == NULL)
405 		sz = &szp;
406 
407 	/* Have we already parsed this? */
408 
409 	if (trans->bodyok > 0) {
410 		*sz = trans->bbufsz;
411 		return trans->bbuf;
412 	} else if (trans->bodyok < 0)
413 		return NULL;
414 
415 	*sz = 0;
416 	trans->bodyok = -1;
417 
418 	do {
419 		/* If less than sizeof(buf), at EOF. */
420 		if ((ssz = http_read(buf, sizeof(buf), http)) < 0)
421 			return NULL;
422 		else if (ssz == 0)
423 			break;
424 
425 		pp = recallocarray(trans->bbuf,
426 		    trans->bbufsz, trans->bbufsz + ssz, 1);
427 		if (pp == NULL) {
428 			warn("recallocarray");
429 			return NULL;
430 		}
431 		trans->bbuf = pp;
432 		memcpy(trans->bbuf + trans->bbufsz, buf, ssz);
433 		trans->bbufsz += ssz;
434 	} while (ssz == sizeof(buf));
435 
436 	trans->bodyok = 1;
437 	*sz = trans->bbufsz;
438 	return trans->bbuf;
439 }
440 
441 struct httphead *
http_head_get(const char * v,struct httphead * h,size_t hsz)442 http_head_get(const char *v, struct httphead *h, size_t hsz)
443 {
444 	size_t	 i;
445 
446 	for (i = 0; i < hsz; i++) {
447 		if (strcmp(h[i].key, v))
448 			continue;
449 		return &h[i];
450 	}
451 	return NULL;
452 }
453 
454 /*
455  * Look through the headers and determine our HTTP code.
456  * This will return -1 on failure, otherwise the code.
457  */
458 int
http_head_status(const struct http * http,struct httphead * h,size_t sz)459 http_head_status(const struct http *http, struct httphead *h, size_t sz)
460 {
461 	int		 rc;
462 	unsigned int	 code;
463 	struct httphead *st;
464 
465 	if ((st = http_head_get("Status", h, sz)) == NULL) {
466 		warnx("%s: no status header", http->src.ip);
467 		return -1;
468 	}
469 
470 	rc = sscanf(st->val, "%*s %u %*s", &code);
471 	if (rc < 0) {
472 		warn("sscanf");
473 		return -1;
474 	} else if (rc != 1) {
475 		warnx("%s: cannot convert status header", http->src.ip);
476 		return -1;
477 	}
478 	return code;
479 }
480 
481 /*
482  * Parse headers from the transfer.
483  * Malformed headers are skipped.
484  * A special "Status" header is added for the HTTP status line.
485  * This can only happen once http_head_read has been called with
486  * success.
487  * This can be invoked multiple times: it will only parse the headers
488  * once and after that it will just return the cache.
489  * You must not free the returned pointer.
490  * If the original header parse failed, or if memory allocation fails
491  * internally, this returns NULL.
492  */
493 struct httphead *
http_head_parse(const struct http * http,struct httpxfer * trans,size_t * sz)494 http_head_parse(const struct http *http, struct httpxfer *trans, size_t *sz)
495 {
496 	size_t		 hsz, szp;
497 	struct httphead	*h;
498 	char		*cp, *ep, *ccp, *buf;
499 
500 	if (sz == NULL)
501 		sz = &szp;
502 
503 	/*
504 	 * If we've already parsed the headers, return the
505 	 * previously-parsed buffer now.
506 	 * If we have errors on the stream, return NULL now.
507 	 */
508 
509 	if (trans->head != NULL) {
510 		*sz = trans->headsz;
511 		return trans->head;
512 	} else if (trans->headok <= 0)
513 		return NULL;
514 
515 	if ((buf = strdup(trans->hbuf)) == NULL) {
516 		warn("strdup");
517 		return NULL;
518 	}
519 	hsz = 0;
520 	cp = buf;
521 
522 	do {
523 		if ((cp = strstr(cp, "\r\n")) != NULL)
524 			cp += 2;
525 		hsz++;
526 	} while (cp != NULL);
527 
528 	/*
529 	 * Allocate headers, then step through the data buffer, parsing
530 	 * out headers as we have them.
531 	 * We know at this point that the buffer is NUL-terminated in
532 	 * the usual way.
533 	 */
534 
535 	h = calloc(hsz, sizeof(struct httphead));
536 	if (h == NULL) {
537 		warn("calloc");
538 		free(buf);
539 		return NULL;
540 	}
541 
542 	*sz = hsz;
543 	hsz = 0;
544 	cp = buf;
545 
546 	do {
547 		if ((ep = strstr(cp, "\r\n")) != NULL) {
548 			*ep = '\0';
549 			ep += 2;
550 		}
551 		if (hsz == 0) {
552 			h[hsz].key = "Status";
553 			h[hsz++].val = cp;
554 			continue;
555 		}
556 
557 		/* Skip bad headers. */
558 		if ((ccp = strchr(cp, ':')) == NULL) {
559 			warnx("%s: header without separator", http->src.ip);
560 			continue;
561 		}
562 
563 		*ccp++ = '\0';
564 		while (isspace((int)*ccp))
565 			ccp++;
566 		h[hsz].key = cp;
567 		h[hsz++].val = ccp;
568 	} while ((cp = ep) != NULL);
569 
570 	trans->headbuf = buf;
571 	trans->head = h;
572 	trans->headsz = hsz;
573 	return h;
574 }
575 
576 /*
577  * Read the HTTP headers from the wire.
578  * If invoked multiple times, this will return the same pointer with the
579  * same data (or NULL, if the original invocation returned NULL).
580  * Returns NULL if read or allocation errors occur.
581  * You must not free the returned pointer.
582  */
583 char *
http_head_read(const struct http * http,struct httpxfer * trans,size_t * sz)584 http_head_read(const struct http *http, struct httpxfer *trans, size_t *sz)
585 {
586 	char		 buf[BUFSIZ];
587 	ssize_t		 ssz;
588 	char		*ep;
589 	void		*pp;
590 	size_t		 szp;
591 
592 	if (sz == NULL)
593 		sz = &szp;
594 
595 	/* Have we already parsed this? */
596 
597 	if (trans->headok > 0) {
598 		*sz = trans->hbufsz;
599 		return trans->hbuf;
600 	} else if (trans->headok < 0)
601 		return NULL;
602 
603 	*sz = 0;
604 	ep = NULL;
605 	trans->headok = -1;
606 
607 	/*
608 	 * Begin by reading by BUFSIZ blocks until we reach the header
609 	 * termination marker (two CRLFs).
610 	 * We might read into our body, but that's ok: we'll copy out
611 	 * the body parts into our body buffer afterward.
612 	 */
613 
614 	do {
615 		/* If less than sizeof(buf), at EOF. */
616 		if ((ssz = http_read(buf, sizeof(buf), http)) < 0)
617 			return NULL;
618 		else if (ssz == 0)
619 			break;
620 		pp = realloc(trans->hbuf, trans->hbufsz + ssz);
621 		if (pp == NULL) {
622 			warn("realloc");
623 			return NULL;
624 		}
625 		trans->hbuf = pp;
626 		memcpy(trans->hbuf + trans->hbufsz, buf, ssz);
627 		trans->hbufsz += ssz;
628 		/* Search for end of headers marker. */
629 		ep = memmem(trans->hbuf, trans->hbufsz, "\r\n\r\n", 4);
630 	} while (ep == NULL && ssz == sizeof(buf));
631 
632 	if (ep == NULL) {
633 		warnx("%s: partial transfer", http->src.ip);
634 		return NULL;
635 	}
636 	*ep = '\0';
637 
638 	/*
639 	 * The header data is invalid if it has any binary characters in
640 	 * it: check that now.
641 	 * This is important because we want to guarantee that all
642 	 * header keys and pairs are properly NUL-terminated.
643 	 */
644 
645 	if (strlen(trans->hbuf) != (uintptr_t)(ep - trans->hbuf)) {
646 		warnx("%s: binary data in header", http->src.ip);
647 		return NULL;
648 	}
649 
650 	/*
651 	 * Copy remaining buffer into body buffer.
652 	 */
653 
654 	ep += 4;
655 	trans->bbufsz = (trans->hbuf + trans->hbufsz) - ep;
656 	trans->bbuf = malloc(trans->bbufsz);
657 	if (trans->bbuf == NULL) {
658 		warn("malloc");
659 		return NULL;
660 	}
661 	memcpy(trans->bbuf, ep, trans->bbufsz);
662 
663 	trans->headok = 1;
664 	*sz = trans->hbufsz;
665 	return trans->hbuf;
666 }
667 
668 void
http_get_free(struct httpget * g)669 http_get_free(struct httpget *g)
670 {
671 
672 	if (g == NULL)
673 		return;
674 	http_close(g->xfer);
675 	http_free(g->http);
676 	free(g);
677 }
678 
679 struct httpget *
http_get(const struct source * addrs,size_t addrsz,const char * domain,short port,const char * path,const void * post,size_t postsz)680 http_get(const struct source *addrs, size_t addrsz, const char *domain,
681     short port, const char *path, const void *post, size_t postsz)
682 {
683 	struct http	*h;
684 	struct httpxfer	*x;
685 	struct httpget	*g;
686 	struct httphead	*head;
687 	size_t		 headsz, bodsz, headrsz;
688 	int		 code;
689 	char		*bod, *headr;
690 
691 	h = http_alloc(addrs, addrsz, domain, port, path);
692 	if (h == NULL)
693 		return NULL;
694 
695 	if ((x = http_open(h, post, postsz)) == NULL) {
696 		http_free(h);
697 		return NULL;
698 	} else if ((headr = http_head_read(h, x, &headrsz)) == NULL) {
699 		http_close(x);
700 		http_free(h);
701 		return NULL;
702 	} else if ((bod = http_body_read(h, x, &bodsz)) == NULL) {
703 		http_close(x);
704 		http_free(h);
705 		return NULL;
706 	}
707 
708 	http_disconnect(h);
709 
710 	if ((head = http_head_parse(h, x, &headsz)) == NULL) {
711 		http_close(x);
712 		http_free(h);
713 		return NULL;
714 	} else if ((code = http_head_status(h, head, headsz)) < 0) {
715 		http_close(x);
716 		http_free(h);
717 		return NULL;
718 	}
719 
720 	if ((g = calloc(1, sizeof(struct httpget))) == NULL) {
721 		warn("calloc");
722 		http_close(x);
723 		http_free(h);
724 		return NULL;
725 	}
726 
727 	g->headpart = headr;
728 	g->headpartsz = headrsz;
729 	g->bodypart = bod;
730 	g->bodypartsz = bodsz;
731 	g->head = head;
732 	g->headsz = headsz;
733 	g->code = code;
734 	g->xfer = x;
735 	g->http = h;
736 	return g;
737 }
738 
739 #if 0
740 int
741 main(void)
742 {
743 	struct httpget	*g;
744 	struct httphead	*httph;
745 	size_t		 i, httphsz;
746 	struct source	 addrs[2];
747 	size_t		 addrsz;
748 
749 #if 0
750 	addrs[0].ip = "127.0.0.1";
751 	addrs[0].family = 4;
752 	addrsz = 1;
753 #else
754 	addrs[0].ip = "2a00:1450:400a:806::2004";
755 	addrs[0].family = 6;
756 	addrs[1].ip = "193.135.3.123";
757 	addrs[1].family = 4;
758 	addrsz = 2;
759 #endif
760 
761 	if (http_init() == -1)
762 		errx(EXIT_FAILURE, "http_init");
763 
764 #if 0
765 	g = http_get(addrs, addrsz, "localhost", 80, "/index.html");
766 #else
767 	g = http_get(addrs, addrsz, "www.google.ch", 80, "/index.html",
768 	    NULL, 0);
769 #endif
770 
771 	if (g == NULL)
772 		errx(EXIT_FAILURE, "http_get");
773 
774 	httph = http_head_parse(g->http, g->xfer, &httphsz);
775 	warnx("code: %d", g->code);
776 
777 	for (i = 0; i < httphsz; i++)
778 		warnx("head: [%s]=[%s]", httph[i].key, httph[i].val);
779 
780 	http_get_free(g);
781 	return (EXIT_SUCCESS);
782 }
783 #endif
784