Added debug logging to verify ZOOM_EVENT_RECV_SEARCH
[yaz-moved-to-github.git] / src / comstack.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2010 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_read_chunk(const char *buf, int i, int len)
212 {
213     /* inside chunked body .. */
214     while (1)
215     {
216         int chunk_len = 0;
217 #if CHUNK_DEBUG
218         if (i < len-2)
219         {
220             printf ("\n<<<");
221             int j;
222             for (j = i; j <= i+3; j++)
223                 printf ("%c", buf[j]);
224             printf (">>>\n");
225         }
226 #endif
227         /* read chunk length */
228         while (1)
229             if (i >= len-2) {
230 #if CHUNK_DEBUG
231                 printf ("returning incomplete read at 1\n");
232                 printf ("i=%d len=%d\n", i, len);
233 #endif
234                 return 0;
235             } else if (isdigit(buf[i]))
236                 chunk_len = chunk_len * 16 + 
237                     (buf[i++] - '0');
238             else if (isupper(buf[i]))
239                 chunk_len = chunk_len * 16 + 
240                     (buf[i++] - ('A'-10));
241             else if (islower(buf[i]))
242                 chunk_len = chunk_len * 16 + 
243                     (buf[i++] - ('a'-10));
244             else
245                 break;
246         if (chunk_len == 0)
247             break;
248         if (chunk_len < 0)
249             return i;
250         
251         while (1)
252         {
253             if (i >= len -1)
254                 return 0;
255             if (skip_crlf(buf, len, &i))
256                 break;
257             i++;
258         }
259         /* got CRLF */
260 #if CHUNK_DEBUG
261         printf ("chunk_len=%d\n", chunk_len);
262 #endif                      
263         i += chunk_len;
264         if (i >= len-2)
265             return 0;
266         if (!skip_crlf(buf, len, &i))
267             return 0;
268     }
269     /* consider trailing headers .. */
270     while (i < len)
271     {
272         if (skip_crlf(buf, len, &i))
273         {
274             if (skip_crlf(buf, len, &i))
275                 return i;
276         }
277         else
278             i++;
279     }
280 #if CHUNK_DEBUG
281     printf ("returning incomplete read at 2\n");
282     printf ("i=%d len=%d\n", i, len);
283 #endif
284     return 0;
285 }
286
287 static int cs_complete_http(const char *buf, int len, int head_only)
288 {
289     /* deal with HTTP request/response */
290     int i = 2, content_len = 0, chunked = 0;
291
292     if (len < 6)
293         return 0;
294
295     /* if dealing with HTTP responses - then default
296        content length is unlimited (socket close) */
297     if (!head_only && !memcmp(buf, "HTTP/", 5))
298         content_len = -1; 
299
300 #if 0
301     printf("len = %d\n", len);
302     fwrite (buf, 1, len, stdout);
303     printf("----------\n");
304 #endif
305     while (i <= len-2)
306     {
307         if (i > 8192)
308         {
309             return i;  /* do not allow more than 8K HTTP header */
310         }
311         if (skip_crlf(buf, len, &i))
312         {
313             if (skip_crlf(buf, len, &i))
314             {
315                 /* inside content */
316                 if (chunked)
317                     return cs_read_chunk(buf, i, len);
318                 else
319                 {   /* not chunked ; inside body */
320                     if (content_len == -1)
321                         return 0;   /* no content length */
322                     else if (len >= i + content_len)
323                     {
324                         return i + content_len;
325                     }
326                 }
327                 break;
328             }
329             else if (i < len - 20 && 
330                      !strncasecmp((const char *) buf+i, "Transfer-Encoding:", 18))
331             {
332                 i+=18;
333                 while (buf[i] == ' ')
334                     i++;
335                 if (i < len - 8)
336                     if (!strncasecmp((const char *) buf+i, "chunked", 7))
337                         chunked = 1;
338             }
339             else if (i < len - 17 &&
340                      !strncasecmp((const char *)buf+i, "Content-Length:", 15))
341             {
342                 i+= 15;
343                 while (buf[i] == ' ')
344                     i++;
345                 content_len = 0;
346                 while (i <= len-4 && isdigit(buf[i]))
347                     content_len = content_len*10 + (buf[i++] - '0');
348                 if (content_len < 0) /* prevent negative offsets */
349                     content_len = 0;
350             }
351             else
352                 i++;
353         }
354         else
355             i++;
356     }
357     return 0;
358 }
359
360 static int cs_complete_auto_x(const char *buf, int len, int head_only)
361 {
362     if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
363                 && buf[1] >= 0x20 && buf[1] < 0x7f
364                 && buf[2] >= 0x20 && buf[2] < 0x7f)
365     {
366         int r = cs_complete_http(buf, len, head_only);
367         return r;
368     }
369     return completeBER((const unsigned char *) buf, len);
370 }
371
372
373 int cs_complete_auto(const char *buf, int len)
374 {
375     return cs_complete_auto_x(buf, len, 0);
376 }
377
378 int cs_complete_auto_head(const char *buf, int len)
379 {
380     return cs_complete_auto_x(buf, len, 1);
381 }
382
383 void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
384 {
385     cs->max_recv_bytes = max_recv_bytes;
386 }
387
388 /*
389  * Local variables:
390  * c-basic-offset: 4
391  * c-file-style: "Stroustrup"
392  * indent-tabs-mode: nil
393  * End:
394  * vim: shiftwidth=4 tabstop=8 expandtab
395  */
396