Added cs_get_SSL. yaz-client-ssl prints peer info
[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.6 2004-02-25 12:59:56 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         if (host[0] == '/' && host[1] == '/')
105             host = host + 2;
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         if (host[0] == '/' && host[1] == '/')
114             host = host + 2;
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     }
126     cs = cs_create (t, blocking, proto);
127     if (!cs)
128         return 0;
129
130     if (!(*vp = cs_straddr(cs, host)))
131     {
132         cs_close (cs);
133         return 0;
134     }    
135     return cs;
136 }
137
138 int cs_look (COMSTACK cs)
139 {
140     return cs->event;
141 }
142
143 #define CHUNK_DEBUG 0
144 int cs_complete_auto(const unsigned char *buf, int len)
145 {
146     if (len > 5 && buf[0] >= 0x20 && buf[0] < 0x7f
147                 && buf[1] >= 0x20 && buf[1] < 0x7f
148                 && buf[2] >= 0x20 && buf[2] < 0x7f)
149     {
150         /* deal with HTTP request/response */
151         int i = 2, content_len = 0, chunked = 0;
152
153         while (i <= len-4)
154         {
155             if (i > 8192)
156                 return i;  /* do not allow more than 8K HTTP header */
157             if (buf[i] == '\r' && buf[i+1] == '\n')
158             {
159                 i += 2;
160                 if (buf[i] == '\r' && buf[i+1] == '\n')
161                 {
162                     if (chunked)
163                     { 
164                         /* inside chunked body .. */
165                         while(1)
166                         {
167                             int j, chunk_len = 0;
168                             i += 2;
169 #if CHUNK_DEBUG
170 /* debugging */
171                             if (i <len-2)
172                             {
173                                 printf ("\n<<<");
174                                 int j;
175                                 for (j = i; j <= i+4; j++)
176                                     printf ("%c", buf[j]);
177                                 printf (">>>\n");
178                             }
179 #endif
180                             /* read chunk length */
181                             while (1)
182                                 if (i >= len-2) {
183 #if CHUNK_DEBUG
184 /* debugging */                                    
185                                     printf ("XXXXXXXX not there yet 1\n");
186                                     printf ("i=%d len=%d\n", i, len);
187 #endif
188                                     return 0;
189                                 } else if (isdigit(buf[i]))
190                                     chunk_len = chunk_len * 16 + 
191                                         (buf[i++] - '0');
192                                 else if (isupper(buf[i]))
193                                     chunk_len = chunk_len * 16 + 
194                                         (buf[i++] - ('A'-10));
195                                 else if (islower(buf[i]))
196                                     chunk_len = chunk_len * 16 + 
197                                         (buf[i++] - ('a'-10));
198                                 else
199                                     break;
200                             /* move forward until CRLF - skip chunk ext */
201                             j = 0;
202                             while (buf[i] != '\r' && buf[i+1] != '\n')
203                             {
204                                 if (i >= len-2)
205                                     return 0;   /* need more buffer .. */
206                                 if (++j > 1000)
207                                     return i; /* enough.. stop */
208                                 i++;
209                             }
210                             /* got CRLF */
211 #if CHUNK_DEBUG
212                             printf ("XXXXXX chunk_len=%d\n", chunk_len);
213 #endif                      
214                             if (chunk_len < 0)
215                                 return i+2;    /* bad chunk_len */
216                             if (chunk_len == 0)
217                                 break;
218                             i += chunk_len+2;
219                         }
220                         /* consider trailing headers .. */
221                         while(i <= len-4)
222                         {
223                             if (buf[i] == '\r' &&  buf[i+1] == '\n' &&
224                                 buf[i+2] == '\r' && buf[i+3] == '\n')
225                                 if (len >= i+4)
226                                     return i+4;
227                             i++;
228                         }
229 #if CHUNK_DEBUG
230 /* debugging */
231                         printf ("XXXXXXXXX not there yet 2\n");
232                         printf ("i=%d len=%d\n", i, len);
233 #endif
234                         return 0;
235                     }
236                     else
237                     {   /* not chunked ; inside body */
238                         /* i += 2 seems not to work with GCC -O2 .. 
239                            so i+2 is used instead .. */
240                         if (len >= (i+2)+ content_len)
241                             return (i+2)+ content_len;
242                     }
243                     break;
244                 }
245                 else if (i < len - 20 && 
246                          !strncasecmp(buf+i, "Transfer-Encoding:", 18))
247                 {
248                     i+=18;
249                     while (buf[i] == ' ')
250                         i++;
251                     if (i < len - 8)
252                         if (!strncasecmp(buf+i, "chunked", 7))
253                             chunked = 1;
254                 }
255                 else if (i < len - 17 &&
256                          !strncasecmp(buf+i, "Content-Length:", 15))
257                 {
258                     i+= 15;
259                     while (buf[i] == ' ')
260                         i++;
261                     content_len = 0;
262                     while (i <= len-4 && isdigit(buf[i]))
263                         content_len = content_len*10 + (buf[i++] - '0');
264                     if (content_len < 0) /* prevent negative offsets */
265                         content_len = 0;
266                 }
267                 else
268                     i++;
269             }
270             else
271                 i++;
272         }
273         return 0;
274     }
275     return completeBER(buf, len);
276 }