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