Example #1
0
bud_error_t bud_config_drop_privileges(bud_config_t* config) {
#ifndef _WIN32
  if (config->user != NULL) {
    struct passwd* p;

    p = getpwnam(config->user);
    if (p == NULL)
      return bud_error_dstr(kBudErrInvalidUser, config->user);

    if (setgid(p->pw_gid) != 0)
      return bud_error_num(kBudErrSetgid, errno);
    if (setuid(p->pw_uid) != 0)
      return bud_error_num(kBudErrSetuid, errno);
  } else if (config->group != NULL) {
    struct group* g;

    g = getgrnam(config->group);
    if (g == NULL)
      return bud_error_dstr(kBudErrInvalidGroup, config->group);

    if (setgid(g->gr_gid) != 0)
      return bud_error_num(kBudErrSetgid, errno);
  }
#endif  /* !_WIN32 */

  return bud_ok();
}
Example #2
0
void bud_client_shutdown_cb(uv_shutdown_t* req, int status) {
  bud_client_t* client;
  bud_client_side_t* side;

  client = req->data;
  if (req == &client->frontend.shutdown_req)
    side = &client->frontend;
  else
    side = &client->backend;

  side->shutdown = kBudProgressDone;

  if (status == UV_ECANCELED)
    return;

  if (status == 0)
    DBG_LN(side, "shutdown cb");

  if (status != 0) {
    bud_client_close(client,
                     bud_client_error(bud_error_num(kBudErrClientShutdownCb,
                                                    status),
                                      side));

  /* If either closing, or shutdown both sides - kill both sockets! */
  } else if (side->close == kBudProgressRunning ||
             client->frontend.shutdown == client->backend.shutdown ||
             (side == &client->frontend &&
                  !client->config->frontend.allow_half_open)) {
    bud_client_close(client, bud_client_ok(side));
  }
}
Example #3
0
bud_client_error_t bud_client_prepend_proxyline(bud_client_t* client) {
  int r;
  const char* family;
  char proxyline[256];

  if (client->family == AF_INET) {
    family = "TCP4";
  } else if (client->family == AF_INET6) {
    family = "TCP6";
  } else {
    r = -1;
    goto fatal;
  }

  r = snprintf(proxyline,
               sizeof(proxyline),
               client->config->proxyline_fmt,
               family,
               client->host,
               ntohs(client->port));
  ASSERT(0 <= r && r < (int) sizeof(proxyline), "Client proxyline overflow");

  r = ringbuffer_insert(&client->backend.output,
                        0,
                        proxyline,
                        (size_t) r);
  if (r != 0)
    goto fatal;

  return bud_client_ok(&client->backend);

fatal:
  return bud_client_error(bud_error_num(kBudErrClientProxyline, r),
                          &client->backend);
}
Example #4
0
bud_client_error_t bud_client_throttle(bud_client_t* client,
                                       bud_client_side_t* side,
                                       ringbuffer* buf) {
  int err;
  bud_client_side_t* opposite;

  if (!ringbuffer_is_full(buf))
    return bud_client_ok(side);

  opposite = side == &client->frontend ? &client->backend : &client->frontend;
  if (opposite->reading != kBudProgressRunning)
    goto done;

  DBG(opposite, "throttle, buffer full: %ld", ringbuffer_size(buf));

  err = uv_read_stop((uv_stream_t*) &opposite->tcp);
  if (err != 0) {
    NOTICE(opposite,
           "uv_read_stop failed: %d - \"%s\"",
           err,
           uv_strerror(err));
    return bud_client_error(bud_error_num(kBudErrClientReadStop, err), side);
  }
  opposite->reading = kBudProgressNone;

done:
  return bud_client_error(bud_error(kBudErrClientThrottle), side);
}
Example #5
0
bud_client_error_t bud_client_backend_out(bud_client_t* client) {
  int read;
  int err;
  size_t avail;
  char* out;
  bud_client_error_t cerr;

  /* If buffer is full - stop reading */
  cerr = bud_client_throttle(client,
                             &client->backend,
                             &client->backend.output);
  if (cerr.err.code == kBudErrClientThrottle)
    return bud_client_ok(&client->frontend);
  else if (!bud_is_ok(cerr.err))
    return cerr;

  do {
    avail = 0;
    out = ringbuffer_write_ptr(&client->backend.output, &avail);
    read = SSL_read(client->ssl, out, avail);
    DBG(&client->frontend, "SSL_read() => %d", read);
    if (read > 0) {
      ringbuffer_write_append(&client->backend.output, read);
      if (client->selected_backend->xforward &&
          !bud_client_xforward_done(client)) {
        cerr = bud_client_prepend_xforward(client);
        if (!bud_is_ok(cerr.err))
          return cerr;
      }

      cerr = bud_client_send(client, &client->backend);
      if (!bud_is_ok(cerr.err))
        return cerr;
    }

    /* info_cb() has closed front-end */
    if (client->close != kBudProgressNone)
      return bud_client_ok(&client->frontend);
  } while (read > 0);

  if (read > 0)
    goto success;

  err = SSL_get_error(client->ssl, read);
  if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
    goto success;

  /* Close-notify, most likely */
  if (err == SSL_ERROR_ZERO_RETURN)
    return bud_client_shutdown(client, &client->backend);

  return bud_client_error(bud_error_num(kBudErrClientSSLRead, err),
                          &client->frontend);

success:
  return bud_client_ok(&client->backend);
}
Example #6
0
File: config.c Project: indutny/bud
bud_error_t bud_config_load_backend(bud_config_t* config,
                                    JSON_Object* obj,
                                    bud_config_backend_t* backend,
                                    bud_hashmap_t* map,
                                    unsigned int* ext_count) {
  bud_error_t err;
  JSON_Value* val;
  const char* external;
  int r;

  bud_config_load_addr(obj, (bud_config_addr_t*) backend);
  backend->config = config;
  backend->xforward = -1;

  val = json_object_get_value(obj, "proxyline");
  if (json_value_get_type(val) == JSONString) {
    const char* pline;

    pline = json_value_get_string(val);
    if (strcmp(pline, "haproxy") == 0)
      backend->proxyline = kBudProxylineHAProxy;
    else if (strcmp(pline, "json") == 0)
      backend->proxyline = kBudProxylineJSON;
    else
      return bud_error_dstr(kBudErrProxyline, pline);
  } else {
    backend->proxyline = val != NULL && json_value_get_boolean(val) ?
        kBudProxylineHAProxy :
        kBudProxylineNone;
  }

  val = json_object_get_value(obj, "x-forward");
  if (val != NULL)
    backend->xforward = json_value_get_boolean(val);

  /* Set defaults here to use them in sni.c */
  bud_config_set_backend_defaults(backend);

  r = bud_config_str_to_addr(backend->host, backend->port, &backend->addr);
  if (r != 0)
    return bud_error_num(kBudErrPton, r);

  external = json_object_get_string(obj, "external");
  if (external == NULL)
    return bud_ok();

  /* Insert backend into a hashmap */
  err = bud_hashmap_insert(map, external, strlen(external), backend);
  if (!bud_is_ok(err))
    return err;

  (*ext_count)++;

  return bud_ok();
}
Example #7
0
bud_client_error_t bud_client_shutdown(bud_client_t* client,
                                       bud_client_side_t* side) {
  int r;
  bud_client_error_t cerr;

  /* Ignore if already shutdown or destroyed */
  if (side->shutdown || client->close == kBudProgressDone)
    return bud_client_ok(side);

  /* Do not shutdown not-connected socket */
  if (side == &client->backend && client->connect != kBudProgressDone)
    return bud_client_ok(side);

  side->shutdown = kBudProgressNone;

  /* Try cycling data to figure out if there is still something to send */
  cerr = bud_client_cycle(client);
  if (!bud_is_ok(cerr.err))
    return cerr;

  /* Not empty, send everything first */
  if (!ringbuffer_is_empty(&side->output)) {
    side->shutdown = kBudProgressRunning;
    return bud_client_ok(side);
  }

  DBG_LN(side, "shutdown");

  if (side == &client->frontend) {
    if (SSL_shutdown(client->ssl) != 1)
      SSL_shutdown(client->ssl);

    /* Try writing close_notify */
    cerr = bud_client_send(client, &client->frontend);
    if (!bud_is_ok(cerr.err))
      goto fatal;
  }

  side->shutdown_req.data = client;
  r = uv_shutdown(&side->shutdown_req,
                  (uv_stream_t*) &side->tcp,
                  bud_client_shutdown_cb);
  if (r != 0) {
    cerr = bud_client_error(bud_error_num(kBudErrClientShutdown, r), side);
  } else {
    cerr = bud_client_ok(side);
    side->shutdown = kBudProgressRunning;
  }

fatal:
  side->shutdown = 1;

  return cerr;
}
Example #8
0
bud_error_t bud_server_new(bud_config_t* config, bud_config_addr_t* addr) {
  int r;
  bud_error_t err;
  bud_server_t* server;

  server = calloc(1, sizeof(*server));

  server->config = config;

  /* Initialize tcp handle */
  r = uv_tcp_init(config->loop, &server->tcp);
  if (r != 0) {
    err = bud_error_num(kBudErrTcpServerInit, r);
    goto failed_tcp_init;
  }

  r = uv_tcp_bind(&server->tcp, (struct sockaddr*) &addr->addr, 0);
  if (r != 0) {
    err = bud_error_num(kBudErrTcpServerBind, r);
    goto failed_bind;
  }

  r = uv_listen((uv_stream_t*) &server->tcp, 256, bud_server_connection_cb);
  if (r != 0) {
    err = bud_error_num(kBudErrServerListen, r);
    goto failed_bind;
  }

  server->prev = config->server;
  config->server = server;
  return bud_ok();

failed_bind:
  uv_close((uv_handle_t*) &server->tcp, bud_server_close_cb);
  return err;

failed_tcp_init:
  free(server);

  return err;
}
Example #9
0
bud_client_error_t bud_client_fill_host(bud_client_t* client,
                                        bud_client_host_t* host) {
  int r;
  struct sockaddr_storage storage;
  int storage_size;
  struct sockaddr_in* addr;
  struct sockaddr_in6* addr6;

  storage_size = sizeof(storage);
  if (host == &client->remote) {
    r = uv_tcp_getpeername(&client->frontend.tcp,
                           (struct sockaddr*) &storage,
                           &storage_size);
  } else {
    r = uv_tcp_getsockname(&client->frontend.tcp,
                           (struct sockaddr*) &storage,
                           &storage_size);
  }
  if (r != 0)
    goto fatal;

  addr = (struct sockaddr_in*) &storage;
  addr6 = (struct sockaddr_in6*) &storage;
  host->family = storage.ss_family;
  if (storage.ss_family == AF_INET) {
    host->port = addr->sin_port;
    r = uv_inet_ntop(AF_INET,
                     &addr->sin_addr,
                     host->host,
                     sizeof(host->host));
  } else if (storage.ss_family == AF_INET6) {
    host->port = addr6->sin6_port;
    r = uv_inet_ntop(AF_INET6,
                     &addr6->sin6_addr,
                     host->host,
                     sizeof(host->host));
  } else {
    r = -1;
    goto fatal;
  }

  if (r != 0)
    goto fatal;

  host->host_len = strlen(host->host);

  return bud_client_ok(&client->backend);

fatal:
  return bud_client_error(bud_error_num(kBudErrClientProxyline, r),
                          &client->backend);
}
Example #10
0
void bud_client_read_cb(uv_stream_t* stream,
                        ssize_t nread,
                        const uv_buf_t* buf) {
  int r;
  bud_client_t* client;
  bud_client_side_t* side;
  bud_client_side_t* opposite;
  bud_client_error_t cerr;

  client = stream->data;
  side = bud_client_side_by_tcp(client, (uv_tcp_t*) stream);

  /* Commit data if there was no error */
  r = 0;
  if (nread >= 0)
    r = ringbuffer_write_append(&side->input, nread);

  DBG(side, "after read_cb() => %d", nread);

  /* Handle EOF */
  if (nread == UV_EOF) {
    side->reading = kBudProgressDone;

    /* Shutdown opposite side */
    opposite = side == &client->frontend ? &client->backend : &client->frontend;
    cerr = bud_client_shutdown(client, opposite);
    if (!bud_is_ok(cerr.err))
      goto done;
  }

  /* Try writing out data anyway */
  cerr = bud_client_cycle(client);
  if (!bud_is_ok(cerr.err))
    goto done;

  if ((r != 0 || nread < 0) && nread != UV_EOF) {
    if (nread < 0)
      cerr = bud_client_error(bud_error_num(kBudErrClientReadCb, nread), side);
    else
      cerr = bud_client_error(bud_error(kBudErrClientWriteAppend), side);

    /* Unrecoverable socket error, close */
    return bud_client_close(client, cerr);
  }

  /* If buffer is full - stop reading */
  cerr = bud_client_throttle(client, side, &side->input);

done:
  if (!bud_is_ok(cerr.err) && cerr.err.code != kBudErrClientThrottle)
    bud_client_close(client, cerr);
}
Example #11
0
bud_client_error_t bud_client_backend_in(bud_client_t* client) {
  size_t size;
  int written;
  int err;
  bud_client_error_t cerr;

  written = 0;
  while (!ringbuffer_is_empty(&client->backend.input)) {
    char* data;

    data = ringbuffer_read_next(&client->backend.input, &size);
    written = SSL_write(client->ssl, data, size);
    DBG(&client->frontend, "SSL_write() => %d", written);
    DBG(&client->frontend,
        "frontend.output => %d",
        ringbuffer_size(&client->frontend.output));
    if (written < 0)
      break;

    ASSERT(written == (int) size, "SSL_write() did unexpected partial write");
    ringbuffer_read_skip(&client->backend.input, written);

    /* info_cb() has closed front-end */
    if (client->frontend.close != kBudProgressNone)
      return bud_client_ok(&client->backend);
  }

  cerr = bud_client_throttle(client,
                             &client->frontend,
                             &client->frontend.output);
  if (!bud_is_ok(cerr.err) && cerr.err.code != kBudErrClientThrottle)
    return cerr;

  if (written >= 0)
    return bud_client_ok(&client->backend);

  err = SSL_get_error(client->ssl, written);
  if (err == SSL_ERROR_WANT_READ ||
      err == SSL_ERROR_WANT_WRITE ||
      err == SSL_ERROR_WANT_X509_LOOKUP) {
    return bud_client_ok(&client->backend);
  }

  return bud_client_error(bud_error_num(kBudErrClientSSLWrite, err),
                          &client->backend);
}
Example #12
0
bud_config_t* bud_config_cli_load(uv_loop_t* loop,
                                  int argc,
                                  char** argv,
                                  bud_error_t* err) {
  int c;
  int r;
  int index;
  int is_daemon;
  int is_worker;
  size_t path_len;
  bud_config_t* config;

  struct option long_options[] = {
    { "version", 0, NULL, 'v' },
    { "config", 1, NULL, 'c' },
#ifndef _WIN32
    { "daemonize", 0, NULL, 'd' },
#endif  /* !_WIN32 */
    { "worker", 0, NULL, 1000 },
    { "default-config", 0, NULL, 1001 },
    { NULL, 0, NULL, 0 }
  };

  *err = bud_ok();
  config = NULL;
  is_daemon = 0;
  is_worker = 0;
  do {
    index = 0;
    c = getopt_long(argc, argv, "vc:d", long_options, &index);
    switch (c) {
      case 'v':
        bud_print_version();
        break;
      case 'c':
        config = bud_config_load(loop,optarg, err);
        if (config == NULL) {
          ASSERT(!bud_is_ok(*err), "Config load failed without error");
          c = -1;
          break;
        }
        if (is_daemon)
          config->is_daemon = 1;
        if (is_worker)
          config->is_worker = 1;
        break;
#ifndef _WIN32
      case 'd':
        is_daemon = 1;
        if (config != NULL)
          config->is_daemon = 1;
#endif  /* !_WIN32 */
        break;
      case 1000:
        is_worker = 1;
        if (config != NULL)
          config->is_worker = 1;
        break;
      case 1001:
        bud_config_print_default();
        c = -1;
        break;
      default:
        if (config == NULL)
          bud_print_help(argc, argv);
        c = -1;
        break;
    }
  } while (c != -1);

  if (config != NULL) {
    /* CLI options */
    config->argc = argc;
    config->argv = argv;

    /* Get executable path */
    path_len = sizeof(config->exepath);
    r = uv_exepath(config->exepath, &path_len);
    ASSERT(path_len < sizeof(config->exepath), "Exepath OOB");

    config->exepath[path_len] = 0;
    if (r != 0) {
      bud_config_free(config);
      config = NULL;
      *err = bud_error_num(kBudErrExePath, r);
    }

    /* Initialize config */
    *err = bud_config_init(config);
    if (!bud_is_ok(*err)) {
      bud_config_free(config);
      return NULL;
    }
  }

  return config;
}
Example #13
0
File: config.c Project: indutny/bud
bud_error_t bud_config_new(int argc, char** argv, bud_config_t** out) {
  bud_error_t err;
  bud_config_t* config;
  int i;
  int r;
  size_t path_len;
  int c;
  int index;
  int loaded;

  config = calloc(1, sizeof(*config));
  if (config == NULL)
    return bud_error_str(kBudErrNoMem, "bud_config_t");

  loaded = 0;
  do {
    index = 0;
    c = getopt_long(argc, argv, bud_long_flags, bud_long_options, &index);
    switch (c) {
      case 'v':
        bud_print_version();
        err = bud_error(kBudErrSkip);
        goto fatal;
#ifndef _WIN32
      case 'd':
        config->is_daemon = 1;
#endif  /* !_WIN32 */
        break;
      case 'p':
      case 'i':
      case 'c':
        if (loaded) {
          err = bud_error(kBudErrMultipleConfigs);
          goto fatal;
        }
        loaded = 1;

        if (c == 'p') {
          config->piped = 1;
          config->path = kPipedConfigPath;
        } else {
          config->piped = 0;
          config->path = optarg;
          config->inlined = c == 'i';
        }
        break;
      case 1000:
        config->is_worker = 1;
        break;
      case 1001:
        bud_config_print_default();
        err = bud_error(kBudErrSkip);
        goto fatal;
      default:
        if (loaded)
          break;

        bud_print_help(argc, argv);
        goto no_config;
    }
  } while (c != -1);

  if (!config->piped) {
    config->piped_index = -1;
  } else {
    /* get_opt does not provide the argc offset so must manually retrieve it */
    for (i = 0; i < argc; i++) {
      if (strcmp(argv[i], "--piped-config") == 0 ||
          strcmp(argv[i], "-p") == 0) {
        config->piped_index = i;
        break;
      }
    }
  }

  /* CLI options */
  config->argc = argc;
  config->argv = argv;

  /* Get executable path */
  path_len = sizeof(config->exepath);
  r = uv_exepath(config->exepath, &path_len);
  ASSERT(path_len < sizeof(config->exepath), "Exepath OOB");

  if (r != 0) {
    bud_config_free(config);
    config = NULL;
    return bud_error_num(kBudErrExePath, r);
  }

  err = bud_hashmap_init(&config->files.hashmap, kBudFileCacheSize);
  if (!bud_is_ok(err))
    goto fatal;

  *out = config;
  return bud_ok();

no_config:
  free(config);
  return bud_error(kBudErrNoConfig);

fatal:
  free(config);
  return err;
}
Example #14
0
bud_client_error_t bud_client_prepend_proxyline(bud_client_t* client) {
  int r;
  const char* family;
  char proxyline[1024];
  bud_config_proxyline_t type;

  /*
   * Client should both handshake and connect to backend in order to
   * be able to send proper proxyline
   */
  ASSERT(client->proxyline_waiting > 0, "Too many prepend proxyline calls");
  if (--client->proxyline_waiting != 0)
    return bud_client_ok();

  type = client->selected_backend->proxyline;

  if (type == kBudProxylineNone)
    return bud_client_ok();

  if (client->remote.family == AF_INET) {
    family = "TCP4";
  } else if (client->remote.family == AF_INET6) {
    family = "TCP6";
  } else {
    r = -1;
    goto fatal;
  }

  if (type == kBudProxylineHAProxy) {
    r = snprintf(proxyline,
                 sizeof(proxyline),
                 client->config->proxyline_fmt.haproxy,
                 family,
                 client->remote.host,
                 ntohs(client->remote.port));
  } else {
    const char* cn;

    cn = bud_client_get_peer_name(client);
    r = snprintf(proxyline,
                 sizeof(proxyline),
                 client->config->proxyline_fmt.json,
                 family,
                 client->remote.host,
                 ntohs(client->remote.port),
                 cn != NULL ? '"' : 'f',
                 cn != NULL ? cn : "als",
                 cn != NULL ? '"' : 'e');
  }
  ASSERT(0 <= r && r < (int) sizeof(proxyline), "Client proxyline overflow");

  r = ringbuffer_insert(&client->backend.output,
                        0,
                        proxyline,
                        (size_t) r);
  if (r != 0)
    goto fatal;

  return bud_client_ok(&client->backend);

fatal:
  return bud_client_error(bud_error_num(kBudErrClientProxyline, r),
                          &client->backend);
}
Example #15
0
bud_error_t bud_config_init(bud_config_t* config) {
  int i;
  int r;
  bud_context_t* ctx;
  bud_error_t err;
  const char* cert_file;
  const char* key_file;
  BIO* cert_bio;

  i = 0;

  /* Get addresses of frontend and backend */
  r = bud_config_str_to_addr(config->frontend.host,
                             config->frontend.port,
                             &config->frontend.addr);
  if (r != 0) {
    err = bud_error_num(kBudErrPton, r);
    goto fatal;
  }

  r = bud_config_str_to_addr(config->backend.host,
                             config->backend.port,
                             &config->backend.addr);
  if (r != 0) {
    err = bud_error_num(kBudErrPton, r);
    goto fatal;
  }

  /* Get indexes for SSL_set_ex_data()/SSL_get_ex_data() */
  if (kBudSSLClientIndex == -1) {
    kBudSSLClientIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    kBudSSLSNIIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    if (kBudSSLClientIndex == -1 || kBudSSLSNIIndex == -1)
      goto fatal;
  }

#ifndef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
  if (config->context_count != 0) {
    err = bud_error(kBudErrSNINotSupported);
    goto fatal;
  }
#endif  /* !SSL_CTRL_SET_TLSEXT_SERVERNAME_CB */

  /* Allocate workers */
  if (!config->is_worker) {
    config->workers = calloc(config->worker_count, sizeof(*config->workers));
    if (config->workers == NULL) {
      err = bud_error_str(kBudErrNoMem, "workers");
      goto fatal;
    }
  }

  /* Initialize logger */
  err = bud_logger_new(config);
  if (!bud_is_ok(err))
    goto fatal;

  if (config->is_worker || config->worker_count == 0) {
    /* Connect to SNI server */
    if (config->sni.enabled) {
      config->sni.pool = bud_http_pool_new(config,
                                           config->sni.host,
                                           config->sni.port,
                                           &err);
      if (config->sni.pool == NULL)
        goto fatal;
    }

    /* Connect to OCSP Stapling server */
    if (config->stapling.enabled) {
      config->stapling.pool = bud_http_pool_new(config,
                                                config->stapling.host,
                                                config->stapling.port,
                                                &err);
      if (config->stapling.pool == NULL)
        goto fatal;
    }
  }

  /* Load all contexts */
  for (i = 0; i < config->context_count + 1; i++) {
    ctx = &config->contexts[i];

    err = bud_config_new_ssl_ctx(config, ctx);
    if (!bud_is_ok(err))
      goto fatal;

    /* Default context */
    if (i == 0) {
      cert_file = config->frontend.cert_file;
      key_file = config->frontend.key_file;
    } else {
      cert_file = ctx->cert_file;
      key_file = ctx->key_file;
    }

    cert_bio = BIO_new_file(cert_file, "r");
    if (cert_bio == NULL) {
      err = bud_error_str(kBudErrLoadCert, cert_file);
      goto fatal;
    }

    r = bud_context_use_certificate_chain(ctx, cert_bio);
    BIO_free_all(cert_bio);
    if (!r) {
      err = bud_error_str(kBudErrParseCert, cert_file);
      goto fatal;
    }

    if (!SSL_CTX_use_PrivateKey_file(ctx->ctx,
                                     key_file,
                                     SSL_FILETYPE_PEM)) {
      err = bud_error_str(kBudErrParseKey, key_file);
      goto fatal;
    }
  }

  return bud_ok();

fatal:
  /* Free all allocated contexts */
  do
    bud_context_free(&config->contexts[i--]);
  while (i >= 0);

  return err;
}
Example #16
0
void bud_client_send_cb(uv_write_t* req, int status) {
  bud_client_t* client;
  bud_client_error_t cerr;
  bud_client_side_t* side;
  bud_client_side_t* opposite;

  /* Closing, ignore */
  if (status == UV_ECANCELED)
    return;

  client = req->data;

  if (req == &client->frontend.write_req) {
    side = &client->frontend;
    opposite = &client->backend;
  } else {
    side = &client->backend;
    opposite = &client->frontend;
  }

  if (status != 0) {
    side->write = kBudProgressDone;
    bud_client_close(
        client,
        bud_client_error(bud_error_num(kBudErrClientWriteCb, status), side));
    return;
  }

  /* Consume written data */
  DBG(side, "write_cb => %d", side->write_size);
  ringbuffer_read_skip(&side->output, side->write_size);

  /* Skip data in xforward parser */
  if (side == &client->backend)
    bud_client_xforward_skip(client, side->write_size);

  side->write = kBudProgressNone;
  side->write_size = 0;

  if (opposite->reading == kBudProgressNone) {
    if ((client->retry == kBudProgressRunning ||
         client->connect == kBudProgressRunning) &&
        opposite == &client->backend) {
      /* Set reading mark on backend to resume it after reconnecting */
      opposite->reading = kBudProgressRunning;
    } else if (opposite->close != kBudProgressDone &&
               side->close != kBudProgressDone &&
               side->shutdown != kBudProgressDone &&
               !ringbuffer_is_full(&side->output)) {
      DBG_LN(opposite, "read_start");
      cerr = bud_client_read_start(client, opposite);
      if (!bud_is_ok(cerr.err))
        return bud_client_close(client, cerr);
      opposite->reading = kBudProgressRunning;
    }
  }

  /* Cycle again */
  cerr = bud_client_cycle(client);
  if (!bud_is_ok(cerr.err))
    return bud_client_close(client, cerr);

  if (side->close == kBudProgressRunning ||
      side->shutdown == kBudProgressRunning) {
    if (!ringbuffer_is_empty(&side->output))
      return;

    /* No new data, destroy or shutdown */
    if (side->shutdown == kBudProgressRunning) {
      cerr = bud_client_shutdown(client, side);
      if (!bud_is_ok(cerr.err))
        bud_client_close(client, cerr);
      return;
    }
    bud_client_close(client, bud_client_ok(side));
  }
}
Example #17
0
bud_client_error_t bud_client_send(bud_client_t* client,
                                   bud_client_side_t* side) {
  char* out[RING_BUFFER_COUNT];
  uv_buf_t buf[RING_BUFFER_COUNT];
  uv_buf_t* pbuf;
  size_t size[ARRAY_SIZE(out)];
  size_t count;
  size_t i;
  int r;
  bud_client_error_t cerr;

  /* Already writing */
  if (side->write != kBudProgressNone)
    goto done;

  /* If client is closed - stop sending */
  if (client->close == kBudProgressDone)
    goto done;

  /* Backend still connecting */
  if (side == &client->backend && client->connect != kBudProgressDone)
    goto done;

  count = ARRAY_SIZE(out);
  side->write_size = ringbuffer_read_nextv(&side->output, out, size, &count);
  if (side->write_size == 0)
    goto done;

  DBG(side, "uv_write(%ld) iovcnt: %ld", side->write_size, count);

  side->write_req.data = client;
  for (i = 0; i < count; i++)
    buf[i] = uv_buf_init(out[i], size[i]);

  /* Try writing without queueing first */
  r = uv_try_write((uv_stream_t*) &side->tcp, buf, count);
  ASSERT((r >= 0 && (size_t) r <= side->write_size) || r < 0,
         "Value returned by uv_try_write is OOB");

  /* Fully written */
  if (r == (int) side->write_size) {
    DBG_LN(side, "immediate write");

    /* NOTE: not causing recursion */
    bud_client_send_cb(&side->write_req, 0);
    goto done;
  } if (r == UV_ENOSYS || r == UV_EAGAIN) {
    /* Not supported try_write */
    r = 0;
  } else if (r < 0) {
    cerr = bud_client_error(bud_error_num(kBudErrClientTryWrite, r), side);
    goto fatal;
  }

  /* Skip partially written bytes */
  ringbuffer_read_skip(&side->output, r);

  /* Partially written */
  side->write_size -= r;
  pbuf = buf;
  for (; r > 0; pbuf++, count--) {
    if ((int) pbuf->len > r) {
      /* Split */
      pbuf->base += r;
      pbuf->len -= r;
      r = 0;
      break;
    } else {
      r -= pbuf->len;
    }
  }
  DBG(side, "async uv_write(%ld) follow up: %ld", side->write_size, count);

  r = uv_write(&side->write_req,
               (uv_stream_t*) &side->tcp,
               pbuf,
               count,
               bud_client_send_cb);
  if (r != 0) {
    cerr = bud_client_error(bud_error_num(kBudErrClientWrite, r), side);
    goto fatal;
  }

  DBG_LN(side, "queued write");
  side->write = kBudProgressRunning;

done:
  return bud_client_ok(side);

fatal:
  side->write = kBudProgressDone;
  return cerr;
}
Example #18
0
File: config.c Project: indutny/bud
bud_error_t bud_config_init(bud_config_t* config) {
  bud_error_t err;
  int i;
  int r;

  /* Get addresses of frontend and backend */
  r = bud_config_str_to_addr(config->frontend.host,
                             config->frontend.port,
                             &config->frontend.addr);
  if (r != 0)
    return bud_error_num(kBudErrPton, r);

  for (i = 0; i < config->frontend.interface.count; i++) {
    bud_config_addr_t* addr;

    addr = &config->frontend.interface.list[i];
    r = bud_config_str_to_addr(addr->host, addr->port, &addr->addr);
    if (r != 0)
      return bud_error_num(kBudErrPton, r);
  }

  err = bud_config_format_proxyline(config);
  if (!bud_is_ok(err))
    return err;

  i = 0;

  config->balance_e = bud_config_balance_to_enum(config->balance);

  /* At least one backend should be present for non-SNI balancing */
  if (config->contexts[0].backend.count == 0 &&
      config->balance_e != kBudBalanceSNI) {
    err = bud_error(kBudErrNoBackend);
    goto fatal;
  }

  /* Get indexes for SSL_set_ex_data()/SSL_get_ex_data() */
  if (kBudSSLClientIndex == -1) {
    kBudSSLConfigIndex = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    kBudSSLClientIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    kBudSSLSNIIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    kBudSSLTicketKeyIndex = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
    if (kBudSSLConfigIndex == -1 ||
        kBudSSLClientIndex == -1 ||
        kBudSSLSNIIndex == -1 ||
        kBudSSLTicketKeyIndex == -1) {
      err = bud_error(kBudErrNoSSLIndex);
      goto fatal;
    }
  }

#ifndef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
  if (config->context_count != 0) {
    err = bud_error(kBudErrSNINotSupported);
    goto fatal;
  }
#endif  /* !SSL_CTRL_SET_TLSEXT_SERVERNAME_CB */

  /* Allocate workers */
  if (!config->is_worker && config->worker_count != 0) {
    config->workers = calloc(config->worker_count, sizeof(*config->workers));
    if (config->workers == NULL) {
      err = bud_error_str(kBudErrNoMem, "workers");
      goto fatal;
    }
  }

  /* Initialize logger */
  config->logger = bud_logger_new(config, &err);
  if (!bud_is_ok(err))
    goto fatal;

  err = bud_config_init_tracing(&config->trace);
  if (!bud_is_ok(err))
    goto fatal;

  if (config->is_worker || config->worker_count == 0) {
    /* Connect to SNI server */
    if (config->sni.enabled) {
      config->sni.pool = bud_http_pool_new(config,
                                           config->sni.host,
                                           config->sni.port,
                                           &err);
      if (config->sni.pool == NULL)
        goto fatal;
    }

    /* Connect to OCSP Stapling server */
    if (config->stapling.enabled) {
      config->stapling.pool = bud_http_pool_new(config,
                                                config->stapling.host,
                                                config->stapling.port,
                                                &err);
      if (config->stapling.pool == NULL)
        goto fatal;
    }
  }

  /* Init all contexts */
  for (i = 0; i < config->context_count + 1; i++) {
    err = bud_context_init(config, &config->contexts[i]);
    if (!bud_is_ok(err))
      goto fatal;
  }

  return bud_ok();

fatal:
  /* Free all allocated contexts */
  do
    bud_context_free(&config->contexts[i--]);
  while (i >= 0);

  return err;
}
Example #19
0
bud_client_error_t bud_client_backend_out(bud_client_t* client) {
  int read;
  int err;
  size_t avail;
  char* out;
  bud_client_error_t cerr;

  /* If buffer is full - stop reading */
  cerr = bud_client_throttle(client,
                             &client->backend,
                             &client->backend.output);
  if (cerr.err.code == kBudErrClientThrottle)
    return bud_client_ok(&client->frontend);
  else if (!bud_is_ok(cerr.err))
    return cerr;

  do {
    avail = 0;
    int init_trigger;

    init_trigger = SSL_is_init_finished(client->ssl);
    out = ringbuffer_write_ptr(&client->backend.output, &avail);
    read = SSL_read(client->ssl, out, avail);
    init_trigger ^= SSL_is_init_finished(client->ssl);

    DBG(&client->frontend, "SSL_read() => %d", read);
    if (read > 0)
      ringbuffer_write_append(&client->backend.output, read);

    /* Send proxyline once the handshake will end */
    if (init_trigger != 0) {
      cerr = bud_client_prepend_proxyline(client);
      if (!bud_is_ok(cerr.err))
        return cerr;
    }

    /* If there is any new data - try to append x-forwarded-for */
    if (read > 0 &&
        client->selected_backend->xforward &&
        !bud_client_xforward_done(client)) {
      cerr = bud_client_prepend_xforward(client);
      if (!bud_is_ok(cerr.err))
        return cerr;
    }

    /* Either proxyline or incoming data - need to send stuff to the client */
    if (init_trigger != 0 || read > 0) {
      cerr = bud_client_send(client, &client->backend);
      if (!bud_is_ok(cerr.err))
        return cerr;
    }

    /* info_cb() has closed front-end */
    if (client->close != kBudProgressNone)
      return bud_client_ok(&client->frontend);
  } while (read > 0);

  if (read > 0)
    goto success;

  err = SSL_get_error(client->ssl, read);
  if (err == SSL_ERROR_WANT_READ ||
      err == SSL_ERROR_WANT_WRITE ||
      err == SSL_ERROR_WANT_X509_LOOKUP) {
    goto success;
  }

  /* Close-notify, most likely */
  if (err == SSL_ERROR_ZERO_RETURN)
    return bud_client_shutdown(client, &client->backend);

  return bud_client_error(bud_error_num(kBudErrClientSSLRead, err),
                          &client->frontend);

success:
  return bud_client_ok(&client->backend);
}
Example #20
0
bud_error_t bud_config_new_ssl_ctx(bud_config_t* config,
                                   bud_context_t* context) {
  SSL_CTX* ctx;
  int ecdh_nid;
  EC_KEY* ecdh;
  bud_error_t err;
  int options;
  int r;
  const char* ticket_key;
  size_t max_len;

  if (context->backend != NULL) {
    if (context->backend->keepalive == -1)
      context->backend->keepalive = kBudDefaultKeepalive;
    r = bud_config_str_to_addr(context->backend->host,
                               context->backend->port,
                               &context->backend->addr);
    if (r != 0)
      return bud_error_num(kBudErrPton, r);
  }

  /* Decode ticket_key */
  ticket_key = context->ticket_key == NULL ? config->frontend.ticket_key :
                                             context->ticket_key;
  if (ticket_key != NULL) {
    max_len = sizeof(context->ticket_key_storage);
    if (bud_base64_decode(context->ticket_key_storage,
                          max_len,
                          ticket_key,
                          strlen(ticket_key)) < max_len) {
      return bud_error(kBudErrSmallTicketKey);
    }
  }

  /* Choose method, tlsv1_2 by default */
  if (config->frontend.method == NULL) {
    if (strcmp(config->frontend.security, "tls1.1") == 0)
      config->frontend.method = TLSv1_1_server_method();
    else if (strcmp(config->frontend.security, "tls1.0") == 0)
      config->frontend.method = TLSv1_server_method();
    else if (strcmp(config->frontend.security, "tls1.2") == 0)
      config->frontend.method = TLSv1_2_server_method();
    else if (strcmp(config->frontend.security, "ssl3") == 0)
      config->frontend.method = SSLv3_server_method();
    else
      config->frontend.method = SSLv23_server_method();
  }

  ctx = SSL_CTX_new(config->frontend.method);
  if (ctx == NULL)
    return bud_error_str(kBudErrNoMem, "SSL_CTX");

  /* Disable sessions, they won't work with cluster anyway */
  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);

  if (config->frontend.max_send_fragment)
    SSL_CTX_set_max_send_fragment(ctx, config->frontend.max_send_fragment);

  if (ticket_key != NULL) {
    SSL_CTX_set_tlsext_ticket_keys(ctx,
                                   context->ticket_key_storage,
                                   sizeof(context->ticket_key_storage));
  }

  /* ECDH curve selection */
  if (context->ecdh != NULL || config->frontend.ecdh != NULL) {
    if (context->ecdh != NULL)
      ecdh_nid = OBJ_sn2nid(context->ecdh);
    else
      ecdh_nid = OBJ_sn2nid(config->frontend.ecdh);

    if (ecdh_nid == NID_undef) {
      ecdh = NULL;
      err = bud_error_str(kBudErrECDHNotFound,
                          context->ecdh == NULL ? config->frontend.ecdh :
                                                  context->ecdh);
      goto fatal;
    }

    ecdh = EC_KEY_new_by_curve_name(ecdh_nid);
    if (ecdh == NULL) {
      err = bud_error_str(kBudErrNoMem, "EC_KEY");
      goto fatal;
    }

    SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);
    SSL_CTX_set_tmp_ecdh(ctx, ecdh);
    EC_KEY_free(ecdh);
  }
  ecdh = NULL;

  /* Cipher suites */
  if (context->ciphers != NULL)
    SSL_CTX_set_cipher_list(ctx, context->ciphers);
  else if (config->frontend.ciphers != NULL)
    SSL_CTX_set_cipher_list(ctx, config->frontend.ciphers);

  /* Disable SSL2 */
  options = SSL_OP_NO_SSLv2 | SSL_OP_ALL;
  if (!config->frontend.ssl3)
    options |= SSL_OP_NO_SSLv3;

  if (config->frontend.server_preference)
    options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
  SSL_CTX_set_options(ctx, options);

#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
  if (config->context_count != 0) {
    SSL_CTX_set_tlsext_servername_callback(ctx,
                                           bud_config_select_sni_context);
    SSL_CTX_set_tlsext_servername_arg(ctx, config);
  }
#endif  /* SSL_CTRL_SET_TLSEXT_SERVERNAME_CB */

#ifdef OPENSSL_NPN_NEGOTIATED
  context->npn_line = bud_config_encode_npn(config,
                                            context->npn,
                                            &context->npn_line_len,
                                            &err);
  if (!bud_is_ok(err))
    goto fatal;

  if (context->npn_line != NULL) {
    SSL_CTX_set_next_protos_advertised_cb(ctx,
                                          bud_config_advertise_next_proto,
                                          context);
  }
#else  /* !OPENSSL_NPN_NEGOTIATED */
  err = bud_error(kBudErrNPNNotSupported);
  goto fatal;
#endif  /* OPENSSL_NPN_NEGOTIATED */

  SSL_CTX_set_tlsext_status_cb(ctx, bud_client_stapling_cb);

  context->ctx = ctx;
  return bud_ok();

fatal:
  if (ecdh != NULL)
    EC_KEY_free(ecdh);

  SSL_CTX_free(ctx);
  return err;
}
Example #21
0
bud_error_t bud_worker(bud_config_t* config) {
  int r;
  bud_error_t err;

  bud_log(config, kBudLogDebug, "worker starting");

  config->loop = uv_default_loop();
  if (config->loop == NULL) {
    err = bud_error_str(kBudErrNoMem, "config->loop");
    goto fatal;
  }

  err = bud_ipc_init(&config->ipc, config);
  if (!bud_is_ok(err))
    goto fatal;

  config->ipc.client_cb = bud_worker_ipc_client_cb;

  err = bud_ipc_open(&config->ipc, 0);
  if (!bud_is_ok(err))
    goto failed_ipc_open;

  err = bud_ipc_start(&config->ipc);
  if (!bud_is_ok(err))
    goto failed_ipc_open;

  config->signal.sighup = malloc(sizeof(*config->signal.sighup));
  if (config->signal.sighup == NULL) {
    err = bud_error_str(kBudErrNoMem, "config->.sighup");
    goto failed_ipc_open;
  }

  config->signal.sighup->data = config;

  r = uv_signal_init(config->loop, config->signal.sighup);
  if (r != 0) {
    err = bud_error_num(kBudErrSignalInit, r);
    goto failed_signal_init;
  }

  r = uv_signal_start(config->signal.sighup, bud_worker_signal_cb, SIGHUP);
  if (r != 0) {
    err = bud_error_num(kBudErrSignalInit, r);
    goto failed_signal_start;
  }

#ifndef _WIN32
  /* Drop privileges */
  err = bud_config_drop_privileges(config);
  if (!bud_is_ok(err))
    goto failed_signal_start;
#endif  /* !_WIN32 */

  err = bud_ok();
  return err;

failed_signal_start:
  uv_close((uv_handle_t*) config->signal.sighup, bud_worker_close_cb);
  goto failed_ipc_open;

failed_signal_init:
  free(config->signal.sighup);
  config->signal.sighup = NULL;

failed_ipc_open:
  bud_ipc_close(&config->ipc);

fatal:
  return err;
}
Example #22
0
bud_error_t bud_worker(bud_config_t* config) {
    int r;
    bud_error_t err;

    bud_log(config, kBudLogDebug, "worker starting");

    config->loop = uv_default_loop();
    config->ipc = malloc(sizeof(*config->ipc));
    config->signal.sighup = malloc(sizeof(*config->signal.sighup));
    if (config->ipc == NULL || config->signal.sighup == NULL) {
        err = bud_error_str(kBudErrNoMem, "config->ipc");
        goto fatal;
    }

    config->ipc->data = config;
    config->signal.sighup->data = config;

    r = uv_pipe_init(config->loop, config->ipc, 1);
    if (r != 0) {
        err = bud_error_num(kBudErrIPCPipeInit, r);
        goto fatal;
    }

    r = uv_pipe_open(config->ipc, 0);
    if (r != 0) {
        err = bud_error_num(kBudErrIPCPipeOpen, r);
        goto failed_pipe_open;
    }

    r = uv_read_start((uv_stream_t*) config->ipc,
                      bud_worker_alloc_cb,
                      bud_worker_read_cb);
    if (r != 0) {
        err = bud_error_num(kBudErrIPCReadStart, r);
        goto failed_pipe_open;
    }

#ifndef _WIN32
    /* Drop privileges */
    err = bud_config_drop_privileges(config);
    if (!bud_is_ok(err))
        goto failed_pipe_open;

    r = uv_signal_init(config->loop, config->signal.sighup);
    if (r != 0) {
        err = bud_error_num(kBudErrSignalInit, r);
        goto failed_pipe_open;
    }

    r = uv_signal_start(config->signal.sighup, bud_worker_signal_cb, SIGHUP);
    if (r != 0) {
        err = bud_error_num(kBudErrSignalInit, r);
        goto failed_signal_start;
    }
#endif  /* !_WIN32 */

    err = bud_ok();
    return err;

#ifndef _WIN32
failed_signal_start:
    uv_close((uv_handle_t*) config->signal.sighup, bud_worker_close_cb);
#endif  /* !_WIN32 */

failed_pipe_open:
    uv_close((uv_handle_t*) config->ipc, bud_worker_close_cb);
    goto cleanup;

fatal:
    free(config->ipc);

cleanup:
    config->ipc = NULL;
    return err;
}