t-http.c 10.1 KB
Newer Older
Werner Koch's avatar
Werner Koch committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/* t-http.c
 * Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006, 2009, 2010,
 *               2011 Free Software Foundation, Inc.
 * Copyright (C) 2014 Werner Koch
 *
 * This file is part of GnuPG.
 *
 * This file is free software; you can redistribute it and/or modify
 * it under the terms of either
 *
 *   - the GNU Lesser General Public License as published by the Free
 *     Software Foundation; either version 3 of the License, or (at
 *     your option) any later version.
 *
 * or
 *
 *   - the GNU General Public License as published by the Free
 *     Software Foundation; either version 2 of the License, or (at
 *     your option) any later version.
 *
 * or both in parallel, as here.
 *
 * This file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
29
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
Werner Koch's avatar
Werner Koch committed
30 31 32 33 34 35 36 37 38
 */

#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
39
#include <assuan.h>
Werner Koch's avatar
Werner Koch committed
40 41 42 43 44 45

#include "util.h"
#include "logging.h"
#include "http.h"


46 47 48
#if HTTP_USE_NTBTLS
# include <ntbtls.h>
#elif HTTP_USE_GNUTLS
Werner Koch's avatar
Werner Koch committed
49 50 51
# include <gnutls/gnutls.h>  /* For init, logging, and deinit.  */
#endif /*HTTP_USE_GNUTLS*/

52
#define PGM "t-http"
Werner Koch's avatar
Werner Koch committed
53

54 55 56
static int verbose;
static int debug;
static int no_verify;
Werner Koch's avatar
Werner Koch committed
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

/* static void */
/* read_dh_params (const char *fname) */
/* { */
/*   gpg_error_t err; */
/*   int rc; */
/*   FILE *fp; */
/*   struct stat st; */
/*   char *buf; */
/*   size_t buflen; */
/*   gnutls_datum_t datum; */

/*   fp = fopen (fname, "rb"); */
/*   if (!fp) */
/*     { */
/*       err = gpg_error_from_syserror (); */
/*       log_fatal ("can't open '%s': %s\n", fname, gpg_strerror (err)); */
/*     } */

/*   if (fstat (fileno(fp), &st)) */
/*     { */
/*       err = gpg_error_from_syserror (); */
/*       log_fatal ("can't stat '%s': %s\n", fname, gpg_strerror (err)); */
/*     } */

/*   buflen = st.st_size; */
/*   buf = xmalloc (buflen+1); */
/*   if (fread (buf, buflen, 1, fp) != 1) */
/*     { */
/*       err = gpg_error_from_syserror (); */
/*       log_fatal ("error reading '%s': %s\n", fname, gpg_strerror (err)); */
/*     } */
/*   fclose (fp); */

/*   datum.size = buflen; */
/*   datum.data = buf; */

/*   rc = gnutls_dh_params_import_pkcs3 (dh_params, &datum, GNUTLS_X509_FMT_PEM); */
/*   if (rc < 0) */
/*     log_fatal ("gnutls_dh_param_import failed: %s\n", gnutls_strerror (rc)); */

/*   xfree (buf); */
/* } */



103
#if HTTP_USE_GNUTLS
Werner Koch's avatar
Werner Koch committed
104 105 106 107 108
static gpg_error_t
verify_callback (http_t hd, http_session_t session, int reserved)
{
  (void)hd;
  (void)reserved;
109
  return no_verify? 0 : http_verify_server_credentials (session);
Werner Koch's avatar
Werner Koch committed
110
}
111
#endif
Werner Koch's avatar
Werner Koch committed
112

113
#if HTTP_USE_GNUTLS
Werner Koch's avatar
Werner Koch committed
114 115 116 117 118
static void
my_gnutls_log (int level, const char *text)
{
  fprintf (stderr, "gnutls:L%d: %s", level, text);
}
119
#endif
Werner Koch's avatar
Werner Koch committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142

/* Prepend FNAME with the srcdir environment variable's value and
   return an allocated filename. */
static char *
prepend_srcdir (const char *fname)
{
  static const char *srcdir;
  char *result;

  if (!srcdir && !(srcdir = getenv ("srcdir")))
    srcdir = ".";

  result = xmalloc (strlen (srcdir) + 1 + strlen (fname) + 1);
  strcpy (result, srcdir);
  strcat (result, "/");
  strcat (result, fname);
  return result;
}


int
main (int argc, char **argv)
{
143
  int last_argc = -1;
Werner Koch's avatar
Werner Koch committed
144 145 146 147 148 149
  gpg_error_t err;
  int rc;
  parsed_uri_t uri;
  uri_tuple_t r;
  http_t hd;
  int c;
150 151
  unsigned int my_http_flags = 0;
  int no_out = 0;
152
  int tls_dbg = 0;
153
  const char *cafile = NULL;
Werner Koch's avatar
Werner Koch committed
154 155
  http_session_t session = NULL;

156
  gpgrt_init ();
157
  log_set_prefix (PGM, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID);
158 159 160
  if (argc)
    { argc--; argv++; }
  while (argc && last_argc != argc )
Werner Koch's avatar
Werner Koch committed
161
    {
162 163 164 165 166 167 168 169 170 171
      last_argc = argc;
      if (!strcmp (*argv, "--"))
        {
          argc--; argv++;
          break;
        }
      else if (!strcmp (*argv, "--help"))
        {
          fputs ("usage: " PGM " URL\n"
                 "Options:\n"
172 173 174 175 176 177
                 "  --verbose         print timings etc.\n"
                 "  --debug           flyswatter\n"
                 "  --gnutls-debug N  use GNUTLS debug level N\n"
                 "  --cacert FNAME    expect CA certificate in file FNAME\n"
                 "  --no-verify       do not verify the certificate\n"
                 "  --force-tls       use HTTP_FLAG_FORCE_TLS\n"
178
                 "  --force-tor       use HTTP_FLAG_FORCE_TOR\n"
179
                 "  --no-out          do not print the content\n",
180 181 182 183 184 185 186 187 188 189 190 191 192 193
                 stdout);
          exit (0);
        }
      else if (!strcmp (*argv, "--verbose"))
        {
          verbose++;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--debug"))
        {
          verbose += 2;
          debug++;
          argc--; argv++;
        }
194 195 196 197 198 199 200 201 202
      else if (!strcmp (*argv, "--gnutls-debug"))
        {
          argc--; argv++;
          if (argc)
            {
              tls_dbg = atoi (*argv);
              argc--; argv++;
            }
        }
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
      else if (!strcmp (*argv, "--cacert"))
        {
          argc--; argv++;
          if (argc)
            {
              cafile = *argv;
              argc--; argv++;
            }
        }
      else if (!strcmp (*argv, "--no-verify"))
        {
          no_verify = 1;
          argc--; argv++;
        }
      else if (!strcmp (*argv, "--force-tls"))
        {
          my_http_flags |= HTTP_FLAG_FORCE_TLS;
          argc--; argv++;
        }
222 223 224 225 226
      else if (!strcmp (*argv, "--force-tor"))
        {
          my_http_flags |= HTTP_FLAG_FORCE_TOR;
          argc--; argv++;
        }
227 228 229 230 231 232 233 234 235 236 237 238 239
      else if (!strcmp (*argv, "--no-out"))
        {
          no_out = 1;
          argc--; argv++;
        }
      else if (!strncmp (*argv, "--", 2))
        {
          fprintf (stderr, PGM ": unknown option '%s'\n", *argv);
          exit (1);
        }
    }
  if (argc != 1)
    {
240
      fprintf (stderr, PGM ": no or too many URLS given\n");
241
      exit (1);
Werner Koch's avatar
Werner Koch committed
242
    }
243 244 245

  if (!cafile)
    cafile = prepend_srcdir ("tls-ca.pem");
Werner Koch's avatar
Werner Koch committed
246

247 248 249
  /* http.c makes use of the assuan socket wrapper.  */
  assuan_sock_init ();

250 251 252 253 254 255 256 257
#if HTTP_USE_NTBTLS

  (void)err;

  ntbtls_set_debug (tls_dbg, NULL, NULL);

#elif HTTP_USE_GNUTLS

Werner Koch's avatar
Werner Koch committed
258 259 260 261 262
  rc = gnutls_global_init ();
  if (rc)
    log_error ("gnutls_global_init failed: %s\n", gnutls_strerror (rc));

  http_register_tls_callback (verify_callback);
263
  http_register_tls_ca (cafile);
Werner Koch's avatar
Werner Koch committed
264

265
  err = http_session_new (&session, NULL, NULL, HTTP_FLAG_TRUST_DEF);
Werner Koch's avatar
Werner Koch committed
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
  if (err)
    log_error ("http_session_new failed: %s\n", gpg_strerror (err));

  /* rc = gnutls_dh_params_init(&dh_params); */
  /* if (rc) */
  /*   log_error ("gnutls_dh_params_init failed: %s\n", gnutls_strerror (rc)); */
  /* read_dh_params ("dh_param.pem"); */

  /* rc = gnutls_certificate_set_x509_trust_file */
  /*   (certcred, "ca.pem", GNUTLS_X509_FMT_PEM); */
  /* if (rc) */
  /*   log_error ("gnutls_certificate_set_x509_trust_file failed: %s\n", */
  /*              gnutls_strerror (rc)); */

  /* gnutls_certificate_set_dh_params (certcred, dh_params); */

  gnutls_global_set_log_function (my_gnutls_log);
283 284
  if (tls_dbg)
    gnutls_global_set_log_level (tls_dbg);
Werner Koch's avatar
Werner Koch committed
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325

#endif /*HTTP_USE_GNUTLS*/

  rc = http_parse_uri (&uri, *argv, 1);
  if (rc)
    {
      log_error ("'%s': %s\n", *argv, gpg_strerror (rc));
      return 1;
    }

  printf ("Scheme: %s\n", uri->scheme);
  if (uri->opaque)
    printf ("Value : %s\n", uri->path);
  else
    {
      printf ("Auth  : %s\n", uri->auth? uri->auth:"[none]");
      printf ("Host  : %s\n", uri->host);
      printf ("Port  : %u\n", uri->port);
      printf ("Path  : %s\n", uri->path);
      for (r = uri->params; r; r = r->next)
        {
          printf ("Params: %s", r->name);
          if (!r->no_value)
            {
              printf ("=%s", r->value);
              if (strlen (r->value) != r->valuelen)
                printf (" [real length=%d]", (int) r->valuelen);
            }
          putchar ('\n');
        }
      for (r = uri->query; r; r = r->next)
        {
          printf ("Query : %s", r->name);
          if (!r->no_value)
            {
              printf ("=%s", r->value);
              if (strlen (r->value) != r->valuelen)
                printf (" [real length=%d]", (int) r->valuelen);
            }
          putchar ('\n');
        }
326 327 328 329 330
      printf ("Flags :%s%s%s%s\n",
              uri->is_http? " http":"",
              uri->opaque?  " opaque":"",
              uri->v6lit?   " v6lit":"",
              uri->onion?   " onion":"");
331 332 333
      printf ("TLS   : %s\n",
              uri->use_tls? "yes":
              (my_http_flags&HTTP_FLAG_FORCE_TLS)? "forced" : "no");
334 335
      printf ("Tor   : %s\n",
              (my_http_flags&HTTP_FLAG_FORCE_TOR)? "yes" : "no");
336

Werner Koch's avatar
Werner Koch committed
337
    }
338
  fflush (stdout);
Werner Koch's avatar
Werner Koch committed
339 340 341
  http_release_parsed_uri (uri);
  uri = NULL;

342 343
  rc = http_open_document (&hd, *argv, NULL, my_http_flags,
                           NULL, session, NULL, NULL);
Werner Koch's avatar
Werner Koch committed
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
  if (rc)
    {
      log_error ("can't get '%s': %s\n", *argv, gpg_strerror (rc));
      return 1;
    }
  log_info ("open_http_document succeeded; status=%u\n",
            http_get_status_code (hd));

  {
    const char **names;
    int i;

    names = http_get_header_names (hd);
    if (!names)
      log_fatal ("http_get_header_names failed: %s\n",
                 gpg_strerror (gpg_error_from_syserror ()));
    for (i = 0; names[i]; i++)
      printf ("HDR: %s: %s\n", names[i], http_get_header (hd, names[i]));
    xfree (names);
  }
364
  fflush (stdout);
Werner Koch's avatar
Werner Koch committed
365 366 367 368 369 370 371 372

  switch (http_get_status_code (hd))
    {
    case 200:
    case 400:
    case 401:
    case 403:
    case 404:
373 374 375 376 377 378 379 380 381 382
      {
        unsigned long count = 0;
        while ((c = es_getc (http_get_read_ptr (hd))) != EOF)
          {
            count++;
            if (!no_out)
              putchar (c);
          }
        log_info ("Received bytes: %lu\n", count);
      }
Werner Koch's avatar
Werner Koch committed
383 384 385
      break;
    case 301:
    case 302:
386 387
    case 307:
      log_info ("Redirected to: %s\n", http_get_header (hd, "Location"));
Werner Koch's avatar
Werner Koch committed
388 389 390 391 392 393 394 395 396 397 398
      break;
    }
  http_close (hd, 0);

  http_session_release (session);
#ifdef HTTP_USE_GNUTLS
  gnutls_global_deinit ();
#endif /*HTTP_USE_GNUTLS*/

  return 0;
}