More detailed logging of HTTP requests
[pazpar2-moved-to-github.git] / src / http.c
1 /*
2  * $Id: http.c,v 1.3 2006-12-21 04:16:25 quinn Exp $
3  */
4
5 #include <stdio.h>
6 #include <sys/socket.h>
7 #include <sys/types.h>
8 #include <sys/uio.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <strings.h>
12 #include <ctype.h>
13 #include <fcntl.h>
14 #include <netdb.h>
15 #include <errno.h>
16 #include <assert.h>
17
18 #include <yaz/yaz-util.h>
19 #include <yaz/comstack.h>
20 #include <netdb.h>
21
22 #include "command.h"
23 #include "util.h"
24 #include "eventl.h"
25 #include "pazpar2.h"
26 #include "http.h"
27 #include "http_command.h"
28
29 static void proxy_io(IOCHAN i, int event);
30 static struct http_channel *http_create(void);
31 static void http_destroy(IOCHAN i);
32
33 extern IOCHAN channel_list;
34
35 static struct sockaddr_in *proxy_addr = 0; // If this is set, we proxy normal HTTP requests
36 static char proxy_url[256] = "";
37 static struct http_buf *http_buf_freelist = 0;
38 static struct http_channel *http_channel_freelist = 0;
39
40 static struct http_buf *http_buf_create()
41 {
42     struct http_buf *r;
43
44     if (http_buf_freelist)
45     {
46         r = http_buf_freelist;
47         http_buf_freelist = http_buf_freelist->next;
48     }
49     else
50         r = xmalloc(sizeof(struct http_buf));
51     r->offset = 0;
52     r->len = 0;
53     r->next = 0;
54     return r;
55 }
56
57 static void http_buf_destroy(struct http_buf *b)
58 {
59     b->next = http_buf_freelist;
60     http_buf_freelist = b;
61 }
62
63 static void http_buf_destroy_queue(struct http_buf *b)
64 {
65     struct http_buf *p;
66     while (b)
67     {
68         p = b->next;
69         http_buf_destroy(b);
70         b = p;
71     }
72 }
73
74 #ifdef GAGA
75 // Calculate length of chain
76 static int http_buf_len(struct http_buf *b)
77 {
78     int sum = 0;
79     for (; b; b = b->next)
80         sum += b->len;
81     return sum;
82 }
83 #endif
84
85 static struct http_buf *http_buf_bybuf(char *b, int len)
86 {
87     struct http_buf *res = 0;
88     struct http_buf **p = &res;
89
90     while (len)
91     {
92         *p = http_buf_create();
93         int tocopy = len;
94         if (tocopy > HTTP_BUF_SIZE)
95             tocopy = HTTP_BUF_SIZE;
96         memcpy((*p)->buf, b, tocopy);
97         (*p)->len = tocopy;
98         len -= tocopy;
99         b += tocopy;
100         p = &(*p)->next;
101     }
102     return res;
103 }
104
105 // Add a (chain of) buffers to the end of an existing queue.
106 static void http_buf_enqueue(struct http_buf **queue, struct http_buf *b)
107 {
108     while (*queue)
109         queue = &(*queue)->next;
110     *queue = b;
111 }
112
113 static struct http_buf *http_buf_bywrbuf(WRBUF wrbuf)
114 {
115     // Heavens to Betsy (buf)!
116     return http_buf_bybuf(wrbuf_buf(wrbuf), wrbuf_len(wrbuf));
117 }
118
119 // Non-destructively collapse chain of buffers into a string (max *len)
120 // Return
121 static int http_buf_peek(struct http_buf *b, char *buf, int len)
122 {
123     int rd = 0;
124     while (b && rd < len)
125     {
126         int toread = len - rd;
127         if (toread > b->len)
128             toread = b->len;
129         memcpy(buf + rd, b->buf + b->offset, toread);
130         rd += toread;
131         b = b->next;
132     }
133     buf[rd] = '\0';
134     return rd;
135 }
136
137 // Ddestructively munch up to len  from head of queue.
138 static int http_buf_read(struct http_buf **b, char *buf, int len)
139 {
140     int rd = 0;
141     while ((*b) && rd < len)
142     {
143         int toread = len - rd;
144         if (toread > (*b)->len)
145             toread = (*b)->len;
146         memcpy(buf + rd, (*b)->buf + (*b)->offset, toread);
147         rd += toread;
148         if (toread < (*b)->len)
149         {
150             (*b)->len -= toread;
151             (*b)->offset += toread;
152             break;
153         }
154         else
155         {
156             struct http_buf *n = (*b)->next;
157             http_buf_destroy(*b);
158             *b = n;
159         }
160     }
161     buf[rd] = '\0';
162     return rd;
163 }
164
165 void static urldecode(char *i, char *o)
166 {
167     while (*i)
168     {
169         if (*i == '+')
170         {
171             *(o++) = ' ';
172             i++;
173         }
174         else if (*i == '%')
175         {
176             i++;
177             sscanf(i, "%2hhx", o);
178             i += 2;
179             o++;
180         }
181         else
182             *(o++) = *(i++);
183     }
184     *o = '\0';
185 }
186
187 void http_addheader(struct http_response *r, const char *name, const char *value)
188 {
189     struct http_channel *c = r->channel;
190     struct http_header *h = nmem_malloc(c->nmem, sizeof *h);
191     h->name = nmem_strdup(c->nmem, name);
192     h->value = nmem_strdup(c->nmem, value);
193     h->next = r->headers;
194     r->headers = h;
195 }
196
197 char *http_argbyname(struct http_request *r, char *name)
198 {
199     struct http_argument *p;
200     if (!name)
201         return 0;
202     for (p = r->arguments; p; p = p->next)
203         if (!strcmp(p->name, name))
204             return p->value;
205     return 0;
206 }
207
208 char *http_headerbyname(struct http_request *r, char *name)
209 {
210     struct http_header *p;
211     for (p = r->headers; p; p = p->next)
212         if (!strcmp(p->name, name))
213             return p->value;
214     return 0;
215 }
216
217 struct http_response *http_create_response(struct http_channel *c)
218 {
219     struct http_response *r = nmem_malloc(c->nmem, sizeof(*r));
220     strcpy(r->code, "200");
221     r->msg = "OK";
222     r->channel = c;
223     r->headers = 0;
224     r->payload = 0;
225     return r;
226 }
227
228 // Check if we have a complete request. Return 0 or length (including trailing newline)
229 // FIXME: Does not deal gracefully with requests carrying payload
230 // but this is kind of OK since we will reject anything other than an empty GET
231 static int request_check(struct http_buf *queue)
232 {
233     char tmp[4096];
234     int len = 0;
235     char *buf = tmp;
236
237     http_buf_peek(queue, tmp, 4096);
238     while (*buf) // Check if we have a sequence of lines terminated by an empty line
239     {
240         char *b = strstr(buf, "\r\n");
241
242         if (!b)
243             return 0;
244
245         len += (b - buf) + 2;
246         if (b == buf)
247             return len;
248         buf = b + 2;
249     }
250     return 0;
251 }
252
253 struct http_request *http_parse_request(struct http_channel *c, struct http_buf **queue,
254         int len)
255 {
256     struct http_request *r = nmem_malloc(c->nmem, sizeof(*r));
257     char *p, *p2;
258     char tmp[4096];
259     char *buf = tmp;
260
261     if (len > 4096)
262         return 0;
263     if (http_buf_read(queue, buf, len) < len)
264         return 0;
265
266     r->search = "";
267     r->channel = c;
268     r->arguments = 0;
269     r->headers = 0;
270     // Parse first line
271     for (p = buf, p2 = r->method; *p && *p != ' ' && p - buf < 19; p++)
272         *(p2++) = *p;
273     if (*p != ' ')
274     {
275         yaz_log(YLOG_WARN, "Unexpected HTTP method in request");
276         return 0;
277     }
278     *p2 = '\0';
279
280     if (!(buf = strchr(buf, ' ')))
281     {
282         yaz_log(YLOG_WARN, "Syntax error in request (1)");
283         return 0;
284     }
285     buf++;
286     if (!(p = strchr(buf, ' ')))
287     {
288         yaz_log(YLOG_WARN, "Syntax error in request (2)");
289         return 0;
290     }
291     *(p++) = '\0';
292     if ((p2 = strchr(buf, '?'))) // Do we have arguments?
293         *(p2++) = '\0';
294     r->path = nmem_strdup(c->nmem, buf);
295     if (p2)
296     {
297         r->search = nmem_strdup(c->nmem, p2);
298         // Parse Arguments
299         while (*p2)
300         {
301             struct http_argument *a;
302             char *equal = strchr(p2, '=');
303             char *eoa = strchr(p2, '&');
304             if (!equal)
305             {
306                 yaz_log(YLOG_WARN, "Expected '=' in argument");
307                 return 0;
308             }
309             if (!eoa)
310                 eoa = equal + strlen(equal); // last argument
311             else
312                 *(eoa++) = '\0';
313             a = nmem_malloc(c->nmem, sizeof(struct http_argument));
314             *(equal++) = '\0';
315             a->name = nmem_strdup(c->nmem, p2);
316             urldecode(equal, equal);
317             a->value = nmem_strdup(c->nmem, equal);
318             a->next = r->arguments;
319             r->arguments = a;
320             p2 = eoa;
321         }
322     }
323     buf = p;
324
325     if (strncmp(buf, "HTTP/", 5))
326         strcpy(r->http_version, "1.0");
327     else
328     {
329         buf += 5;
330         if (!(p = strstr(buf, "\r\n")))
331             return 0;
332         *(p++) = '\0';
333         p++;
334         strcpy(r->http_version, buf);
335         buf = p;
336     }
337     strcpy(c->version, r->http_version);
338
339     r->headers = 0;
340     while (*buf)
341     {
342         if (!(p = strstr(buf, "\r\n")))
343             return 0;
344         if (p == buf)
345             break;
346         else
347         {
348             struct http_header *h = nmem_malloc(c->nmem, sizeof(*h));
349             if (!(p2 = strchr(buf, ':')))
350                 return 0;
351             *(p2++) = '\0';
352             h->name = nmem_strdup(c->nmem, buf);
353             while (isspace(*p2))
354                 p2++;
355             if (p2 >= p) // Empty header?
356             {
357                 buf = p + 2;
358                 continue;
359             }
360             *p = '\0';
361             h->value = nmem_strdup(c->nmem, p2);
362             h->next = r->headers;
363             r->headers = h;
364             buf = p + 2;
365         }
366     }
367
368     return r;
369 }
370
371
372 static struct http_buf *http_serialize_response(struct http_channel *c,
373         struct http_response *r)
374 {
375     wrbuf_rewind(c->wrbuf);
376     struct http_header *h;
377
378     wrbuf_printf(c->wrbuf, "HTTP/1.1 %s %s\r\n", r->code, r->msg);
379     for (h = r->headers; h; h = h->next)
380         wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
381     wrbuf_printf(c->wrbuf, "Content-length: %d\r\n", r->payload ? (int) strlen(r->payload) : 0);
382     wrbuf_printf(c->wrbuf, "Content-type: text/xml\r\n");
383     wrbuf_puts(c->wrbuf, "\r\n");
384
385     if (r->payload)
386         wrbuf_puts(c->wrbuf, r->payload);
387
388     return http_buf_bywrbuf(c->wrbuf);
389 }
390
391 // Serialize a HTTP request
392 static struct http_buf *http_serialize_request(struct http_request *r)
393 {
394     struct http_channel *c = r->channel;
395     wrbuf_rewind(c->wrbuf);
396     struct http_header *h;
397     struct http_argument *a;
398
399     wrbuf_printf(c->wrbuf, "%s %s", r->method, r->path);
400
401     if (r->arguments)
402     {
403         wrbuf_putc(c->wrbuf, '?');
404         for (a = r->arguments; a; a = a->next) {
405             if (a != r->arguments)
406                 wrbuf_putc(c->wrbuf, '&');
407             wrbuf_printf(c->wrbuf, "%s=%s", a->name, a->value);
408         }
409     }
410
411     wrbuf_printf(c->wrbuf, " HTTP/%s\r\n", r->http_version);
412
413     for (h = r->headers; h; h = h->next)
414         wrbuf_printf(c->wrbuf, "%s: %s\r\n", h->name, h->value);
415
416     wrbuf_puts(c->wrbuf, "\r\n");
417     
418     return http_buf_bywrbuf(c->wrbuf);
419 }
420
421
422 static int http_weshouldproxy(struct http_request *rq)
423 {
424     if (proxy_addr && !strstr(rq->path, "search.pz2"))
425         return 1;
426     return 0;
427 }
428
429 static int http_proxy(struct http_request *rq)
430 {
431     struct http_channel *c = rq->channel;
432     struct http_proxy *p = c->proxy;
433     struct http_header *hp;
434     struct http_buf *requestbuf;
435
436     if (!p) // This is a new connection. Create a proxy channel
437     {
438         int sock;
439         struct protoent *pe;
440         int one = 1;
441         int flags;
442
443         if (!(pe = getprotobyname("tcp"))) {
444             abort();
445         }
446         if ((sock = socket(PF_INET, SOCK_STREAM, pe->p_proto)) < 0)
447         {
448             yaz_log(YLOG_WARN|YLOG_ERRNO, "socket");
449             return -1;
450         }
451         if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)
452                         &one, sizeof(one)) < 0)
453             abort();
454         if ((flags = fcntl(sock, F_GETFL, 0)) < 0) 
455             yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
456         if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
457             yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
458         if (connect(sock, (struct sockaddr *) proxy_addr, sizeof(*proxy_addr)) < 0)
459             if (errno != EINPROGRESS)
460             {
461                 yaz_log(YLOG_WARN|YLOG_ERRNO, "Proxy connect");
462                 return -1;
463             }
464
465         p = xmalloc(sizeof(struct http_proxy));
466         p->oqueue = 0;
467         p->channel = c;
468         c->proxy = p;
469         // We will add EVENT_OUTPUT below
470         p->iochan = iochan_create(sock, proxy_io, EVENT_INPUT);
471         iochan_setdata(p->iochan, p);
472         p->iochan->next = channel_list;
473         channel_list = p->iochan;
474     }
475
476     // Modify Host: header
477     for (hp = rq->headers; hp; hp = hp->next)
478         if (!strcmp(hp->name, "Host"))
479             break;
480     if (!hp)
481     {
482         yaz_log(YLOG_WARN, "Failed to find Host header in proxy");
483         return -1;
484     }
485     hp->value = nmem_strdup(c->nmem, proxy_url);
486     requestbuf = http_serialize_request(rq);
487     http_buf_enqueue(&p->oqueue, requestbuf);
488     iochan_setflag(p->iochan, EVENT_OUTPUT);
489     return 0;
490 }
491
492 void http_send_response(struct http_channel *ch)
493 {
494     struct http_response *rs = ch->response;
495     assert(rs);
496     struct http_buf *hb = http_serialize_response(ch, rs);
497     if (!hb)
498     {
499         yaz_log(YLOG_WARN, "Failed to serialize HTTP response");
500         http_destroy(ch->iochan);
501     }
502     else
503     {
504         http_buf_enqueue(&ch->oqueue, hb);
505         iochan_setflag(ch->iochan, EVENT_OUTPUT);
506         ch->state = Http_Idle;
507     }
508 }
509
510 static void http_io(IOCHAN i, int event)
511 {
512     struct http_channel *hc = iochan_getdata(i);
513
514     switch (event)
515     {
516         int res, reqlen;
517         struct http_buf *htbuf;
518
519         case EVENT_INPUT:
520             htbuf = http_buf_create();
521             res = read(iochan_getfd(i), htbuf->buf, HTTP_BUF_SIZE -1);
522             if (res == -1 && errno == EAGAIN)
523             {
524                 http_buf_destroy(htbuf);
525                 return;
526             }
527             if (res <= 0)
528             {
529                 http_buf_destroy(htbuf);
530                 http_destroy(i);
531                 return;
532             }
533             if (res > 0)
534             {
535                 htbuf->buf[res] = '\0';
536                 htbuf->len = res;
537                 http_buf_enqueue(&hc->iqueue, htbuf);
538             }
539
540             if (hc->state == Http_Busy)
541                 return;
542             if ((reqlen = request_check(hc->iqueue)) <= 2)
543                 return;
544
545             nmem_reset(hc->nmem);
546             if (!(hc->request = http_parse_request(hc, &hc->iqueue, reqlen)))
547             {
548                 yaz_log(YLOG_WARN, "Failed to parse request");
549                 http_destroy(i);
550                 return;
551             }
552             hc->response = 0;
553             yaz_log(YLOG_LOG, "Request: %s %s%s%s v %s", hc->request->method,
554                     hc->request->path,
555                     *hc->request->search ? "?" : "",
556                     hc->request->search,
557                     hc->request->http_version);
558             if (http_weshouldproxy(hc->request))
559                 http_proxy(hc->request);
560             else
561             {
562                 // Execute our business logic!
563                 hc->state = Http_Busy;
564                 http_command(hc);
565             }
566             if (hc->iqueue)
567             {
568                 yaz_log(YLOG_DEBUG, "We think we have more input to read. Forcing event");
569                 iochan_setevent(i, EVENT_INPUT);
570             }
571
572             break;
573
574         case EVENT_OUTPUT:
575             if (hc->oqueue)
576             {
577                 struct http_buf *wb = hc->oqueue;
578                 res = write(iochan_getfd(hc->iochan), wb->buf + wb->offset, wb->len);
579                 if (res <= 0)
580                 {
581                     yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
582                     http_destroy(i);
583                     return;
584                 }
585                 if (res == wb->len)
586                 {
587                     hc->oqueue = hc->oqueue->next;
588                     http_buf_destroy(wb);
589                 }
590                 else
591                 {
592                     wb->len -= res;
593                     wb->offset += res;
594                 }
595                 if (!hc->oqueue) {
596                     if (!strcmp(hc->version, "1.0"))
597                     {
598                         http_destroy(i);
599                         return;
600                     }
601                     else
602                     {
603                         iochan_clearflag(i, EVENT_OUTPUT);
604                         if (hc->iqueue)
605                             iochan_setevent(hc->iochan, EVENT_INPUT);
606                     }
607                 }
608             }
609
610             if (!hc->oqueue && hc->proxy && !hc->proxy->iochan) 
611                 http_destroy(i); // Server closed; we're done
612             break;
613         default:
614             yaz_log(YLOG_WARN, "Unexpected event on connection");
615             http_destroy(i);
616     }
617 }
618
619 // Handles I/O on a client connection to a backend web server (proxy mode)
620 static void proxy_io(IOCHAN pi, int event)
621 {
622     struct http_proxy *pc = iochan_getdata(pi);
623     struct http_channel *hc = pc->channel;
624
625     switch (event)
626     {
627         int res;
628         struct http_buf *htbuf;
629
630         case EVENT_INPUT:
631             htbuf = http_buf_create();
632             res = read(iochan_getfd(pi), htbuf->buf, HTTP_BUF_SIZE -1);
633             if (res == 0 || (res < 0 && errno != EINPROGRESS))
634             {
635                 if (hc->oqueue)
636                 {
637                     yaz_log(YLOG_WARN, "Proxy read came up short");
638                     // Close channel and alert client HTTP channel that we're gone
639                     http_buf_destroy(htbuf);
640                     close(iochan_getfd(pi));
641                     iochan_destroy(pi);
642                     pc->iochan = 0;
643                 }
644                 else
645                 {
646                     http_destroy(hc->iochan);
647                     return;
648                 }
649             }
650             else
651             {
652                 htbuf->buf[res] = '\0';
653                 htbuf->len = res;
654                 http_buf_enqueue(&hc->oqueue, htbuf);
655             }
656             iochan_setflag(hc->iochan, EVENT_OUTPUT);
657             break;
658         case EVENT_OUTPUT:
659             if (!(htbuf = pc->oqueue))
660             {
661                 iochan_clearflag(pi, EVENT_OUTPUT);
662                 return;
663             }
664             res = write(iochan_getfd(pi), htbuf->buf + htbuf->offset, htbuf->len);
665             if (res <= 0)
666             {
667                 yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
668                 http_destroy(hc->iochan);
669                 return;
670             }
671             if (res == htbuf->len)
672             {
673                 struct http_buf *np = htbuf->next;
674                 http_buf_destroy(htbuf);
675                 pc->oqueue = np;
676             }
677             else
678             {
679                 htbuf->len -= res;
680                 htbuf->offset += res;
681             }
682
683             if (!pc->oqueue) {
684                 iochan_setflags(pi, EVENT_INPUT); // Turns off output flag
685             }
686             break;
687         default:
688             yaz_log(YLOG_WARN, "Unexpected event on connection");
689             http_destroy(hc->iochan);
690     }
691 }
692
693 // Cleanup channel
694 static void http_destroy(IOCHAN i)
695 {
696     struct http_channel *s = iochan_getdata(i);
697
698     if (s->proxy)
699     {
700         if (s->proxy->iochan)
701         {
702             close(iochan_getfd(s->proxy->iochan));
703             iochan_destroy(s->proxy->iochan);
704         }
705         http_buf_destroy_queue(s->proxy->oqueue);
706         xfree(s->proxy);
707     }
708     s->next = http_channel_freelist;
709     http_channel_freelist = s;
710     close(iochan_getfd(i));
711     iochan_destroy(i);
712 }
713
714 static struct http_channel *http_create(void)
715 {
716     struct http_channel *r = http_channel_freelist;
717
718     if (r)
719     {
720         http_channel_freelist = r->next;
721         nmem_reset(r->nmem);
722         wrbuf_rewind(r->wrbuf);
723     }
724     else
725     {
726         r = xmalloc(sizeof(struct http_channel));
727         r->nmem = nmem_create();
728         r->wrbuf = wrbuf_alloc();
729     }
730     r->proxy = 0;
731     r->iochan = 0;
732     r->iqueue = r->oqueue = 0;
733     r->state = Http_Idle;
734     r->request = 0;
735     r->response = 0;
736     return r;
737 }
738
739
740 /* Accept a new command connection */
741 static void http_accept(IOCHAN i, int event)
742 {
743     struct sockaddr_in addr;
744     int fd = iochan_getfd(i);
745     socklen_t len;
746     int s;
747     IOCHAN c;
748     int flags;
749     struct http_channel *ch;
750
751     len = sizeof addr;
752     if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0)
753     {
754         yaz_log(YLOG_WARN|YLOG_ERRNO, "accept");
755         return;
756     }
757     if ((flags = fcntl(s, F_GETFL, 0)) < 0) 
758         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
759     if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
760         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
761
762     yaz_log(YLOG_LOG, "New command connection");
763     c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT);
764
765     ch = http_create();
766     ch->iochan = c;
767     iochan_setdata(c, ch);
768
769     c->next = channel_list;
770     channel_list = c;
771 }
772
773 /* Create a http-channel listener, syntax [host:]port */
774 void http_init(const char *addr)
775 {
776     IOCHAN c;
777     int l;
778     struct protoent *p;
779     struct sockaddr_in myaddr;
780     int one = 1;
781     const char *pp;
782     int port;
783
784     yaz_log(YLOG_LOG, "HTTP listener is %s", addr);
785
786     bzero(&myaddr, sizeof myaddr);
787     myaddr.sin_family = AF_INET;
788     pp = strchr(addr, ':');
789     if (pp)
790     {
791         int len = pp - addr;
792         char hostname[128];
793         struct hostent *he;
794
795         strncpy(hostname, addr, len);
796         hostname[len] = '\0';
797         if (!(he = gethostbyname(hostname)))
798         {
799             yaz_log(YLOG_FATAL, "Unable to resolve '%s'", hostname);
800             exit(1);
801         }
802         memcpy(&myaddr.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
803         port = atoi(pp + 1);
804     }
805     else
806     {
807         port = atoi(addr);
808         myaddr.sin_addr.s_addr = INADDR_ANY;
809     }
810     myaddr.sin_port = htons(port);
811
812     if (!(p = getprotobyname("tcp"))) {
813         abort();
814     }
815     if ((l = socket(PF_INET, SOCK_STREAM, p->p_proto)) < 0)
816         yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket");
817     if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (char*)
818                     &one, sizeof(one)) < 0)
819         abort();
820
821     if (bind(l, (struct sockaddr *) &myaddr, sizeof myaddr) < 0) 
822         yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind");
823     if (listen(l, SOMAXCONN) < 0) 
824         yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen");
825
826     c = iochan_create(l, http_accept, EVENT_INPUT | EVENT_EXCEPT);
827     c->next = channel_list;
828     channel_list = c;
829 }
830
831 void http_set_proxyaddr(char *host)
832 {
833     char *p;
834     int port;
835     struct hostent *he;
836
837     strcpy(proxy_url, host);
838     p = strchr(host, ':');
839     yaz_log(YLOG_DEBUG, "Proxying for %s", host);
840     if (p) {
841         port = atoi(p + 1);
842         *p = '\0';
843     }
844     else
845         port = 80;
846     if (!(he = gethostbyname(host))) 
847     {
848         fprintf(stderr, "Failed to lookup '%s'\n", host);
849         exit(1);
850     }
851     proxy_addr = xmalloc(sizeof(struct sockaddr_in));
852     proxy_addr->sin_family = he->h_addrtype;
853     memcpy(&proxy_addr->sin_addr.s_addr, he->h_addr_list[0], he->h_length);
854     proxy_addr->sin_port = htons(port);
855 }
856
857 /*
858  * Local variables:
859  * c-basic-offset: 4
860  * indent-tabs-mode: nil
861  * End:
862  * vim: shiftwidth=4 tabstop=8 expandtab
863  */