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