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