YAZ 5.38.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#include <limits.h>
16
17#include <yaz/yaz-iconv.h>
18#include <yaz/log.h>
19#include "comstack-p.h"
20#include <yaz/tcpip.h>
21#include <yaz/unix.h>
22#include <yaz/odr.h>
23#include <yaz/matchstr.h>
24#include <yaz/atoi.h>
25
26static const char *cs_errlist[] =
27{
28 "No error or unspecified error",
29 "System (lower-layer) error",
30 "Operation out of state",
31 "No data (operation would block)",
32 "New data while half of old buffer is on the line (flow control)",
33 "Permission denied",
34 "SSL error",
35 "Too large incoming buffer",
36 "Protocol error in incoming data"
37};
38
39const char *cs_errmsg(int n)
40{
41 if (n < CSNONE || n > CSLASTERROR)
42 n = CSNONE;
43 return cs_errlist[n];
44}
45
46const char *cs_strerror(COMSTACK h)
47{
48 return cs_errmsg(h->cerrno);
49}
50
51int cs_get_error(COMSTACK cs, const char **details)
52{
53 if (details)
54 *details = yaz_tcpip_get_error_details(cs);
55 return cs->cerrno;
56}
57
58void cs_get_host_args(const char *type_and_host, const char **args)
59{
60 *args = "";
61 if (!strncmp(type_and_host, "unix:", 5))
62 {
63 const char *cp = strchr(type_and_host + 5, ':');
64 if (!cp)
65 return;
66 type_and_host = cp + 1;
67 if (!strchr(type_and_host, ':'))
68 {
69 *args = type_and_host; /* unix:path:args */
70 return;
71 }
72 }
73 if (*type_and_host)
74 {
75 const char *cp = strchr(type_and_host, '/');
76 if (cp)
77 {
78 if (cp > type_and_host && !memcmp(cp - 1, "://", 3))
79 cp = strchr(cp + 2, '/');
80 }
81 if (cp)
82 *args = cp + 1;
83 }
84}
85
86int cs_parse_host(const char *uri, const char **host,
87 CS_TYPE *t, enum oid_proto *proto,
88 char **connect_host)
89{
90 *connect_host = 0;
91
92 *t = tcpip_type;
93 if (strncmp(uri, "connect:", 8) == 0)
94 {
95 const char *cp = strchr(uri, ',');
96 if (cp)
97 {
98 size_t len;
99
100 uri += 8;
101 len = cp - uri;
102 *connect_host = (char *) xmalloc(len + 1);
103 memcpy(*connect_host, uri, len);
104 (*connect_host)[len] = '\0';
105 uri = cp + 1;
106 }
107 }
108 else if (strncmp(uri, "unix:", 5) == 0)
109 {
110 const char *cp;
111
112 uri += 5;
113 cp = strchr(uri, ':');
114 if (cp)
115 {
116 size_t len = cp - uri;
117 *connect_host = (char *) xmalloc(len + 1);
118 memcpy(*connect_host, uri, len);
119 (*connect_host)[len] = '\0';
120 uri = cp + 1;
121 }
122#ifdef WIN32
123 xfree(*connect_host);
124 *connect_host = 0;
125 return 0;
126#else
127 *t = unix_type;
128#endif
129 }
130
131 if (strncmp (uri, "tcp:", 4) == 0)
132 {
133 *host = uri + 4;
134 *proto = PROTO_Z3950;
135 }
136 else if (strncmp (uri, "ssl:", 4) == 0)
137 {
138#if HAVE_GNUTLS_H
139 *t = ssl_type;
140 *host = uri + 4;
141 *proto = PROTO_Z3950;
142#else
143 xfree(*connect_host);
144 *connect_host = 0;
145 return 0;
146#endif
147 }
148 else if (strncmp(uri, "http:", 5) == 0)
149 {
150 *host = uri + 5;
151 while (**host == '/')
152 (*host)++;
153 *proto = PROTO_HTTP;
154 }
155 else if (strncmp(uri, "https:", 6) == 0)
156 {
157#if HAVE_GNUTLS_H
158 *t = ssl_type;
159 *host = uri + 6;
160 while (**host == '/')
161 (*host)++;
162 *proto = PROTO_HTTP;
163#else
164 xfree(*connect_host);
165 *connect_host = 0;
166 return 0;
167#endif
168 }
169 else
170 {
171 *host = uri;
172 *proto = PROTO_Z3950;
173 }
174 return 1;
175}
176
177COMSTACK cs_create_host(const char *vhost, int flags, void **vp)
178{
179 return cs_create_host_proxy(vhost, flags, vp, 0);
180}
181
182COMSTACK cs_create_host_proxy(const char *vhost, int flags, void **vp,
183 const char *proxy_host)
184{
185 int proxy_mode;
186 return cs_create_host2(vhost, flags, vp, proxy_host, &proxy_mode);
187}
188
189COMSTACK cs_create_host2(const char *vhost, int flags, void **vp,
190 const char *proxy_host, int *proxy_mode)
191{
192 enum oid_proto proto = PROTO_Z3950;
193 const char *host = 0;
194 COMSTACK cs;
195 CS_TYPE t;
196 char *connect_buf = 0;
197 char *connect_host = 0;
198 char *connect_auth = 0;
199
200 const char *bind_host = strchr(vhost, ' ');
201 if (bind_host && bind_host[1])
202 bind_host++;
203 else
204 bind_host = 0;
205
206 *proxy_mode = 0;
207 if (!cs_parse_host(vhost, &host, &t, &proto, &connect_buf))
208 return 0;
209
210 /* vhost proxy proxy method proxy-flag */
211 /* TCP+Z3950 TCP+Z3950 TCP+Z3950 1 */
212 /* TCP+Z3950 TCP+HTTP CONNECT 0 */
213 /* TCP+HTTP TCP+Z3950 TCP+HTTP 1 */
214 /* TCP+HTTP TCP+HTTP TCP+HTTP 1 */
215 /* SSL+* TCP+* CONNECT 0 */
216 /* ? SSL error */
217
218 if (proxy_host && !connect_buf)
219 {
220 enum oid_proto proto1;
221 CS_TYPE t1;
222 const char *host1 = 0;
223
224 if (!cs_parse_host(proxy_host, &host1, &t1, &proto1, &connect_buf))
225 return 0;
226 if (connect_buf)
227 {
228 xfree(connect_buf);
229 return 0;
230 }
231 if (t1 != tcpip_type)
232 return 0;
233
234 if (t == ssl_type || (proto == PROTO_Z3950 && proto1 == PROTO_HTTP))
235 connect_buf = xstrdup(host1);
236 else
237 {
238 *proxy_mode = 1;
239 host = host1;
240 }
241 }
242 if (connect_buf)
243 {
244 char *cp = strchr(connect_buf, '@');
245 if (cp)
246 {
247 *cp = '\0';
248 connect_host = cp + 1;
249 connect_auth = connect_buf;
250 } else {
251 connect_host = connect_buf;
252 }
253 }
254 if (t == tcpip_type)
255 {
256 cs = yaz_tcpip_create3(-1, flags, proto, connect_host ? host : 0,
257 connect_auth, bind_host);
258 }
259 else if (t == ssl_type)
260 {
261 cs = yaz_ssl_create(-1, flags, proto, connect_host ? host : 0,
262 connect_auth, bind_host);
263 }
264 else
265 {
266 cs = cs_create(t, flags, proto);
267 }
268 if (cs)
269 {
270 if (!(*vp = cs_straddr(cs, connect_host ? connect_host : host)))
271 {
272 cs_close (cs);
273 cs = 0;
274 }
275 }
276 xfree(connect_buf);
277 return cs;
278}
279
281{
282 return cs->event;
283}
284
285static int skip_crlf(const char *buf, int len, int *i)
286{
287 if (*i < len)
288 {
289 if (buf[*i] == '\r' && *i < len-1 && buf[*i + 1] == '\n')
290 {
291 (*i) += 2;
292 return 1;
293 }
294 else if (buf[*i] == '\n')
295 {
296 (*i)++;
297 return 1;
298 }
299 }
300 return 0;
301}
302
303static int http_token_char(char c)
304{
305 /* RFC 9110, Section 5.6.2: tchar */
306 return yaz_isdigit(c) || yaz_isupper(c) || yaz_islower(c)
307 || (c && strchr("!#$%&'*+-.^_`|~", c));
308}
309
310int yaz_http_parse_chunks(const char *buf, int len, char *content_buf,
311 int *content_len)
312{
313 int i = 0;
314 int off = 0;
315
316 /* inside chunked body .. */
317 while (1)
318 {
319 int chunk_len = 0;
320 int ret;
321
322 if (i >= len)
323 return 0;
324 /* read chunk length */
325 ret = yaz_atoi(16, buf + i, len - i, &chunk_len);
326 if (ret <= 0)
327 return -1;
328 i += ret;
329 /* skip chunk extension and consume the line ending */
330 while (1)
331 {
332 if (i >= len)
333 return 0;
334 if (skip_crlf(buf, len, &i))
335 break;
336 i++;
337 }
338
339 if (chunk_len == 0)
340 break;
341 if (chunk_len > len - i)
342 return 0;
343 if (content_buf)
344 memcpy(content_buf + off, buf + i, chunk_len);
345 i += chunk_len;
346 off += chunk_len;
347 if (i >= len)
348 return 0;
349 if (!skip_crlf(buf, len, &i))
350 {
351 if (buf[i] == '\r' && i == len - 1)
352 return 0;
353 return -1;
354 }
355 }
356 /* consider trailing headers .. */
357 while (1)
358 {
359 if (i >= len)
360 return 0;
361 if (buf[i] == '\r' && i == len - 1)
362 return 0;
363 if (skip_crlf(buf, len, &i))
364 {
365 if (content_len)
366 *content_len = off;
367 return i;
368 }
369
370 /* validate the trailer field name */
371 {
372 int field_start = i;
373
374 while (1)
375 {
376 if (i >= len)
377 return 0;
378 if (buf[i] == ':')
379 {
380 if (i == field_start)
381 return -1;
382 i++;
383 break;
384 }
385 if (!http_token_char(buf[i]))
386 return -1;
387 i++;
388 }
389 }
390
391 /* skip the trailer field value */
392 while (1)
393 {
394 if (i >= len)
395 return 0;
396 if (skip_crlf(buf, len, &i))
397 break;
398 i++;
399 }
400 }
401}
402
403static int cs_complete_http(const char *buf, int len, int head_only)
404{
405 /* deal with HTTP request/response */
406 int i, content_len = 0, chunked = 0;
407
408 /* need at least one line followed by \n or \r .. */
409 for (i = 0; ; i++)
410 if (i == len)
411 return 0; /* incomplete */
412 else if (buf[i] == '\n' || buf[i] == '\r')
413 break;
414
415 /* check to see if it's a response with content */
416 if (!head_only && !memcmp(buf, "HTTP/", 5))
417 {
418 int j;
419 for (j = 5; j < i; j++)
420 if (buf[j] == ' ')
421 {
422 ++j;
423 if (buf[j] == '1') /* 1XX */
424 head_only = 1;
425 else if (!memcmp(buf + j, "204", 3))
426 head_only = 1;
427 else if (!memcmp(buf + j, "304", 3))
428 head_only = 1;
429 else
430 content_len = -1;
431 break;
432 }
433 }
434 for (i = 2; i <= len - 2; )
435 {
436 if (i > 8192)
437 return -1; /* header exceeds 8K: protocol error */
438 if (!skip_crlf(buf, len, &i))
439 {
440 i++;
441 continue;
442 }
443 if (skip_crlf(buf, len, &i))
444 {
445 /* inside content */
446 if (head_only)
447 return i;
448 if (chunked)
449 {
450 int r = yaz_http_parse_chunks(buf + i, len - i, 0, 0);
451 return r > 0 ? i + r : r;
452 }
453 if (content_len == -1)
454 return 0; /* no content length */
455 if (content_len > (unsigned)(INT_MAX - i))
456 return -1;
457 if (len >= i + content_len)
458 return i + content_len;
459 break;
460 }
461 if (i < len - 19 && !yaz_strncasecmp(buf+i, "Transfer-Encoding:", 18))
462 {
463 int token_start;
464 i += 18;
465 while (i < len && buf[i] == ' ')
466 i++;
467 token_start = i;
468 for (;;)
469 {
470 int j;
471 if (i >= len - 2)
472 return 0;
473 if (i == token_start + 7 && !yaz_strncasecmp(buf + token_start, "chunked", 7))
474 chunked = 1;
475 j = i;
476 if (skip_crlf(buf, len, &j))
477 break;
478 i++;
479 }
480 }
481 else if (i < len - 16 && !yaz_strncasecmp(buf+i, "Content-Length:", 15))
482 {
483 int no_read;
484
485 i += 15;
486 while (i < len && buf[i] == ' ')
487 i++;
488 if (i == len)
489 return 0;
490 no_read = yaz_atoi(10, buf + i, len - i, &content_len);
491 if (no_read <= 0)
492 return -1; /* overflow or no digits */
493 i += no_read;
494 }
495 else
496 i++;
497 }
498 return 0;
499}
500
501static int cs_complete_auto_x(const char *buf, int len, int head_only)
502{
503 if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
504 && buf[1] >= 0x20 && buf[1] < 0x7f
505 && buf[2] >= 0x20 && buf[2] < 0x7f
506 && buf[3] >= 0x20 && buf[3] < 0x7f)
507 {
508 int r = cs_complete_http(buf, len, head_only);
509 return r;
510 }
511 return completeBER_n(buf, len, 0);
512}
513
514int cs_complete_auto(const char *buf, int len)
515{
516 return cs_complete_auto_x(buf, len, 0);
517}
518
519int cs_complete_auto_head(const char *buf, int len)
520{
521 return cs_complete_auto_x(buf, len, 1);
522}
523
524int cs_set_head_only(COMSTACK cs, int head_only)
525{
526 if (cs->type == tcpip_type || cs->type == ssl_type)
527 return yaz_tcpip_set_head_only(cs, head_only);
528#ifndef WIN32
529 if (cs->type == unix_type)
530 return yaz_unix_set_head_only(cs, head_only);
531#endif
532 cs->cerrno = CSOUTSTATE;
533 return -1;
534}
535
536void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
537{
538 cs->max_recv_bytes = max_recv_bytes;
539}
540
541/*
542 * Local variables:
543 * c-basic-offset: 4
544 * c-file-style: "Stroustrup"
545 * indent-tabs-mode: nil
546 * End:
547 * vim: shiftwidth=4 tabstop=8 expandtab
548 */
ASCII to integer conversion.
int yaz_atoi(int base, const char *ptr, int len, int *val)
string to int conversion, with base
Definition atoin.c:33
int completeBER_n(const char *buf, int len, int level)
determine whether a buffer is a complete BER buffer
Definition ber_any.c:48
Private Header for COMSTACK.
const char * yaz_tcpip_get_error_details(COMSTACK cs)
Definition tcpip.c:1787
int yaz_http_parse_chunks(const char *buf, int len, char *content_buf, int *content_len)
Definition comstack.c:310
int yaz_unix_set_head_only(COMSTACK cs, int head_only)
Definition unix.c:313
int yaz_tcpip_set_head_only(COMSTACK cs, int head_only)
Definition tcpip.c:1772
int cs_get_error(COMSTACK cs, const char **details)
Returns the last COMSTACK error and optional transport details.
Definition comstack.c:51
static int skip_crlf(const char *buf, int len, int *i)
Definition comstack.c:285
void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
Sets the maximum incoming message size.
Definition comstack.c:536
COMSTACK cs_create_host2(const char *vhost, int flags, void **vp, const char *proxy_host, int *proxy_mode)
Creates an endpoint and selects direct or proxy connection handling.
Definition comstack.c:189
int cs_complete_auto(const char *buf, int len)
Finds the length of a complete BER or HTTP message.
Definition comstack.c:514
static int cs_complete_http(const char *buf, int len, int head_only)
Definition comstack.c:403
int cs_parse_host(const char *uri, const char **host, CS_TYPE *t, enum oid_proto *proto, char **connect_host)
Parses transport and protocol prefixes without resolving an address.
Definition comstack.c:86
void cs_get_host_args(const char *type_and_host, const char **args)
Extracts application arguments from a host or URI specification.
Definition comstack.c:58
static int cs_complete_auto_x(const char *buf, int len, int head_only)
Definition comstack.c:501
int cs_look(COMSTACK cs)
Returns the last recorded event (CS_NONE, CS_CONNECT, etc.).
Definition comstack.c:280
int cs_complete_auto_head(const char *buf, int len)
Finds the length of a BER message or HTTP headers.
Definition comstack.c:519
static const char * cs_errlist[]
Definition comstack.c:26
const char * cs_errmsg(int n)
Returns a static message for a COMSTACK error code.
Definition comstack.c:39
int cs_set_head_only(COMSTACK cs, int head_only)
Selects HTTP header-only framing for TCP/IP, SSL or UNIX transports.
Definition comstack.c:524
COMSTACK cs_create_host(const char *vhost, int flags, void **vp)
Creates an endpoint from a host or URI specification.
Definition comstack.c:177
COMSTACK cs_create_host_proxy(const char *vhost, int flags, void **vp, const char *proxy_host)
Creates an endpoint with an optional proxy.
Definition comstack.c:182
const char * cs_strerror(COMSTACK h)
Returns a static message for the endpoint's last error.
Definition comstack.c:46
static int http_token_char(char c)
Definition comstack.c:303
#define cs_close(handle)
Closes the socket and frees the endpoint and its transport state.
Definition comstack.h:223
#define CSLASTERROR
Definition comstack.h:557
#define CSOUTSTATE
Definition comstack.h:543
#define CSNONE
Definition comstack.h:539
struct comstack * COMSTACK
Communication endpoint; release with cs_close.
Definition comstack.h:48
#define cs_create(type, flags, proto)
Creates an endpoint using a transport constructor.
Definition comstack.h:230
COMSTACK(*) CS_TYPE(int s, int flags, int protocol, void *vp)
Transport constructor and transport identity.
Definition comstack.h:56
#define cs_straddr(handle, str)
Parses a transport-specific address and prepares the endpoint.
Definition comstack.h:280
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
CS_TYPE type
Definition comstack.h:66
int event
Definition comstack.h:89
int cerrno
Definition comstack.h:67
int max_recv_bytes
Definition comstack.h:70
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:401
COMSTACK tcpip_type(int s, int flags, int protocol, void *vp)
Definition tcpip.c:238
COMSTACK ssl_type(int s, int flags, int protocol, void *vp)
Definition tcpip.c:380
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:327
Header for TCP/IP + SSL COMSTACK.
COMSTACK unix_type(int s, int flags, int protocol, void *vp)
Definition unix.c:115
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