NCBI C Toolkit Cross Reference

C/connect/test/test_ncbi_http_get.c


  1 /* $Id: test_ncbi_http_get.c,v 6.48 2016/03/04 01:54:16 fukanchi Exp $
  2  * ===========================================================================
  3  *
  4  *                            PUBLIC DOMAIN NOTICE
  5  *               National Center for Biotechnology Information
  6  *
  7  *  This software/database is a "United States Government Work" under the
  8  *  terms of the United States Copyright Act.  It was written as part of
  9  *  the author's official duties as a United States Government employee and
 10  *  thus cannot be copyrighted.  This software/database is freely available
 11  *  to the public for use. The National Library of Medicine and the U.S.
 12  *  Government have not placed any restriction on its use or reproduction.
 13  *
 14  *  Although all reasonable efforts have been taken to ensure the accuracy
 15  *  and reliability of the software and data, the NLM and the U.S.
 16  *  Government do not and cannot warrant the performance or results that
 17  *  may be obtained by using this software or data. The NLM and the U.S.
 18  *  Government disclaim all warranties, express or implied, including
 19  *  warranties of performance, merchantability or fitness for any particular
 20  *  purpose.
 21  *
 22  *  Please cite the author in any work or product based on this material.
 23  *
 24  * ===========================================================================
 25  *
 26  * Author:  Anton Lavrentiev
 27  *
 28  * File Description:
 29  *   Retrieve a Web document via HTTP
 30  *
 31  */
 32 
 33 #include <connect/ncbi_base64.h>
 34 #include <connect/ncbi_gnutls.h>
 35 #include <connect/ncbi_http_connector.h>
 36 #include <connect/ncbi_util.h>
 37 #include "../ncbi_ansi_ext.h"
 38 #include "../ncbi_priv.h"               /* CORE logging facilities */
 39 #include <ctype.h>
 40 #include <errno.h>
 41 #ifdef NCBI_OS_MSWIN
 42 #  include <io.h>      /* for access() */
 43 #  define   access  _access
 44 #  ifndef   R_OK
 45 #    define R_OK    4  /* per MSDN     */
 46 #  endif /*!R_OK*/
 47 #endif /*NCBI_OS_MSWIN*/
 48 #include <stdlib.h>
 49 #include <time.h>
 50 #ifdef NCBI_OS_UNIX
 51 #  include <unistd.h>  /* for access() and maybe usleep() */
 52 #endif /*NCBI_OS_UNIX*/
 53 #ifdef HAVE_LIBGNUTLS
 54 #  include <gnutls/gnutls.h>
 55 #  if LIBGNUTLS_VERSION_NUMBER >= 0x021000
 56 #    include <gnutls/x509.h>
 57 #  endif /*LIBGNUTLS_VERSION_NUMBER>=2.10.0*/
 58 #  define GNUTLS_PKCS12_TYPE  "TEST_NCBI_HTTP_GET_TYPE"
 59 #  define GNUTLS_PKCS12_FILE  "TEST_NCBI_HTTP_GET_CERT"
 60 #  define GNUTLS_PKCS12_PASS  "TEST_NCBI_HTTP_GET_PASS"
 61 #endif /*HAVE_LIBGNUTLS*/
 62 
 63 #include "test_assert.h"  /* This header must go last */
 64 
 65 
 66 #ifdef HAVE_LIBGNUTLS
 67 
 68 #ifdef __GNUC__
 69 inline
 70 #endif /*__GNUC__*/
 71 static int/*bool*/ x_IsPrintable(const char* buf, size_t size)
 72 {
 73     size_t n;
 74     for (n = 0;  n < size;  ++n) {
 75         if (!buf[n]  ||  !isprint((unsigned char) buf[n]))
 76             return 0/*false*/;
 77     }
 78     return 1/*true*/;
 79 }
 80 
 81 
 82 /* Accept both plain and BASE-64 encoded, printable password */
 83 static const char* x_GetPkcs12Pass(const char* val, char* buf, size_t bufsize)
 84 {
 85     size_t in, out, len, half = bufsize >> 1;
 86     if (!(val = ConnNetInfo_GetValue(0, val, buf, half - 1, 0))  ||  !*val)
 87         return "";
 88     if (!x_IsPrintable(val, len = strlen(val)))
 89         return "";
 90     buf += half;
 91     if (BASE64_Decode(val, len, &in, buf, half - 1, &out)  &&  in == len) {
 92         assert(out  &&  out < half);
 93         if ((buf[out - 1] != '\n'  ||  --out)  &&
 94             (buf[out - 1] != '\r'  ||  --out)  &&
 95             x_IsPrintable(buf, out)) {
 96             buf[out] = '\0';
 97             return buf;
 98         }
 99     }
100     return (const char*) memcpy(buf, val, ++len);
101 }
102 
103 
104 #if LIBGNUTLS_VERSION_NUMBER >= 0x021000
105 static int x_CertVfyCB(gnutls_session_t session)
106 {
107         unsigned int n, cert_list_size = 0;
108         const gnutls_datum_t* cert_list;
109         
110         cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
111         if (cert_list_size == 0) {
112                 CORE_LOG(eLOG_Error, "No certificates obtained from server");
113                 return 1;
114         }
115     assert(cert_list);
116 
117     CORE_LOGF(eLOG_Note,
118               ("%u certificate%s received from server:",
119                cert_list_size, &"s"[cert_list_size == 1]));
120         for (n = 0;  n < cert_list_size;  ++n) {
121         gnutls_x509_crt_t crt;
122                 gnutls_datum_t cinfo;
123         int err;
124 
125                 gnutls_x509_crt_init(&crt);
126         err = gnutls_x509_crt_import(crt, &cert_list[n],
127                                      GNUTLS_X509_FMT_DER);
128                 if (!err) {
129             err = gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_ONELINE, &cinfo);
130             if (!err) {
131                 CORE_LOGF(eLOG_Note,
132                           ("Certificate[%u]: %s", n + 1, cinfo.data));
133                 /*gnutls_*/free(cinfo.data);
134             }
135         }
136         if (err) {
137             CORE_LOGF(eLOG_Error,
138                       ("Certificate[%u] error: %s",
139                        n + 1, gnutls_strerror(err)));
140         }
141         gnutls_x509_crt_deinit(crt);
142         }
143     return 0;
144 }
145 #endif /*LIBGNUTLS_VERSION_NUMBER>=2.10.0*/
146 
147 
148 #if 0
149 static int x_CertRtrCB(gnutls_session_t session,
150                        const gnutls_datum_t* req_ca_dn,    int n_req_ca_dn,
151                        const gnutls_pk_algorithm_t* algos, int n_algos,
152                        gnutls_pcert_st** pcert,  unsigned int* n_pcert,
153                        gnutls_privkey_t* pkey)
154 {
155     gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
156     const char* str;
157     char buf[256];
158 
159     /* The very fact of receiving this callback is that
160      * gnutls_certificate_client_get_request_status(session)
161      * would return non-zero (i.e. certificate is required).
162      */
163     switch (type) {
164     case GNUTLS_CRT_X509:
165         str = "X.509";
166         break;
167     case GNUTLS_CRT_OPENPGP:
168         str = "Open PGP";
169         break;
170     case GNUTLS_CRT_RAW:
171         str = "Raw public key";
172         break;
173     default:
174         sprintf(buf, "%u", (int) type);
175         str = buf;
176         break;
177     }
178     CORE_LOGF(eLOG_Note,
179               ("Server requires %s certificate for client authentication",
180                str));
181 
182     if (n_req_ca_dn > 0) {
183         int n;
184         CORE_LOGF(eLOG_Note,
185                   ("Server's %d trusted certificate authorit%s:",
186                    n_req_ca_dn, n_req_ca_dn == 1 ? "y" : "ies"));
187         for (n = 0;  n < n_req_ca_dn;  ++n) {
188             size_t len = sizeof(buf);
189             int    err = gnutls_x509_rdn_get(&req_ca_dn[n], buf, &len);
190             if (err >= 0) {
191                 CORE_LOGF(eLOG_Note,
192                           ("CA[%d]: %s", n + 1, buf));
193             } else {
194                 CORE_LOGF(eLOG_Error,
195                           ("CA[%d]: requires %lu bytes: %s",
196                            n + 1, (unsigned long) len, gnutls_strerror(err)));
197             }
198         }
199     } else {
200         CORE_LOG(eLOG_Note,
201                  "Server does not specify any trusted CAs");
202     }
203 
204     if (n_algos) {
205         int n;
206         CORE_LOGF(eLOG_Note,
207                   ("Server supports %d signature algorithm%s:",
208                    n_algos, &"s"[n_algos == 1]));
209         for (n = 0;  n < n_algos;  ++n) {
210             str = gnutls_pk_algorithm_get_name(algos[n]);
211             CORE_LOGF(eLOG_Note,
212                       ("PK algorithm[%d]: %s(%d)",
213                        n + 1, !str ? "<NULL>" : *str ? str : "\"\"",
214                        (int) algos[n]));
215         }
216     }
217 
218     if (type == GNUTLS_CRT_X509) {
219         *pcert   = &pcrt;
220         *n_pcert = 1;
221         *pkey    = key;
222     } else*/ {
223         *pcert   = 0;
224         *n_pcert = 0;
225         *pkey    = 0;
226     }
227     return 0;
228 }
229 #endif/*0*/
230 
231 #endif /*HAVE_LIBGNUTLS*/
232 
233 
234 int main(int argc, char* argv[])
235 {
236 #ifdef HAVE_LIBGNUTLS
237     gnutls_certificate_credentials_t xcred = 0;
238 #endif /*HAVE_LIBGNUTLS*/
239     CONNECTOR     connector;
240     SConnNetInfo* net_info;
241     char          blk[250];
242     EIO_Status    status;
243     THTTP_Flags   flags;
244     NCBI_CRED     cred;
245     CONN          conn;
246     FILE*         fp;
247     time_t        t;
248     size_t        n;
249 
250     CORE_SetLOGFormatFlags(fLOG_None          | fLOG_Level   |
251                            fLOG_OmitNoteLevel | fLOG_DateTime);
252     CORE_SetLOGFILE(stderr, 0/*false*/);
253 
254     if (argc < 2  ||  !*argv[1])
255         CORE_LOG(eLOG_Fatal, "URL has to be supplied on the command line");
256     if (argc > 3)
257         CORE_LOG(eLOG_Fatal, "Cannot take more than 2 arguments");
258     if (argc < 3)
259         fp = 0;
260     else if (!(fp = strcmp(argv[2], "-") == 0 ? stdin : fopen(argv[2], "rb")))
261         CORE_LOGF_ERRNO(eLOG_Error, errno, ("Cannot open \"%s\"", argv[2]));
262 
263     ConnNetInfo_GetValue(0, "RECONNECT", blk, 32, 0);
264     if (ConnNetInfo_Boolean(blk)) {
265         CORE_LOG(eLOG_Note, "Reconnect mode acknowledged");
266         flags = fHTTP_AutoReconnect;
267     } else
268         flags = 0;
269 
270     CORE_LOG(eLOG_Note, "Creating network info structure");
271     if (!(net_info = ConnNetInfo_Create(0)))
272         CORE_LOG(eLOG_Fatal, "Cannot create network info structure");
273     assert(!net_info->credentials);
274 
275     CORE_LOGF(eLOG_Note, ("Parsing URL \"%s\"", argv[1]));
276     if (!ConnNetInfo_ParseURL(net_info, argv[1]))
277         CORE_LOG(eLOG_Fatal, "Cannot parse URL");
278 
279     ConnNetInfo_GetValue(0, "HTTP11", blk, 32, 0);
280     if (ConnNetInfo_Boolean(blk))
281         net_info->version = 1;
282 
283     ConnNetInfo_GetValue(0, "WRITETHRU", blk, 32, 0);
284     if (net_info->version  &&  ConnNetInfo_Boolean(blk)) {
285         CORE_LOG(eLOG_Note, "HTTP/1.1 Write-through mode acknowledged");
286         flags |= fHTTP_WriteThru;
287     }
288 
289     cred = 0;
290     ConnNetInfo_GetValue(0, "USESSL", blk, 32, 0);
291     if (net_info->scheme == eURL_Https  &&  ConnNetInfo_Boolean(blk)) {
292 #ifdef HAVE_LIBGNUTLS
293         int err;
294         char type[40];
295         const char* file, *pass;
296         status = SOCK_SetupSSLEx(NcbiSetupGnuTls);
297         CORE_LOGF(eLOG_Note, ("SSL request acknowledged: %s",
298                               IO_StatusStr(status)));
299         if (!ConnNetInfo_GetValue(0, GNUTLS_PKCS12_TYPE, type, sizeof(type), 0)
300             ||  !*type) {
301             strncpy0(type, "PEM", sizeof(type));
302         }
303         pass = x_GetPkcs12Pass(GNUTLS_PKCS12_PASS, blk, sizeof(blk));
304         file = ConnNetInfo_GetValue(0, GNUTLS_PKCS12_FILE,
305                                     blk, sizeof(blk)/2 - 1, 0);
306         if (file  &&  *file  &&  access(file, R_OK) == 0) {
307             if ((err = gnutls_certificate_allocate_credentials(&xcred)) != 0 ||
308                 (err = gnutls_certificate_set_x509_simple_pkcs12_file
309                  (xcred, file, strcasecmp(type, "PEM") == 0
310                   ? GNUTLS_X509_FMT_PEM
311                   : GNUTLS_X509_FMT_DER, pass)) != 0) {
312                 CORE_LOGF(eLOG_Fatal,
313                           ("Cannot load PKCS#12 %s credentials from"
314                            " \"%s\": %s", type, file, gnutls_strerror(err)));
315             }
316         } else if (net_info->debug_printout == eDebugPrintout_Data) {
317             /* We don't have to create empty cert credentials, in general
318              * (as the ncbi_gnutls shim does that for us); but if we want
319              * callbacks, then they can only be associated with the
320              * credentials handle (gnutls design???) so here it goes: */
321             if ((err = gnutls_certificate_allocate_credentials(&xcred)) != 0) {
322                 CORE_LOGF(eLOG_Critical,
323                           ("Cannot allocate certificate credentials: %s",
324                            gnutls_strerror(err)));
325                 xcred = 0;
326             }
327             file = 0;
328         } else
329             file = 0;
330         if (xcred) {
331             if (!(cred = NcbiCredGnuTls(xcred)))
332                 CORE_LOG_ERRNO(eLOG_Fatal, errno, "Cannot create NCBI_CRED");
333             net_info->credentials = cred;
334             if (!file)
335                 CORE_LOG(eLOG_Note, "Debug certificate credentials set");
336             else {
337                 CORE_LOGF(eLOG_Note, ("PKCS#12 %s credentials loaded from"
338                                       " \"%s\"", type, file));
339             }
340             if (net_info->debug_printout == eDebugPrintout_Data) {
341 #if LIBGNUTLS_VERSION_NUMBER >= 0x021000
342                 gnutls_certificate_set_verify_function(xcred,
343                                                        x_CertVfyCB);
344 #endif /*LIBGNUTLS_VERSION_NUMBER>=2.10.0*/
345 #if 0
346                 gnutls_certificate_set_retrieve_function2(xcred,
347                                                           x_CertRtrCB);
348 #endif
349             }
350         }
351 #else
352         CORE_LOG(eLOG_Warning, "SSL requested but may not be supported");
353 #endif /*HAVE_LIBGNUTLS*/
354     }
355 
356     CORE_LOGF(eLOG_Note, ("Creating HTTP%s connector",
357                           &"S"[net_info->scheme != eURL_Https]));
358     if (!(connector = HTTP_CreateConnector(net_info, 0, flags)))
359         CORE_LOG(eLOG_Fatal, "Cannot create HTTP connector");
360     /* Could have destroyed net_info at this point here if we did not use the
361      * timeout off of it below, so at least unlink the credentials, if any...
362      */
363     net_info->credentials = 0;
364 
365     CORE_LOG(eLOG_Note, "Creating connection");
366     if (CONN_Create(connector, &conn) != eIO_Success)
367         CORE_LOG(eLOG_Fatal, "Cannot create connection");
368     CONN_SetTimeout(conn, eIO_Open,      net_info->timeout);
369     CONN_SetTimeout(conn, eIO_ReadWrite, net_info->timeout);
370     while (fp  &&  !feof(fp)) {
371         static char block[20000/*to force non-default BUF allocations*/];
372         n = fread(block, 1, sizeof(block), fp);
373         status = CONN_Write(conn, block, n, &n, eIO_WritePersist);
374         if (status != eIO_Success)
375             CORE_LOGF(eLOG_Fatal, ("Write error: %s", IO_StatusStr(status)));
376     }
377     if (fp)
378         fclose(fp);
379 
380     t = time(0);
381     do {
382         status = CONN_Wait(conn, eIO_Read, net_info->timeout);
383         if (status == eIO_Timeout) {
384             if  ((net_info->timeout  &&
385                   (net_info->timeout->sec | net_info->timeout->usec))
386                  ||  (unsigned long)(time(0) - t) > DEF_CONN_TIMEOUT) {
387                 CORE_LOG(eLOG_Fatal, "Timed out");
388             }
389 #if defined(NCBI_OS_UNIX)  &&  defined(HAVE_USLEEP)
390             usleep(500);
391 #endif /*NCBI_OS_UNIX && HAVE_USLEEP*/
392             continue;
393         }
394         if (status == eIO_Success)
395             status  = CONN_ReadLine(conn, blk, sizeof(blk), &n);
396         else
397             n = 0;
398         if (n  ||  status == eIO_Success) {
399             connector = 0/*as bool, visited*/;
400             if (n)
401                 fwrite(blk, 1, n, stdout);
402             if (status != eIO_Timeout)
403                 fputc('\n', stdout);
404             if (status != eIO_Timeout  ||  n)
405                 fflush(stdout);
406             if (n == sizeof(blk)  &&  status != eIO_Closed)
407                 CORE_LOGF(eLOG_Warning, ("Line too long, continuing..."));
408         }
409         if (status == eIO_Timeout)
410             continue;
411         if (status != eIO_Success  &&  (status != eIO_Closed  ||  connector))
412             CORE_LOGF(eLOG_Fatal, ("Read error: %s", IO_StatusStr(status)));
413 
414     } while (status == eIO_Success  ||  status == eIO_Timeout);
415 
416     ConnNetInfo_Destroy(net_info); /* done using the timeout field */
417 
418     CORE_LOG(eLOG_Note, "Closing connection");
419     CONN_Close(conn);  /* this makes sure credentials are no longer accessed */
420 
421     if (cred)
422         free(cred);
423 #ifdef HAVE_LIBGNUTLS
424     if (xcred)
425         gnutls_certificate_free_credentials(xcred);
426 #endif /*HAVE_LIBGNUTLS*/
427 
428     CORE_LOG(eLOG_Note, "Completed successfully");
429     CORE_SetLOG(0);
430     return 0;
431 }
432 

source navigation ]   [ diff markup ]   [ identifier search ]   [ freetext search ]   [ file search ]  

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.