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