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