YAZ  5.34.0
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 
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 
36 const char *cs_errmsg(int n)
37 {
38  if (n < CSNONE || n > CSLASTERROR)
39  n = CSNONE;
40  return cs_errlist[n];
41 }
42 
43 const char *cs_strerror(COMSTACK h)
44 {
45  return cs_errmsg(h->cerrno);
46 }
47 
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 
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 
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 
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 
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 
258 {
259  return cs->event;
260 }
261 
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 
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 
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 
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 
466 int cs_complete_auto(const char *buf, int len)
467 {
468  return cs_complete_auto_x(buf, len, 0);
469 }
470 
471 int cs_complete_auto_head(const char *buf, int len)
472 {
473  return cs_complete_auto_x(buf, len, 1);
474 }
475 
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 
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
const char * cs_strerror(COMSTACK h)
Definition: comstack.c:43
const char * cs_errmsg(int n)
Definition: comstack.c:36
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
COMSTACK cs_create_host_proxy(const char *vhost, int blocking, void **vp, const char *proxy_host)
Definition: comstack.c:172
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