YAZ 5.35.1
comstack.c
Go to the documentation of this file.
1/* This file is part of the YAZ toolkit.
2 * Copyright (C) Index Data
3 * See the file LICENSE for details.
4 */
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
24static 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
36const char *cs_errmsg(int n)
37{
38 if (n < CSNONE || n > CSLASTERROR)
39 n = CSNONE;
40 return cs_errlist[n];
41}
42
43const char *cs_strerror(COMSTACK h)
44{
45 return cs_errmsg(h->cerrno);
46}
47
48void 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
76int 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
167COMSTACK cs_create_host(const char *vhost, int blocking, void **vp)
168{
169 return cs_create_host_proxy(vhost, blocking, vp, 0);
170}
171
172COMSTACK 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
179COMSTACK 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
258{
259 return cs->event;
260}
261
262static 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
282static 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
358static 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
453static 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
466int cs_complete_auto(const char *buf, int len)
467{
468 return cs_complete_auto_x(buf, len, 0);
469}
470
471int cs_complete_auto_head(const char *buf, int len)
472{
473 return cs_complete_auto_x(buf, len, 1);
474}
475
476void 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
int completeBER(const char *buf, int len)
determine whether a buffer is a complete BER buffer
Definition ber_any.c:123
static int skip_crlf(const char *buf, int len, int *i)
Definition comstack.c:262
COMSTACK cs_create_host(const char *vhost, int blocking, void **vp)
Definition comstack.c:167
void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
Definition comstack.c:476
static int cs_read_chunk(const char *buf, int i, int len)
Definition comstack.c:282
int cs_complete_auto(const char *buf, int len)
Definition comstack.c:466
static int cs_complete_http(const char *buf, int len, int head_only)
Definition comstack.c:358
int cs_parse_host(const char *uri, const char **host, CS_TYPE *t, enum oid_proto *proto, char **connect_host)
Definition comstack.c:76
void cs_get_host_args(const char *type_and_host, const char **args)
Definition comstack.c:48
static int cs_complete_auto_x(const char *buf, int len, int head_only)
Definition comstack.c:453
int cs_look(COMSTACK cs)
Definition comstack.c:257
int cs_complete_auto_head(const char *buf, int len)
Definition comstack.c:471
static const char * cs_errlist[]
Definition comstack.c:24
COMSTACK cs_create_host2(const char *vhost, int blocking, void **vp, const char *proxy_host, int *proxy_mode)
Definition comstack.c:179
const char * cs_errmsg(int n)
Definition comstack.c:36
COMSTACK cs_create_host_proxy(const char *vhost, int blocking, void **vp, const char *proxy_host)
Definition comstack.c:172
const char * cs_strerror(COMSTACK h)
Definition comstack.c:43
Header for COMSTACK.
#define cs_close(handle)
Definition comstack.h:99
#define CSLASTERROR
Definition comstack.h:163
#define CSNONE
Definition comstack.h:155
#define cs_straddr(handle, str)
Definition comstack.h:109
#define cs_create(type, blocking, proto)
Definition comstack.h:100
COMSTACK(* CS_TYPE)(int s, int flags, int protocol, void *vp)
Definition comstack.h:44
Header for errno utilities.
Logging utility.
int yaz_strncasecmp(const char *s1, const char *s2, size_t n)
ala strncasecmp - no locale
Definition matchstr.c:26
Header for YAZ iconv interface.
Header for ODR (Open Data Representation)
oid_proto
Definition oid_util.h:45
@ PROTO_HTTP
Definition oid_util.h:48
@ PROTO_Z3950
Definition oid_util.h:47
int event
Definition comstack.h:64
int cerrno
Definition comstack.h:49
int max_recv_bytes
Definition comstack.h:52
COMSTACK yaz_ssl_create(int s, int flags, int protocol, const char *connect_host, const char *connect_auth, const char *bind_host)
Definition tcpip.c:345
COMSTACK tcpip_type(int s, int flags, int protocol, void *vp)
Definition tcpip.c:207
COMSTACK ssl_type(int s, int flags, int protocol, void *vp)
Definition tcpip.c:324
COMSTACK yaz_tcpip_create3(int s, int flags, int protocol, const char *connect_host, const char *connect_auth, const char *bind_host)
Definition tcpip.c:288
Header for TCP/IP + SSL COMSTACK.
COMSTACK unix_type(int s, int flags, int protocol, void *vp)
Definition unix.c:108
Header for UNIX domain socket COMSTACK.
#define xstrdup(s)
utility macro which calls xstrdup_f
Definition xmalloc.h:55
#define xfree(x)
utility macro which calls xfree_f
Definition xmalloc.h:53
#define xmalloc(x)
utility macro which calls malloc_f
Definition xmalloc.h:49
Header for YAZ iconv interface.
#define yaz_islower(x)
Definition yaz-iconv.h:91
#define yaz_isdigit(x)
Definition yaz-iconv.h:86
#define yaz_isupper(x)
Definition yaz-iconv.h:89