Fix serious bug regarding HTTP requests (introduced in YAZ 2.0.24)
[yaz-moved-to-github.git] / src / comstack.c
1 /*
2  * Copyright (c) 1995-2004, Index Data
3  * See the file LICENSE for details.
4  *
5  * $Id: comstack.c,v 1.12 2004-09-30 10:00:17 adam Exp $
6  */
7
8 #include <string.h>
9 #include <ctype.h>
10 #include <errno.h>
11
12 #include <yaz/comstack.h>
13 #include <yaz/tcpip.h>
14 #include <yaz/unix.h>
15 #include <yaz/odr.h>
16
17 #ifdef WIN32
18 #define strncasecmp _strnicmp
19 #endif
20
21 static const char *cs_errlist[] =
22 {
23     "No error or unspecified error",
24     "System (lower-layer) error",
25     "Operation out of state",
26     "No data (operation would block)",
27     "New data while half of old buffer is on the line (flow control)",
28     "Permission denied",
29     "SSL error"
30 };
31
32 const char *cs_errmsg(int n)
33 {
34     static char buf[250];
35
36     if (n < CSNONE || n > CSLASTERROR) {
37         sprintf(buf, "unknown comstack error %d", n);
38         return buf;
39     }
40     if (n == CSYSERR) {
41         sprintf(buf, "%s: %s", cs_errlist[n], strerror(errno));
42         return buf;
43     }
44     return cs_errlist[n];
45 }
46
47 const char *cs_strerror(COMSTACK h)
48 {
49     return cs_errmsg(h->cerrno);
50 }
51
52 void cs_get_host_args(const char *type_and_host, const char **args)
53 {
54     
55     *args = "";
56     if (*type_and_host && strncmp(type_and_host, "unix:", 5))
57     {
58         const char *cp;
59         cp = strstr(type_and_host, "://");
60         if (cp)
61             cp = cp+3;
62         else
63             cp = type_and_host;
64         cp = strchr(cp, '/');
65         if (cp)
66             *args = cp+1;
67     }
68 }
69
70 COMSTACK cs_create_host(const char *type_and_host, int blocking, void **vp)
71 {
72     enum oid_proto proto = PROTO_Z3950;
73     const char *host = 0;
74     COMSTACK cs;
75     CS_TYPE t;
76
77     if (strncmp (type_and_host, "tcp:", 4) == 0)
78     {
79         t = tcpip_type;
80         host = type_and_host + 4;
81     }
82     else if (strncmp (type_and_host, "ssl:", 4) == 0)
83     {
84 #if HAVE_OPENSSL_SSL_H
85         t = ssl_type;
86         host = type_and_host + 4;
87 #else
88         return 0;
89 #endif
90     }
91     else if (strncmp (type_and_host, "unix:", 5) == 0)
92     {
93 #ifndef WIN32
94         t = unix_type;
95         host = type_and_host + 5;
96 #else
97         return 0;
98 #endif
99     }
100     else if (strncmp(type_and_host, "http:", 5) == 0)
101     {
102         t = tcpip_type;
103         host = type_and_host + 5;
104         while (host[0] == '/')
105             host++;
106         proto = PROTO_HTTP;
107     }
108     else if (strncmp(type_and_host, "https:", 6) == 0)
109     {
110 #if HAVE_OPENSSL_SSL_H
111         t = ssl_type;
112         host = type_and_host + 6;
113         while (host[0] == '/')
114             host++;
115         proto = PROTO_HTTP;
116 #else
117         return 0;
118 #endif
119     }
120     else
121     {
122         t = tcpip_type;
123         host = type_and_host;
124     }
125     cs = cs_create (t, blocking, proto);
126     if (!cs)
127         return 0;
128
129     if (!(*vp = cs_straddr(cs, host)))
130     {
131         cs_close (cs);
132         return 0;
133     }    
134     return cs;
135 }
136
137 int cs_look (COMSTACK cs)
138 {
139     return cs->event;
140 }
141
142 #define CHUNK_DEBUG 0
143 int cs_complete_auto(const unsigned char *buf, int len)
144 {
145     if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
146                 && buf[1] >= 0x20 && buf[1] < 0x7f
147                 && buf[2] >= 0x20 && buf[2] < 0x7f)
148     {
149         /* deal with HTTP request/response */
150         int i = 2, content_len = 0, chunked = 0;
151         
152         /* if dealing with HTTP responses - then default
153            content length is unlimited (socket close) */
154         if (!memcmp(buf, "HTTP/", 5))
155             content_len = -1; 
156
157         while (i <= len-4)
158         {
159             if (i > 8192)
160             {
161                 return i;  /* do not allow more than 8K HTTP header */
162             }
163             if (buf[i] == '\r' && buf[i+1] == '\n')
164             {
165                 i += 2;
166                 if (buf[i] == '\r' && buf[i+1] == '\n')
167                 {
168                     if (chunked)
169                     { 
170                         /* inside chunked body .. */
171                         while(1)
172                         {
173                             int j, chunk_len = 0;
174                             i += 2;
175 #if CHUNK_DEBUG
176 /* debugging */
177                             if (i <len-2)
178                             {
179                                 printf ("\n<<<");
180                                 int j;
181                                 for (j = i; j <= i+4; j++)
182                                     printf ("%c", buf[j]);
183                                 printf (">>>\n");
184                             }
185 #endif
186                             /* read chunk length */
187                             while (1)
188                                 if (i >= len-2) {
189 #if CHUNK_DEBUG
190 /* debugging */                                    
191                                     printf ("XXXXXXXX not there yet 1\n");
192                                     printf ("i=%d len=%d\n", i, len);
193 #endif
194                                     return 0;
195                                 } else if (isdigit(buf[i]))
196                                     chunk_len = chunk_len * 16 + 
197                                         (buf[i++] - '0');
198                                 else if (isupper(buf[i]))
199                                     chunk_len = chunk_len * 16 + 
200                                         (buf[i++] - ('A'-10));
201                                 else if (islower(buf[i]))
202                                     chunk_len = chunk_len * 16 + 
203                                         (buf[i++] - ('a'-10));
204                                 else
205                                     break;
206                             /* move forward until CRLF - skip chunk ext */
207                             j = 0;
208                             while (buf[i] != '\r' && buf[i+1] != '\n')
209                             {
210                                 if (i >= len-2)
211                                     return 0;   /* need more buffer .. */
212                                 if (++j > 1000)
213                                     return i; /* enough.. stop */
214                                 i++;
215                             }
216                             /* got CRLF */
217 #if CHUNK_DEBUG
218                             printf ("XXXXXX chunk_len=%d\n", chunk_len);
219 #endif                      
220                             if (chunk_len < 0)
221                                 return i+2;    /* bad chunk_len */
222                             if (chunk_len == 0)
223                                 break;
224                             i += chunk_len+2;
225                         }
226                         /* consider trailing headers .. */
227                         while(i <= len-4)
228                         {
229                             if (buf[i] == '\r' &&  buf[i+1] == '\n' &&
230                                 buf[i+2] == '\r' && buf[i+3] == '\n')
231                                 if (len >= i+4)
232                                     return i+4;
233                             i++;
234                         }
235 #if CHUNK_DEBUG
236 /* debugging */
237                         printf ("XXXXXXXXX not there yet 2\n");
238                         printf ("i=%d len=%d\n", i, len);
239 #endif
240                         return 0;
241                     }
242                     else
243                     {   /* not chunked ; inside body */
244                         /* i += 2 seems not to work with GCC -O2 .. 
245                            so i+2 is used instead .. */
246                         if (content_len == -1)
247                             return 0;   /* no content length */
248                         else if (len >= (i+2)+ content_len)
249                         {
250                             return (i+2)+ content_len;
251                         }
252                     }
253                     break;
254                 }
255                 else if (i < len - 20 && 
256                          !strncasecmp((const char *) buf+i, "Transfer-Encoding:", 18))
257                 {
258                     i+=18;
259                     while (buf[i] == ' ')
260                         i++;
261                     if (i < len - 8)
262                         if (!strncasecmp((const char *) buf+i, "chunked", 7))
263                             chunked = 1;
264                 }
265                 else if (i < len - 17 &&
266                          !strncasecmp((const char *)buf+i, "Content-Length:", 15))
267                 {
268                     i+= 15;
269                     while (buf[i] == ' ')
270                         i++;
271                     content_len = 0;
272                     while (i <= len-4 && isdigit(buf[i]))
273                         content_len = content_len*10 + (buf[i++] - '0');
274                     if (content_len < 0) /* prevent negative offsets */
275                         content_len = 0;
276                 }
277                 else
278                     i++;
279             }
280             else
281                 i++;
282         }
283         return 0;
284     }
285     return completeBER(buf, len);
286 }