5430f6e04f0fa881034d5bbccc663aed854b1ecf
[pazpar2-moved-to-github.git] / src / http.c
1 /*
2  * $Id: http.c,v 1.4 2006-12-21 04:27:17 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", hc->request->method,
554                     hc->request->path,
555                     *hc->request->search ? "?" : "",
556                     hc->request->search);
557             if (http_weshouldproxy(hc->request))
558                 http_proxy(hc->request);
559             else
560             {
561                 // Execute our business logic!
562                 hc->state = Http_Busy;
563                 http_command(hc);
564             }
565             if (hc->iqueue)
566             {
567                 yaz_log(YLOG_DEBUG, "We think we have more input to read. Forcing event");
568                 iochan_setevent(i, EVENT_INPUT);
569             }
570
571             break;
572
573         case EVENT_OUTPUT:
574             if (hc->oqueue)
575             {
576                 struct http_buf *wb = hc->oqueue;
577                 res = write(iochan_getfd(hc->iochan), wb->buf + wb->offset, wb->len);
578                 if (res <= 0)
579                 {
580                     yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
581                     http_destroy(i);
582                     return;
583                 }
584                 if (res == wb->len)
585                 {
586                     hc->oqueue = hc->oqueue->next;
587                     http_buf_destroy(wb);
588                 }
589                 else
590                 {
591                     wb->len -= res;
592                     wb->offset += res;
593                 }
594                 if (!hc->oqueue) {
595                     if (!strcmp(hc->version, "1.0"))
596                     {
597                         http_destroy(i);
598                         return;
599                     }
600                     else
601                     {
602                         iochan_clearflag(i, EVENT_OUTPUT);
603                         if (hc->iqueue)
604                             iochan_setevent(hc->iochan, EVENT_INPUT);
605                     }
606                 }
607             }
608
609             if (!hc->oqueue && hc->proxy && !hc->proxy->iochan) 
610                 http_destroy(i); // Server closed; we're done
611             break;
612         default:
613             yaz_log(YLOG_WARN, "Unexpected event on connection");
614             http_destroy(i);
615     }
616 }
617
618 // Handles I/O on a client connection to a backend web server (proxy mode)
619 static void proxy_io(IOCHAN pi, int event)
620 {
621     struct http_proxy *pc = iochan_getdata(pi);
622     struct http_channel *hc = pc->channel;
623
624     switch (event)
625     {
626         int res;
627         struct http_buf *htbuf;
628
629         case EVENT_INPUT:
630             htbuf = http_buf_create();
631             res = read(iochan_getfd(pi), htbuf->buf, HTTP_BUF_SIZE -1);
632             if (res == 0 || (res < 0 && errno != EINPROGRESS))
633             {
634                 if (hc->oqueue)
635                 {
636                     yaz_log(YLOG_WARN, "Proxy read came up short");
637                     // Close channel and alert client HTTP channel that we're gone
638                     http_buf_destroy(htbuf);
639                     close(iochan_getfd(pi));
640                     iochan_destroy(pi);
641                     pc->iochan = 0;
642                 }
643                 else
644                 {
645                     http_destroy(hc->iochan);
646                     return;
647                 }
648             }
649             else
650             {
651                 htbuf->buf[res] = '\0';
652                 htbuf->len = res;
653                 http_buf_enqueue(&hc->oqueue, htbuf);
654             }
655             iochan_setflag(hc->iochan, EVENT_OUTPUT);
656             break;
657         case EVENT_OUTPUT:
658             if (!(htbuf = pc->oqueue))
659             {
660                 iochan_clearflag(pi, EVENT_OUTPUT);
661                 return;
662             }
663             res = write(iochan_getfd(pi), htbuf->buf + htbuf->offset, htbuf->len);
664             if (res <= 0)
665             {
666                 yaz_log(YLOG_WARN|YLOG_ERRNO, "write");
667                 http_destroy(hc->iochan);
668                 return;
669             }
670             if (res == htbuf->len)
671             {
672                 struct http_buf *np = htbuf->next;
673                 http_buf_destroy(htbuf);
674                 pc->oqueue = np;
675             }
676             else
677             {
678                 htbuf->len -= res;
679                 htbuf->offset += res;
680             }
681
682             if (!pc->oqueue) {
683                 iochan_setflags(pi, EVENT_INPUT); // Turns off output flag
684             }
685             break;
686         default:
687             yaz_log(YLOG_WARN, "Unexpected event on connection");
688             http_destroy(hc->iochan);
689     }
690 }
691
692 // Cleanup channel
693 static void http_destroy(IOCHAN i)
694 {
695     struct http_channel *s = iochan_getdata(i);
696
697     if (s->proxy)
698     {
699         if (s->proxy->iochan)
700         {
701             close(iochan_getfd(s->proxy->iochan));
702             iochan_destroy(s->proxy->iochan);
703         }
704         http_buf_destroy_queue(s->proxy->oqueue);
705         xfree(s->proxy);
706     }
707     s->next = http_channel_freelist;
708     http_channel_freelist = s;
709     close(iochan_getfd(i));
710     iochan_destroy(i);
711 }
712
713 static struct http_channel *http_create(void)
714 {
715     struct http_channel *r = http_channel_freelist;
716
717     if (r)
718     {
719         http_channel_freelist = r->next;
720         nmem_reset(r->nmem);
721         wrbuf_rewind(r->wrbuf);
722     }
723     else
724     {
725         r = xmalloc(sizeof(struct http_channel));
726         r->nmem = nmem_create();
727         r->wrbuf = wrbuf_alloc();
728     }
729     r->proxy = 0;
730     r->iochan = 0;
731     r->iqueue = r->oqueue = 0;
732     r->state = Http_Idle;
733     r->request = 0;
734     r->response = 0;
735     return r;
736 }
737
738
739 /* Accept a new command connection */
740 static void http_accept(IOCHAN i, int event)
741 {
742     struct sockaddr_in addr;
743     int fd = iochan_getfd(i);
744     socklen_t len;
745     int s;
746     IOCHAN c;
747     int flags;
748     struct http_channel *ch;
749
750     len = sizeof addr;
751     if ((s = accept(fd, (struct sockaddr *) &addr, &len)) < 0)
752     {
753         yaz_log(YLOG_WARN|YLOG_ERRNO, "accept");
754         return;
755     }
756     if ((flags = fcntl(s, F_GETFL, 0)) < 0) 
757         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl");
758     if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0)
759         yaz_log(YLOG_FATAL|YLOG_ERRNO, "fcntl2");
760
761     yaz_log(YLOG_DEBUG, "New command connection");
762     c = iochan_create(s, http_io, EVENT_INPUT | EVENT_EXCEPT);
763
764     ch = http_create();
765     ch->iochan = c;
766     iochan_setdata(c, ch);
767
768     c->next = channel_list;
769     channel_list = c;
770 }
771
772 /* Create a http-channel listener, syntax [host:]port */
773 void http_init(const char *addr)
774 {
775     IOCHAN c;
776     int l;
777     struct protoent *p;
778     struct sockaddr_in myaddr;
779     int one = 1;
780     const char *pp;
781     int port;
782
783     yaz_log(YLOG_LOG, "HTTP listener is %s", addr);
784
785     bzero(&myaddr, sizeof myaddr);
786     myaddr.sin_family = AF_INET;
787     pp = strchr(addr, ':');
788     if (pp)
789     {
790         int len = pp - addr;
791         char hostname[128];
792         struct hostent *he;
793
794         strncpy(hostname, addr, len);
795         hostname[len] = '\0';
796         if (!(he = gethostbyname(hostname)))
797         {
798             yaz_log(YLOG_FATAL, "Unable to resolve '%s'", hostname);
799             exit(1);
800         }
801         memcpy(&myaddr.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
802         port = atoi(pp + 1);
803     }
804     else
805     {
806         port = atoi(addr);
807         myaddr.sin_addr.s_addr = INADDR_ANY;
808     }
809     myaddr.sin_port = htons(port);
810
811     if (!(p = getprotobyname("tcp"))) {
812         abort();
813     }
814     if ((l = socket(PF_INET, SOCK_STREAM, p->p_proto)) < 0)
815         yaz_log(YLOG_FATAL|YLOG_ERRNO, "socket");
816     if (setsockopt(l, SOL_SOCKET, SO_REUSEADDR, (char*)
817                     &one, sizeof(one)) < 0)
818         abort();
819
820     if (bind(l, (struct sockaddr *) &myaddr, sizeof myaddr) < 0) 
821         yaz_log(YLOG_FATAL|YLOG_ERRNO, "bind");
822     if (listen(l, SOMAXCONN) < 0) 
823         yaz_log(YLOG_FATAL|YLOG_ERRNO, "listen");
824
825     c = iochan_create(l, http_accept, EVENT_INPUT | EVENT_EXCEPT);
826     c->next = channel_list;
827     channel_list = c;
828 }
829
830 void http_set_proxyaddr(char *host)
831 {
832     char *p;
833     int port;
834     struct hostent *he;
835
836     strcpy(proxy_url, host);
837     p = strchr(host, ':');
838     yaz_log(YLOG_DEBUG, "Proxying for %s", host);
839     if (p) {
840         port = atoi(p + 1);
841         *p = '\0';
842     }
843     else
844         port = 80;
845     if (!(he = gethostbyname(host))) 
846     {
847         fprintf(stderr, "Failed to lookup '%s'\n", host);
848         exit(1);
849     }
850     proxy_addr = xmalloc(sizeof(struct sockaddr_in));
851     proxy_addr->sin_family = he->h_addrtype;
852     memcpy(&proxy_addr->sin_addr.s_addr, he->h_addr_list[0], he->h_length);
853     proxy_addr->sin_port = htons(port);
854 }
855
856 /*
857  * Local variables:
858  * c-basic-offset: 4
859  * indent-tabs-mode: nil
860  * End:
861  * vim: shiftwidth=4 tabstop=8 expandtab
862  */