0d069b038226701346b8d6ecf67d22958ba51252
[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     if (bind_host && bind_host[1])
188         bind_host++;
189     else
190         bind_host = 0;
191
192     if (!cs_parse_host(vhost, &host, &t, &proto, &connect_host))
193         return 0;
194     if (proxy_host)
195     {
196         enum oid_proto proto1;
197         CS_TYPE t1;
198
199         xfree(connect_host);
200         if (!cs_parse_host(proxy_host, &host, &t1, &proto1, &connect_host))
201             return 0;
202     }
203
204     if (t == tcpip_type)
205     {
206         cs = yaz_tcpip_create3(-1, blocking, proto, connect_host ? host : 0,
207                                0, bind_host);
208     }
209     else if (t == ssl_type)
210     {
211         cs = yaz_ssl_create(-1, blocking, proto, connect_host ? host : 0,
212                             0, bind_host);
213     }
214     else
215     {
216         cs = cs_create(t, blocking, proto);
217     }
218     if (cs)
219     {
220         if (!(*vp = cs_straddr(cs, connect_host ? connect_host : host)))
221         {
222             cs_close (cs);
223             cs = 0;
224         }
225     }
226     xfree(connect_host);
227     return cs;
228 }
229
230 int cs_look (COMSTACK cs)
231 {
232     return cs->event;
233 }
234
235 static int skip_crlf(const char *buf, int len, int *i)
236 {
237     if (*i < len)
238     {
239         if (buf[*i] == '\r' && *i < len-1 && buf[*i + 1] == '\n')
240         {
241             (*i) += 2;
242             return 1;
243         }
244         else if (buf[*i] == '\n')
245         {
246             (*i)++;
247             return 1;
248         }
249     }
250     return 0;
251 }
252
253 #define CHUNK_DEBUG 0
254
255 static int cs_read_chunk(const char *buf, int i, int len)
256 {
257     /* inside chunked body .. */
258     while (1)
259     {
260         int chunk_len = 0;
261 #if CHUNK_DEBUG
262         if (i < len-2)
263         {
264             int j;
265             printf ("\n<<<");
266             for (j = i; j <= i+3; j++)
267                 printf ("%c", buf[j]);
268             printf (">>>\n");
269         }
270 #endif
271         /* read chunk length */
272         while (1)
273             if (i >= len-2) {
274 #if CHUNK_DEBUG
275                 printf ("returning incomplete read at 1\n");
276                 printf ("i=%d len=%d\n", i, len);
277 #endif
278                 return 0;
279             } else if (yaz_isdigit(buf[i]))
280                 chunk_len = chunk_len * 16 +
281                     (buf[i++] - '0');
282             else if (yaz_isupper(buf[i]))
283                 chunk_len = chunk_len * 16 +
284                     (buf[i++] - ('A'-10));
285             else if (yaz_islower(buf[i]))
286                 chunk_len = chunk_len * 16 +
287                     (buf[i++] - ('a'-10));
288             else
289                 break;
290         if (chunk_len == 0)
291             break;
292         if (chunk_len < 0)
293             return i;
294
295         while (1)
296         {
297             if (i >= len -1)
298                 return 0;
299             if (skip_crlf(buf, len, &i))
300                 break;
301             i++;
302         }
303         /* got CRLF */
304 #if CHUNK_DEBUG
305         printf ("chunk_len=%d\n", chunk_len);
306 #endif
307         i += chunk_len;
308         if (i >= len-2)
309             return 0;
310         if (!skip_crlf(buf, len, &i))
311             return 0;
312     }
313     /* consider trailing headers .. */
314     while (i < len)
315     {
316         if (skip_crlf(buf, len, &i))
317         {
318             if (skip_crlf(buf, len, &i))
319                 return i;
320         }
321         else
322             i++;
323     }
324 #if CHUNK_DEBUG
325     printf ("returning incomplete read at 2\n");
326     printf ("i=%d len=%d\n", i, len);
327 #endif
328     return 0;
329 }
330
331 static int cs_complete_http(const char *buf, int len, int head_only)
332 {
333     /* deal with HTTP request/response */
334     int i, content_len = 0, chunked = 0;
335
336     /* need at least one line followed by \n or \r .. */
337     for (i = 0; ; i++)
338         if (i == len)
339             return 0; /* incomplete */
340         else if (buf[i] == '\n' || buf[i] == '\r')
341             break;
342
343     /* check to see if it's a response with content */
344     if (!head_only && !memcmp(buf, "HTTP/", 5))
345     {
346         int j;
347         for (j = 5; j < i; j++)
348             if (buf[j] == ' ')
349             {
350                 ++j;
351                 if (buf[j] == '1') /* 1XX */
352                     ;
353                 else if (!memcmp(buf + j, "204", 3))
354                     ;
355                 else if (!memcmp(buf + j, "304", 3))
356                     ;
357                 else
358                     content_len = -1;
359                 break;
360             }
361     }
362 #if 0
363     printf("len = %d\n", len);
364     fwrite (buf, 1, len, stdout);
365     printf("----------\n");
366 #endif
367     for (i = 2; i <= len-2; )
368     {
369         if (i > 8192)
370         {
371             return i;  /* do not allow more than 8K HTTP header */
372         }
373         if (skip_crlf(buf, len, &i))
374         {
375             if (skip_crlf(buf, len, &i))
376             {
377                 /* inside content */
378                 if (chunked)
379                     return cs_read_chunk(buf, i, len);
380                 else
381                 {   /* not chunked ; inside body */
382                     if (content_len == -1)
383                         return 0;   /* no content length */
384                     else if (len >= i + content_len)
385                     {
386                         return i + content_len;
387                     }
388                 }
389                 break;
390             }
391             else if (i < len - 20 &&
392                      !yaz_strncasecmp((const char *) buf+i,
393                                       "Transfer-Encoding:", 18))
394             {
395                 i+=18;
396                 while (buf[i] == ' ')
397                     i++;
398                 if (i < len - 8)
399                     if (!yaz_strncasecmp((const char *) buf+i, "chunked", 7))
400                         chunked = 1;
401             }
402             else if (i < len - 17 &&
403                      !yaz_strncasecmp((const char *)buf+i,
404                                       "Content-Length:", 15))
405             {
406                 i+= 15;
407                 while (buf[i] == ' ')
408                     i++;
409                 content_len = 0;
410                 while (i <= len-4 && yaz_isdigit(buf[i]))
411                     content_len = content_len*10 + (buf[i++] - '0');
412                 if (content_len < 0) /* prevent negative offsets */
413                     content_len = 0;
414             }
415             else
416                 i++;
417         }
418         else
419             i++;
420     }
421     return 0;
422 }
423
424 static int cs_complete_auto_x(const char *buf, int len, int head_only)
425 {
426     if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
427                 && buf[1] >= 0x20 && buf[1] < 0x7f
428                 && buf[2] >= 0x20 && buf[2] < 0x7f)
429     {
430         int r = cs_complete_http(buf, len, head_only);
431         return r;
432     }
433     return completeBER(buf, len);
434 }
435
436
437 int cs_complete_auto(const char *buf, int len)
438 {
439     return cs_complete_auto_x(buf, len, 0);
440 }
441
442 int cs_complete_auto_head(const char *buf, int len)
443 {
444     return cs_complete_auto_x(buf, len, 1);
445 }
446
447 void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
448 {
449     cs->max_recv_bytes = max_recv_bytes;
450 }
451
452 /*
453  * Local variables:
454  * c-basic-offset: 4
455  * c-file-style: "Stroustrup"
456  * indent-tabs-mode: nil
457  * End:
458  * vim: shiftwidth=4 tabstop=8 expandtab
459  */
460