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