Retain cs_create_host_proxy and use blank for optional bind host
[yaz-moved-to-github.git] / src / comstack.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file comstack.c
7  * \brief Implements Generic COMSTACK functions
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <string.h>
14 #include <errno.h>
15
16 #include <yaz/yaz-iconv.h>
17 #include <yaz/log.h>
18 #include <yaz/comstack.h>
19 #include <yaz/tcpip.h>
20 #include <yaz/unix.h>
21 #include <yaz/odr.h>
22 #include <yaz/matchstr.h>
23
24 static const char *cs_errlist[] =
25 {
26     "No error or unspecified error",
27     "System (lower-layer) error",
28     "Operation out of state",
29     "No data (operation would block)",
30     "New data while half of old buffer is on the line (flow control)",
31     "Permission denied",
32     "SSL error",
33     "Too large incoming buffer"
34 };
35
36 const char *cs_errmsg(int n)
37 {
38     static char buf[250];
39
40     if (n < CSNONE || n > CSLASTERROR) {
41         sprintf(buf, "unknown comstack error %d", n);
42         return buf;
43     }
44     if (n == CSYSERR) {
45         sprintf(buf, "%s: %s", cs_errlist[n], strerror(errno));
46         return buf;
47     }
48     return cs_errlist[n];
49 }
50
51 const char *cs_strerror(COMSTACK h)
52 {
53     return cs_errmsg(h->cerrno);
54 }
55
56 void cs_get_host_args(const char *type_and_host, const char **args)
57 {
58
59     *args = "";
60     if (!strncmp(type_and_host, "unix:", 5))
61     {
62         const char *cp = strchr(type_and_host + 5, ':');
63         if (cp)
64             type_and_host = cp + 1;
65         else
66             type_and_host += strlen(type_and_host); /* empty string */
67     }
68     if (*type_and_host)
69     {
70         const char *cp;
71         cp = strstr(type_and_host, "://");
72         if (cp)
73             cp = cp+3;
74         else
75             cp = type_and_host;
76         cp = strchr(cp, '/');
77         if (cp)
78             *args = cp+1;
79     }
80 }
81
82 int cs_parse_host(const char *uri, const char **host,
83                   CS_TYPE *t, enum oid_proto *proto,
84                   char **connect_host)
85 {
86     *connect_host = 0;
87
88     *t = tcpip_type;
89     if (strncmp(uri, "connect:", 8) == 0)
90     {
91         const char *cp = strchr(uri, ',');
92         if (cp)
93         {
94             size_t len;
95
96             uri += 8;
97             len = cp - uri;
98             *connect_host = (char *) xmalloc(len + 1);
99             memcpy(*connect_host, uri, len);
100             (*connect_host)[len] = '\0';
101             uri = cp + 1;
102         }
103     }
104     else if (strncmp(uri, "unix:", 5) == 0)
105     {
106         const char *cp;
107
108         uri += 5;
109         cp = strchr(uri, ':');
110         if (cp)
111         {
112             size_t len = cp - uri;
113             *connect_host = (char *) xmalloc(len + 1);
114             memcpy(*connect_host, uri, len);
115             (*connect_host)[len] = '\0';
116             uri = cp + 1;
117         }
118 #ifdef WIN32
119         xfree(*connect_host);
120         *connect_host = 0;
121         return 0;
122 #else
123         *t = unix_type;
124 #endif
125     }
126
127     if (strncmp (uri, "tcp:", 4) == 0)
128     {
129         *host = uri + 4;
130         *proto = PROTO_Z3950;
131     }
132     else if (strncmp (uri, "ssl:", 4) == 0)
133     {
134 #if HAVE_GNUTLS_H
135         *t = ssl_type;
136         *host = uri + 4;
137         *proto = PROTO_Z3950;
138 #else
139         xfree(*connect_host);
140         *connect_host = 0;
141         return 0;
142 #endif
143     }
144     else if (strncmp(uri, "http:", 5) == 0)
145     {
146         *host = uri + 5;
147         while (**host == '/')
148             (*host)++;
149         *proto = PROTO_HTTP;
150     }
151     else if (strncmp(uri, "https:", 6) == 0)
152     {
153 #if HAVE_GNUTLS_H
154         *t = ssl_type;
155         *host = uri + 6;
156         while (**host == '/')
157             (*host)++;
158         *proto = PROTO_HTTP;
159 #else
160         xfree(*connect_host);
161         *connect_host = 0;
162         return 0;
163 #endif
164     }
165     else
166     {
167         *host = uri;
168         *proto = PROTO_Z3950;
169     }
170     return 1;
171 }
172
173 COMSTACK cs_create_host(const char *vhost, int blocking, void **vp)
174 {
175     return cs_create_host_proxy(vhost, blocking, vp, 0);
176 }
177
178 COMSTACK cs_create_host_proxy(const char *vhost, int blocking, void **vp,
179                               const char *proxy_host)
180 {
181     enum oid_proto proto = PROTO_Z3950;
182     const char *host = 0;
183     COMSTACK cs;
184     CS_TYPE t;
185     char *connect_host = 0;
186     const char *bind_host = strchr(vhost, ' ');
187
188     if (bind_host && bind_host[1])
189         bind_host++;
190     else
191         bind_host = 0;
192
193     if (!cs_parse_host(vhost, &host, &t, &proto, &connect_host))
194         return 0;
195
196     if (proxy_host)
197     {
198         enum oid_proto proto1;
199
200         xfree(connect_host);
201         if (!cs_parse_host(proxy_host, &host, &t, &proto1, &connect_host))
202             return 0;
203     }
204
205     if (t == tcpip_type)
206     {
207         cs = yaz_tcpip_create2(-1, blocking, proto, connect_host ? host : 0,
208                                bind_host);
209     }
210     else
211     {
212         cs = cs_create(t, blocking, proto);
213     }
214     if (cs)
215     {
216         if (!(*vp = cs_straddr(cs, connect_host ? connect_host : host)))
217         {
218             cs_close (cs);
219             cs = 0;
220         }
221     }
222     xfree(connect_host);
223     return cs;
224 }
225
226 int cs_look (COMSTACK cs)
227 {
228     return cs->event;
229 }
230
231 static int skip_crlf(const char *buf, int len, int *i)
232 {
233     if (*i < len)
234     {
235         if (buf[*i] == '\r' && *i < len-1 && buf[*i + 1] == '\n')
236         {
237             (*i) += 2;
238             return 1;
239         }
240         else if (buf[*i] == '\n')
241         {
242             (*i)++;
243             return 1;
244         }
245     }
246     return 0;
247 }
248
249 #define CHUNK_DEBUG 0
250
251 static int cs_read_chunk(const char *buf, int i, int len)
252 {
253     /* inside chunked body .. */
254     while (1)
255     {
256         int chunk_len = 0;
257 #if CHUNK_DEBUG
258         if (i < len-2)
259         {
260             int j;
261             printf ("\n<<<");
262             for (j = i; j <= i+3; j++)
263                 printf ("%c", buf[j]);
264             printf (">>>\n");
265         }
266 #endif
267         /* read chunk length */
268         while (1)
269             if (i >= len-2) {
270 #if CHUNK_DEBUG
271                 printf ("returning incomplete read at 1\n");
272                 printf ("i=%d len=%d\n", i, len);
273 #endif
274                 return 0;
275             } else if (yaz_isdigit(buf[i]))
276                 chunk_len = chunk_len * 16 +
277                     (buf[i++] - '0');
278             else if (yaz_isupper(buf[i]))
279                 chunk_len = chunk_len * 16 +
280                     (buf[i++] - ('A'-10));
281             else if (yaz_islower(buf[i]))
282                 chunk_len = chunk_len * 16 +
283                     (buf[i++] - ('a'-10));
284             else
285                 break;
286         if (chunk_len == 0)
287             break;
288         if (chunk_len < 0)
289             return i;
290
291         while (1)
292         {
293             if (i >= len -1)
294                 return 0;
295             if (skip_crlf(buf, len, &i))
296                 break;
297             i++;
298         }
299         /* got CRLF */
300 #if CHUNK_DEBUG
301         printf ("chunk_len=%d\n", chunk_len);
302 #endif
303         i += chunk_len;
304         if (i >= len-2)
305             return 0;
306         if (!skip_crlf(buf, len, &i))
307             return 0;
308     }
309     /* consider trailing headers .. */
310     while (i < len)
311     {
312         if (skip_crlf(buf, len, &i))
313         {
314             if (skip_crlf(buf, len, &i))
315                 return i;
316         }
317         else
318             i++;
319     }
320 #if CHUNK_DEBUG
321     printf ("returning incomplete read at 2\n");
322     printf ("i=%d len=%d\n", i, len);
323 #endif
324     return 0;
325 }
326
327 static int cs_complete_http(const char *buf, int len, int head_only)
328 {
329     /* deal with HTTP request/response */
330     int i, content_len = 0, chunked = 0;
331
332     /* need at least one line followed by \n or \r .. */
333     for (i = 0; ; i++)
334         if (i == len)
335             return 0; /* incomplete */
336         else if (buf[i] == '\n' || buf[i] == '\r')
337             break;
338
339     /* check to see if it's a response with content */
340     if (!head_only && !memcmp(buf, "HTTP/", 5))
341     {
342         int j;
343         for (j = 5; j < i; j++)
344             if (buf[j] == ' ')
345             {
346                 ++j;
347                 if (buf[j] == '1') /* 1XX */
348                     ;
349                 else if (!memcmp(buf + j, "204", 3))
350                     ;
351                 else if (!memcmp(buf + j, "304", 3))
352                     ;
353                 else
354                     content_len = -1;
355                 break;
356             }
357     }
358 #if 0
359     printf("len = %d\n", len);
360     fwrite (buf, 1, len, stdout);
361     printf("----------\n");
362 #endif
363     for (i = 2; i <= len-2; )
364     {
365         if (i > 8192)
366         {
367             return i;  /* do not allow more than 8K HTTP header */
368         }
369         if (skip_crlf(buf, len, &i))
370         {
371             if (skip_crlf(buf, len, &i))
372             {
373                 /* inside content */
374                 if (chunked)
375                     return cs_read_chunk(buf, i, len);
376                 else
377                 {   /* not chunked ; inside body */
378                     if (content_len == -1)
379                         return 0;   /* no content length */
380                     else if (len >= i + content_len)
381                     {
382                         return i + content_len;
383                     }
384                 }
385                 break;
386             }
387             else if (i < len - 20 &&
388                      !yaz_strncasecmp((const char *) buf+i,
389                                       "Transfer-Encoding:", 18))
390             {
391                 i+=18;
392                 while (buf[i] == ' ')
393                     i++;
394                 if (i < len - 8)
395                     if (!yaz_strncasecmp((const char *) buf+i, "chunked", 7))
396                         chunked = 1;
397             }
398             else if (i < len - 17 &&
399                      !yaz_strncasecmp((const char *)buf+i,
400                                       "Content-Length:", 15))
401             {
402                 i+= 15;
403                 while (buf[i] == ' ')
404                     i++;
405                 content_len = 0;
406                 while (i <= len-4 && yaz_isdigit(buf[i]))
407                     content_len = content_len*10 + (buf[i++] - '0');
408                 if (content_len < 0) /* prevent negative offsets */
409                     content_len = 0;
410             }
411             else
412                 i++;
413         }
414         else
415             i++;
416     }
417     return 0;
418 }
419
420 static int cs_complete_auto_x(const char *buf, int len, int head_only)
421 {
422     if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
423                 && buf[1] >= 0x20 && buf[1] < 0x7f
424                 && buf[2] >= 0x20 && buf[2] < 0x7f)
425     {
426         int r = cs_complete_http(buf, len, head_only);
427         return r;
428     }
429     return completeBER(buf, len);
430 }
431
432
433 int cs_complete_auto(const char *buf, int len)
434 {
435     return cs_complete_auto_x(buf, len, 0);
436 }
437
438 int cs_complete_auto_head(const char *buf, int len)
439 {
440     return cs_complete_auto_x(buf, len, 1);
441 }
442
443 void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
444 {
445     cs->max_recv_bytes = max_recv_bytes;
446 }
447
448 /*
449  * Local variables:
450  * c-basic-offset: 4
451  * c-file-style: "Stroustrup"
452  * indent-tabs-mode: nil
453  * End:
454  * vim: shiftwidth=4 tabstop=8 expandtab
455  */
456