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