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