You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@trafficserver.apache.org by PSUdaemon <gi...@git.apache.org> on 2016/04/11 21:09:06 UTC

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

GitHub user PSUdaemon opened a pull request:

    https://github.com/apache/trafficserver/pull/560

    TS-4320: Add ACME Plugin

    This is a first pass at making an ACME plugin for ATS. This current version amounts to a simple static file server that leverages a CLI client, but there are plans to make this a fully standalone implementation down the road.

You can merge this pull request into a Git repository by running:

    $ git pull https://github.com/PSUdaemon/trafficserver acme_plugin

Alternatively you can review and apply these changes as the patch at:

    https://github.com/apache/trafficserver/pull/560.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

    This closes #560
    
----
commit d8a87109e7edb2419f425e48ad06a1a9bdd782cf
Author: Phil Sorber <so...@apache.org>
Date:   2016-04-11T18:37:52Z

    TS-4320: Add ACME Plugin

----


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by PSUdaemon <gi...@git.apache.org>.
Github user PSUdaemon commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r60003490
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    --- End diff --
    
    I agree, we should make a separate JIRA and get all these at once. I'll leave this be for now.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59385596
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    --- End diff --
    
    Add ``strerror(errno)`` to the error.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59386613
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    +  TSVConnClose(my_state->net_vc);
    +  TSfree(my_state);
    +  TSContDestroy(contp);
    +}
    +
    +/* Add data to the output */
    +inline static int
    +add_data_to_resp(const char *buf, int len, AcmeState *my_state)
    +{
    +  TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +  return len;
    +}
    +
    +static int
    +add_file_to_resp(AcmeState *my_state)
    +{
    +  if (-1 == my_state->fd) {
    +    return add_data_to_resp("\r\n", 2, my_state);
    +  } else {
    +    int ret = 0, len;
    +    char buf[8192];
    +
    +    while (1) {
    +      len = read(my_state->fd, buf, sizeof(buf));
    +      if ((0 == len) || ((-1 == len) && (errno != EAGAIN) && (errno != EINTR))) {
    +        break;
    +      } else {
    +        TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +        ret += len;
    +      }
    +    }
    +    close(my_state->fd);
    --- End diff --
    
    ```C
    my_state->fd = -1;
    ```


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by mlibbey <gi...@git.apache.org>.
Github user mlibbey commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59300512
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    +  TSVConnClose(my_state->net_vc);
    +  TSfree(my_state);
    +  TSContDestroy(contp);
    +}
    +
    +/* Add data to the output */
    +inline static int
    +add_data_to_resp(const char *buf, int len, AcmeState *my_state)
    +{
    +  TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +  return len;
    +}
    +
    +static int
    +add_file_to_resp(AcmeState *my_state)
    +{
    +  if (-1 == my_state->fd) {
    +    return add_data_to_resp("\r\n", 2, my_state);
    +  } else {
    +    int ret = 0, len;
    +    char buf[8192];
    +
    +    while (1) {
    +      len = read(my_state->fd, buf, sizeof(buf));
    +      if ((0 == len) || ((-1 == len) && (errno != EAGAIN) && (errno != EINTR))) {
    +        break;
    +      } else {
    +        TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +        ret += len;
    +      }
    +    }
    +    close(my_state->fd);
    +
    +    return ret;
    +  }
    +}
    +
    +/* Process a read event from the SM */
    +static void
    +acme_process_read(TSCont contp, TSEvent event, AcmeState *my_state)
    +{
    +  if (event == TS_EVENT_VCONN_READ_READY) {
    +    if (-1 == my_state->fd) {
    +      my_state->output_bytes = add_data_to_resp(ACME_DENIED_RESP, strlen(ACME_DENIED_RESP), my_state);
    +    } else {
    +      my_state->output_bytes = add_data_to_resp(ACME_OK_RESP, strlen(ACME_OK_RESP), my_state);
    +    }
    +    TSVConnShutdown(my_state->net_vc, 1, 0);
    +    my_state->write_vio = TSVConnWrite(my_state->net_vc, contp, my_state->resp_reader, INT64_MAX);
    +  } else if (event == TS_EVENT_ERROR) {
    +    TSError("[%s] acme_process_read: Received TS_EVENT_ERROR", PLUGIN_NAME);
    +  } else if (event == TS_EVENT_VCONN_EOS) {
    +    /* client may end the connection, simply return */
    +    return;
    +  } else if (event == TS_EVENT_NET_ACCEPT_FAILED) {
    +    TSError("[%s] acme_process_read: Received TS_EVENT_NET_ACCEPT_FAILED", PLUGIN_NAME);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +}
    +
    +/* Process a write event from the SM */
    +static void
    +acme_process_write(TSCont contp, TSEvent event, AcmeState *my_state)
    +{
    +  if (event == TS_EVENT_VCONN_WRITE_READY) {
    +    char buf[64]; /* Plenty of space for CL: header */
    +    int len;
    +
    +    len = snprintf(buf, sizeof(buf) - 1, "Content-Length: %zd\r\n\r\n", my_state->stat_buf.st_size);
    +    my_state->output_bytes += add_data_to_resp(buf, len, my_state);
    +    my_state->output_bytes += add_file_to_resp(my_state);
    +
    +    TSVIONBytesSet(my_state->write_vio, my_state->output_bytes);
    +    TSVIOReenable(my_state->write_vio);
    +  } else if (TS_EVENT_VCONN_WRITE_COMPLETE) {
    +    cleanup(contp, my_state);
    +  } else if (event == TS_EVENT_ERROR) {
    +    TSError("[%s] acme_process_write: Received TS_EVENT_ERROR", PLUGIN_NAME);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +}
    +
    +/* Process the accept event from the SM */
    +static void
    +acme_process_accept(TSCont contp, AcmeState *my_state)
    +{
    +  my_state->req_buffer = TSIOBufferCreate();
    +  my_state->resp_buffer = TSIOBufferCreate();
    +  my_state->resp_reader = TSIOBufferReaderAlloc(my_state->resp_buffer);
    +  my_state->read_vio = TSVConnRead(my_state->net_vc, contp, my_state->req_buffer, INT64_MAX);
    +}
    +
    +/* Imlement the server intercept */
    --- End diff --
    
    Im*p*lement


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by zwoop <gi...@git.apache.org>.
Github user zwoop commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59397385
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    +  TSVConnClose(my_state->net_vc);
    +  TSfree(my_state);
    +  TSContDestroy(contp);
    +}
    +
    +/* Add data to the output */
    +inline static int
    +add_data_to_resp(const char *buf, int len, AcmeState *my_state)
    +{
    +  TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +  return len;
    +}
    +
    +static int
    +add_file_to_resp(AcmeState *my_state)
    +{
    +  if (-1 == my_state->fd) {
    +    return add_data_to_resp("\r\n", 2, my_state);
    +  } else {
    +    int ret = 0, len;
    +    char buf[8192];
    +
    +    while (1) {
    +      len = read(my_state->fd, buf, sizeof(buf));
    +      if ((0 == len) || ((-1 == len) && (errno != EAGAIN) && (errno != EINTR))) {
    +        break;
    +      } else {
    +        TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +        ret += len;
    +      }
    +    }
    +    close(my_state->fd);
    +
    +    return ret;
    +  }
    +}
    +
    +/* Process a read event from the SM */
    +static void
    +acme_process_read(TSCont contp, TSEvent event, AcmeState *my_state)
    +{
    +  if (event == TS_EVENT_VCONN_READ_READY) {
    +    if (-1 == my_state->fd) {
    +      my_state->output_bytes = add_data_to_resp(ACME_DENIED_RESP, strlen(ACME_DENIED_RESP), my_state);
    +    } else {
    +      my_state->output_bytes = add_data_to_resp(ACME_OK_RESP, strlen(ACME_OK_RESP), my_state);
    +    }
    +    TSVConnShutdown(my_state->net_vc, 1, 0);
    +    my_state->write_vio = TSVConnWrite(my_state->net_vc, contp, my_state->resp_reader, INT64_MAX);
    +  } else if (event == TS_EVENT_ERROR) {
    +    TSError("[%s] acme_process_read: Received TS_EVENT_ERROR", PLUGIN_NAME);
    +  } else if (event == TS_EVENT_VCONN_EOS) {
    +    /* client may end the connection, simply return */
    +    return;
    +  } else if (event == TS_EVENT_NET_ACCEPT_FAILED) {
    +    TSError("[%s] acme_process_read: Received TS_EVENT_NET_ACCEPT_FAILED", PLUGIN_NAME);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +}
    +
    +/* Process a write event from the SM */
    +static void
    +acme_process_write(TSCont contp, TSEvent event, AcmeState *my_state)
    +{
    +  if (event == TS_EVENT_VCONN_WRITE_READY) {
    +    char buf[64]; /* Plenty of space for CL: header */
    +    int len;
    +
    +    len = snprintf(buf, sizeof(buf) - 1, "Content-Length: %zd\r\n\r\n", my_state->stat_buf.st_size);
    +    my_state->output_bytes += add_data_to_resp(buf, len, my_state);
    +    my_state->output_bytes += add_file_to_resp(my_state);
    +
    +    TSVIONBytesSet(my_state->write_vio, my_state->output_bytes);
    +    TSVIOReenable(my_state->write_vio);
    +  } else if (TS_EVENT_VCONN_WRITE_COMPLETE) {
    +    cleanup(contp, my_state);
    +  } else if (event == TS_EVENT_ERROR) {
    +    TSError("[%s] acme_process_write: Received TS_EVENT_ERROR", PLUGIN_NAME);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +}
    +
    +/* Process the accept event from the SM */
    +static void
    +acme_process_accept(TSCont contp, AcmeState *my_state)
    +{
    +  my_state->req_buffer = TSIOBufferCreate();
    +  my_state->resp_buffer = TSIOBufferCreate();
    +  my_state->resp_reader = TSIOBufferReaderAlloc(my_state->resp_buffer);
    +  my_state->read_vio = TSVConnRead(my_state->net_vc, contp, my_state->req_buffer, INT64_MAX);
    +}
    +
    +/* Imlement the server intercept */
    +static int
    +acme_intercept(TSCont contp, TSEvent event, void *edata)
    +{
    +  AcmeState *my_state = TSContDataGet(contp);
    +
    +  if (event == TS_EVENT_NET_ACCEPT) {
    +    my_state->net_vc = (TSVConn)edata;
    +    acme_process_accept(contp, my_state);
    +  } else if (edata == my_state->read_vio) { /* All read events */
    +    acme_process_read(contp, event, my_state);
    +  } else if (edata == my_state->write_vio) { /* All write events */
    +    acme_process_write(contp, event, my_state);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +
    +  return 0;
    +}
    +
    +/* Read-request header continuation, used to kick off the server intercept if necessary */
    +static int
    +acme_hook(TSCont contp ATS_UNUSED, TSEvent event ATS_UNUSED, void *edata)
    +{
    +  TSMBuffer reqp;
    +  TSMLoc hdr_loc = NULL, url_loc = NULL;
    +  TSCont icontp;
    +  AcmeState *my_state;
    +  TSHttpTxn txnp = (TSHttpTxn)edata;
    +
    +  TSDebug(PLUGIN_NAME, "kicking off ACME hook");
    +
    +  if ((TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &reqp, &hdr_loc)) && (TS_SUCCESS == TSHttpHdrUrlGet(reqp, hdr_loc, &url_loc))) {
    +    int path_len = 0;
    +    const char *path = TSUrlPathGet(reqp, url_loc, &path_len);
    +
    +    /* Short circuit the / path, common case */
    +    if (!path || path_len < (strlen(ACME_WK_PATH) + 2) || *path != '.' || memcmp(path, ACME_WK_PATH, strlen(ACME_WK_PATH))) {
    +      TSDebug(PLUGIN_NAME, "skipping URL path = %.*s", path_len, path);
    +      goto cleanup;
    +    }
    +
    +    TSSkipRemappingSet(txnp, 1); /* not strictly necessary, but speed is everything these days */
    +
    +    /* This request is for us -- register our intercept */
    +    icontp = TSContCreate(acme_intercept, TSMutexCreate());
    +
    +    my_state = make_acme_state();
    +    open_acme_file(my_state, path + strlen(ACME_WK_PATH), path_len - strlen(ACME_WK_PATH));
    +
    +    TSContDataSet(icontp, my_state);
    +    TSHttpTxnIntercept(icontp, txnp);
    +    TSDebug(PLUGIN_NAME, "created intercept hook");
    +  }
    +
    +cleanup:
    +  if (url_loc) {
    +    TSHandleMLocRelease(reqp, hdr_loc, url_loc);
    +  }
    +  if (hdr_loc) {
    +    TSHandleMLocRelease(reqp, TS_NULL_MLOC, hdr_loc);
    +  }
    +
    +  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
    +
    +  return 0;
    +}
    +
    +/* Initialize the plugin / global continuation hook */
    +void
    +TSPluginInit(int argc, const char *argv[])
    +{
    +  TSPluginRegistrationInfo info;
    +  const char *proof = "acme";
    +
    +  static const struct option longopt[] = {
    +    {(char *)"proof", optional_argument, NULL, 'p'}, {NULL, no_argument, NULL, '\0'},
    --- End diff --
    
    +1 on --proof-directory.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by mlibbey <gi...@git.apache.org>.
Github user mlibbey commented on the pull request:

    https://github.com/apache/trafficserver/pull/560#issuecomment-208623709
  
    Could you include a readme or better a doc page (https://docs.trafficserver.apache.org/en/latest/developer-guide/documentation/plugins.en.html) explaining what this is and does?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by zwoop <gi...@git.apache.org>.
Github user zwoop commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59397510
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    --- End diff --
    
    Yeah, lets make it CC: no-store,private. This likely is in several other plugins that this is stole^h^h^h borrowed from. Perhaps file a Jira on fixing those as well?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59385661
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    --- End diff --
    
    ``errno`` again.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59386515
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    +  TSVConnClose(my_state->net_vc);
    +  TSfree(my_state);
    +  TSContDestroy(contp);
    +}
    +
    +/* Add data to the output */
    +inline static int
    +add_data_to_resp(const char *buf, int len, AcmeState *my_state)
    +{
    +  TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +  return len;
    +}
    +
    +static int
    +add_file_to_resp(AcmeState *my_state)
    +{
    +  if (-1 == my_state->fd) {
    +    return add_data_to_resp("\r\n", 2, my_state);
    +  } else {
    +    int ret = 0, len;
    +    char buf[8192];
    +
    +    while (1) {
    +      len = read(my_state->fd, buf, sizeof(buf));
    --- End diff --
    
    Maybe a TODO to use the ``TSAIO`` API?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by mlibbey <gi...@git.apache.org>.
Github user mlibbey commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59300496
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    --- End diff --
    
    no-cache (https://tools.ietf.org/html/rfc7234#section-5.2.1.4) or no-store (https://tools.ietf.org/html/rfc7234#section-5.2.1.5)?  Eg, cache it, but validate before serving from cache, or don't cache it at all? 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by zwoop <gi...@git.apache.org>.
Github user zwoop commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59397203
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    +  TSVConnClose(my_state->net_vc);
    +  TSfree(my_state);
    +  TSContDestroy(contp);
    +}
    +
    +/* Add data to the output */
    +inline static int
    +add_data_to_resp(const char *buf, int len, AcmeState *my_state)
    --- End diff --
    
    Bike shedding.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59385843
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    --- End diff --
    
    It's weird to have an ``else if`` just after a return. Kill the else?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit closed the pull request at:

    https://github.com/apache/trafficserver/pull/560


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59384677
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    --- End diff --
    
    static


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59397488
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    +  TSVConnClose(my_state->net_vc);
    +  TSfree(my_state);
    +  TSContDestroy(contp);
    +}
    +
    +/* Add data to the output */
    +inline static int
    +add_data_to_resp(const char *buf, int len, AcmeState *my_state)
    --- End diff --
    
    Yes!


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59384993
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    --- End diff --
    
    Better to return ``ssize_t`` here to indicate a length or error.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59385389
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    --- End diff --
    
    This would be clearer if you used the char literals, eg
    ```C
    (c == '-' || c == ' _' || ...
    ```


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by zwoop <gi...@git.apache.org>.
Github user zwoop commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59397333
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    +  TSVConnClose(my_state->net_vc);
    +  TSfree(my_state);
    +  TSContDestroy(contp);
    +}
    +
    +/* Add data to the output */
    +inline static int
    +add_data_to_resp(const char *buf, int len, AcmeState *my_state)
    +{
    +  TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +  return len;
    +}
    +
    +static int
    +add_file_to_resp(AcmeState *my_state)
    +{
    +  if (-1 == my_state->fd) {
    +    return add_data_to_resp("\r\n", 2, my_state);
    +  } else {
    +    int ret = 0, len;
    +    char buf[8192];
    +
    +    while (1) {
    +      len = read(my_state->fd, buf, sizeof(buf));
    +      if ((0 == len) || ((-1 == len) && (errno != EAGAIN) && (errno != EINTR))) {
    +        break;
    +      } else {
    +        TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +        ret += len;
    +      }
    +    }
    +    close(my_state->fd);
    +
    +    return ret;
    +  }
    +}
    +
    +/* Process a read event from the SM */
    +static void
    +acme_process_read(TSCont contp, TSEvent event, AcmeState *my_state)
    +{
    +  if (event == TS_EVENT_VCONN_READ_READY) {
    +    if (-1 == my_state->fd) {
    +      my_state->output_bytes = add_data_to_resp(ACME_DENIED_RESP, strlen(ACME_DENIED_RESP), my_state);
    +    } else {
    +      my_state->output_bytes = add_data_to_resp(ACME_OK_RESP, strlen(ACME_OK_RESP), my_state);
    +    }
    +    TSVConnShutdown(my_state->net_vc, 1, 0);
    +    my_state->write_vio = TSVConnWrite(my_state->net_vc, contp, my_state->resp_reader, INT64_MAX);
    +  } else if (event == TS_EVENT_ERROR) {
    +    TSError("[%s] acme_process_read: Received TS_EVENT_ERROR", PLUGIN_NAME);
    +  } else if (event == TS_EVENT_VCONN_EOS) {
    +    /* client may end the connection, simply return */
    +    return;
    +  } else if (event == TS_EVENT_NET_ACCEPT_FAILED) {
    +    TSError("[%s] acme_process_read: Received TS_EVENT_NET_ACCEPT_FAILED", PLUGIN_NAME);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +}
    +
    +/* Process a write event from the SM */
    +static void
    +acme_process_write(TSCont contp, TSEvent event, AcmeState *my_state)
    +{
    +  if (event == TS_EVENT_VCONN_WRITE_READY) {
    +    char buf[64]; /* Plenty of space for CL: header */
    +    int len;
    +
    +    len = snprintf(buf, sizeof(buf) - 1, "Content-Length: %zd\r\n\r\n", my_state->stat_buf.st_size);
    +    my_state->output_bytes += add_data_to_resp(buf, len, my_state);
    +    my_state->output_bytes += add_file_to_resp(my_state);
    +
    +    TSVIONBytesSet(my_state->write_vio, my_state->output_bytes);
    +    TSVIOReenable(my_state->write_vio);
    +  } else if (TS_EVENT_VCONN_WRITE_COMPLETE) {
    +    cleanup(contp, my_state);
    +  } else if (event == TS_EVENT_ERROR) {
    +    TSError("[%s] acme_process_write: Received TS_EVENT_ERROR", PLUGIN_NAME);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +}
    +
    +/* Process the accept event from the SM */
    +static void
    +acme_process_accept(TSCont contp, AcmeState *my_state)
    +{
    +  my_state->req_buffer = TSIOBufferCreate();
    +  my_state->resp_buffer = TSIOBufferCreate();
    +  my_state->resp_reader = TSIOBufferReaderAlloc(my_state->resp_buffer);
    +  my_state->read_vio = TSVConnRead(my_state->net_vc, contp, my_state->req_buffer, INT64_MAX);
    +}
    +
    +/* Imlement the server intercept */
    +static int
    +acme_intercept(TSCont contp, TSEvent event, void *edata)
    +{
    +  AcmeState *my_state = TSContDataGet(contp);
    +
    +  if (event == TS_EVENT_NET_ACCEPT) {
    +    my_state->net_vc = (TSVConn)edata;
    +    acme_process_accept(contp, my_state);
    +  } else if (edata == my_state->read_vio) { /* All read events */
    +    acme_process_read(contp, event, my_state);
    +  } else if (edata == my_state->write_vio) { /* All write events */
    +    acme_process_write(contp, event, my_state);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +
    +  return 0;
    +}
    +
    +/* Read-request header continuation, used to kick off the server intercept if necessary */
    +static int
    +acme_hook(TSCont contp ATS_UNUSED, TSEvent event ATS_UNUSED, void *edata)
    +{
    +  TSMBuffer reqp;
    +  TSMLoc hdr_loc = NULL, url_loc = NULL;
    +  TSCont icontp;
    +  AcmeState *my_state;
    +  TSHttpTxn txnp = (TSHttpTxn)edata;
    +
    +  TSDebug(PLUGIN_NAME, "kicking off ACME hook");
    +
    +  if ((TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &reqp, &hdr_loc)) && (TS_SUCCESS == TSHttpHdrUrlGet(reqp, hdr_loc, &url_loc))) {
    +    int path_len = 0;
    +    const char *path = TSUrlPathGet(reqp, url_loc, &path_len);
    +
    +    /* Short circuit the / path, common case */
    +    if (!path || path_len < (strlen(ACME_WK_PATH) + 2) || *path != '.' || memcmp(path, ACME_WK_PATH, strlen(ACME_WK_PATH))) {
    --- End diff --
    
    strlen() seems more C-like, and the compiler will optimize this to a static (and, we avoid the -1).


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59398467
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    --- End diff --
    
    Yeh as I read further I saw where it was closed. Since this is the destructor, I was expecting to see it here ... maybe add an assertion if you don't want to verify the close?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59389367
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    +  TSVConnClose(my_state->net_vc);
    +  TSfree(my_state);
    +  TSContDestroy(contp);
    +}
    +
    +/* Add data to the output */
    +inline static int
    +add_data_to_resp(const char *buf, int len, AcmeState *my_state)
    +{
    +  TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +  return len;
    +}
    +
    +static int
    +add_file_to_resp(AcmeState *my_state)
    +{
    +  if (-1 == my_state->fd) {
    +    return add_data_to_resp("\r\n", 2, my_state);
    +  } else {
    +    int ret = 0, len;
    +    char buf[8192];
    +
    +    while (1) {
    +      len = read(my_state->fd, buf, sizeof(buf));
    +      if ((0 == len) || ((-1 == len) && (errno != EAGAIN) && (errno != EINTR))) {
    +        break;
    +      } else {
    +        TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +        ret += len;
    +      }
    +    }
    +    close(my_state->fd);
    +
    +    return ret;
    +  }
    +}
    +
    +/* Process a read event from the SM */
    +static void
    +acme_process_read(TSCont contp, TSEvent event, AcmeState *my_state)
    +{
    +  if (event == TS_EVENT_VCONN_READ_READY) {
    +    if (-1 == my_state->fd) {
    +      my_state->output_bytes = add_data_to_resp(ACME_DENIED_RESP, strlen(ACME_DENIED_RESP), my_state);
    +    } else {
    +      my_state->output_bytes = add_data_to_resp(ACME_OK_RESP, strlen(ACME_OK_RESP), my_state);
    +    }
    +    TSVConnShutdown(my_state->net_vc, 1, 0);
    +    my_state->write_vio = TSVConnWrite(my_state->net_vc, contp, my_state->resp_reader, INT64_MAX);
    +  } else if (event == TS_EVENT_ERROR) {
    +    TSError("[%s] acme_process_read: Received TS_EVENT_ERROR", PLUGIN_NAME);
    +  } else if (event == TS_EVENT_VCONN_EOS) {
    +    /* client may end the connection, simply return */
    +    return;
    +  } else if (event == TS_EVENT_NET_ACCEPT_FAILED) {
    +    TSError("[%s] acme_process_read: Received TS_EVENT_NET_ACCEPT_FAILED", PLUGIN_NAME);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +}
    +
    +/* Process a write event from the SM */
    +static void
    +acme_process_write(TSCont contp, TSEvent event, AcmeState *my_state)
    +{
    +  if (event == TS_EVENT_VCONN_WRITE_READY) {
    +    char buf[64]; /* Plenty of space for CL: header */
    +    int len;
    +
    +    len = snprintf(buf, sizeof(buf) - 1, "Content-Length: %zd\r\n\r\n", my_state->stat_buf.st_size);
    +    my_state->output_bytes += add_data_to_resp(buf, len, my_state);
    +    my_state->output_bytes += add_file_to_resp(my_state);
    +
    +    TSVIONBytesSet(my_state->write_vio, my_state->output_bytes);
    +    TSVIOReenable(my_state->write_vio);
    +  } else if (TS_EVENT_VCONN_WRITE_COMPLETE) {
    +    cleanup(contp, my_state);
    +  } else if (event == TS_EVENT_ERROR) {
    +    TSError("[%s] acme_process_write: Received TS_EVENT_ERROR", PLUGIN_NAME);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +}
    +
    +/* Process the accept event from the SM */
    +static void
    +acme_process_accept(TSCont contp, AcmeState *my_state)
    +{
    +  my_state->req_buffer = TSIOBufferCreate();
    +  my_state->resp_buffer = TSIOBufferCreate();
    +  my_state->resp_reader = TSIOBufferReaderAlloc(my_state->resp_buffer);
    +  my_state->read_vio = TSVConnRead(my_state->net_vc, contp, my_state->req_buffer, INT64_MAX);
    +}
    +
    +/* Imlement the server intercept */
    +static int
    +acme_intercept(TSCont contp, TSEvent event, void *edata)
    +{
    +  AcmeState *my_state = TSContDataGet(contp);
    +
    +  if (event == TS_EVENT_NET_ACCEPT) {
    +    my_state->net_vc = (TSVConn)edata;
    +    acme_process_accept(contp, my_state);
    +  } else if (edata == my_state->read_vio) { /* All read events */
    +    acme_process_read(contp, event, my_state);
    +  } else if (edata == my_state->write_vio) { /* All write events */
    +    acme_process_write(contp, event, my_state);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +
    +  return 0;
    +}
    +
    +/* Read-request header continuation, used to kick off the server intercept if necessary */
    +static int
    +acme_hook(TSCont contp ATS_UNUSED, TSEvent event ATS_UNUSED, void *edata)
    +{
    +  TSMBuffer reqp;
    +  TSMLoc hdr_loc = NULL, url_loc = NULL;
    +  TSCont icontp;
    +  AcmeState *my_state;
    +  TSHttpTxn txnp = (TSHttpTxn)edata;
    +
    +  TSDebug(PLUGIN_NAME, "kicking off ACME hook");
    +
    +  if ((TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &reqp, &hdr_loc)) && (TS_SUCCESS == TSHttpHdrUrlGet(reqp, hdr_loc, &url_loc))) {
    +    int path_len = 0;
    +    const char *path = TSUrlPathGet(reqp, url_loc, &path_len);
    +
    +    /* Short circuit the / path, common case */
    +    if (!path || path_len < (strlen(ACME_WK_PATH) + 2) || *path != '.' || memcmp(path, ACME_WK_PATH, strlen(ACME_WK_PATH))) {
    +      TSDebug(PLUGIN_NAME, "skipping URL path = %.*s", path_len, path);
    +      goto cleanup;
    +    }
    +
    +    TSSkipRemappingSet(txnp, 1); /* not strictly necessary, but speed is everything these days */
    +
    +    /* This request is for us -- register our intercept */
    +    icontp = TSContCreate(acme_intercept, TSMutexCreate());
    +
    +    my_state = make_acme_state();
    +    open_acme_file(my_state, path + strlen(ACME_WK_PATH), path_len - strlen(ACME_WK_PATH));
    +
    +    TSContDataSet(icontp, my_state);
    +    TSHttpTxnIntercept(icontp, txnp);
    +    TSDebug(PLUGIN_NAME, "created intercept hook");
    +  }
    +
    +cleanup:
    +  if (url_loc) {
    +    TSHandleMLocRelease(reqp, hdr_loc, url_loc);
    +  }
    +  if (hdr_loc) {
    +    TSHandleMLocRelease(reqp, TS_NULL_MLOC, hdr_loc);
    +  }
    +
    +  TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
    +
    +  return 0;
    +}
    +
    +/* Initialize the plugin / global continuation hook */
    +void
    +TSPluginInit(int argc, const char *argv[])
    +{
    +  TSPluginRegistrationInfo info;
    +  const char *proof = "acme";
    +
    +  static const struct option longopt[] = {
    +    {(char *)"proof", optional_argument, NULL, 'p'}, {NULL, no_argument, NULL, '\0'},
    --- End diff --
    
    This is a directory, maybe call it ``--proof-directory``?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59388857
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    +  TSVConnClose(my_state->net_vc);
    +  TSfree(my_state);
    +  TSContDestroy(contp);
    +}
    +
    +/* Add data to the output */
    +inline static int
    +add_data_to_resp(const char *buf, int len, AcmeState *my_state)
    +{
    +  TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +  return len;
    +}
    +
    +static int
    +add_file_to_resp(AcmeState *my_state)
    +{
    +  if (-1 == my_state->fd) {
    +    return add_data_to_resp("\r\n", 2, my_state);
    +  } else {
    +    int ret = 0, len;
    +    char buf[8192];
    +
    +    while (1) {
    +      len = read(my_state->fd, buf, sizeof(buf));
    +      if ((0 == len) || ((-1 == len) && (errno != EAGAIN) && (errno != EINTR))) {
    +        break;
    +      } else {
    +        TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +        ret += len;
    +      }
    +    }
    +    close(my_state->fd);
    +
    +    return ret;
    +  }
    +}
    +
    +/* Process a read event from the SM */
    +static void
    +acme_process_read(TSCont contp, TSEvent event, AcmeState *my_state)
    +{
    +  if (event == TS_EVENT_VCONN_READ_READY) {
    +    if (-1 == my_state->fd) {
    +      my_state->output_bytes = add_data_to_resp(ACME_DENIED_RESP, strlen(ACME_DENIED_RESP), my_state);
    +    } else {
    +      my_state->output_bytes = add_data_to_resp(ACME_OK_RESP, strlen(ACME_OK_RESP), my_state);
    +    }
    +    TSVConnShutdown(my_state->net_vc, 1, 0);
    +    my_state->write_vio = TSVConnWrite(my_state->net_vc, contp, my_state->resp_reader, INT64_MAX);
    +  } else if (event == TS_EVENT_ERROR) {
    +    TSError("[%s] acme_process_read: Received TS_EVENT_ERROR", PLUGIN_NAME);
    +  } else if (event == TS_EVENT_VCONN_EOS) {
    +    /* client may end the connection, simply return */
    +    return;
    +  } else if (event == TS_EVENT_NET_ACCEPT_FAILED) {
    +    TSError("[%s] acme_process_read: Received TS_EVENT_NET_ACCEPT_FAILED", PLUGIN_NAME);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +}
    +
    +/* Process a write event from the SM */
    +static void
    +acme_process_write(TSCont contp, TSEvent event, AcmeState *my_state)
    +{
    +  if (event == TS_EVENT_VCONN_WRITE_READY) {
    +    char buf[64]; /* Plenty of space for CL: header */
    +    int len;
    +
    +    len = snprintf(buf, sizeof(buf) - 1, "Content-Length: %zd\r\n\r\n", my_state->stat_buf.st_size);
    +    my_state->output_bytes += add_data_to_resp(buf, len, my_state);
    +    my_state->output_bytes += add_file_to_resp(my_state);
    +
    +    TSVIONBytesSet(my_state->write_vio, my_state->output_bytes);
    +    TSVIOReenable(my_state->write_vio);
    +  } else if (TS_EVENT_VCONN_WRITE_COMPLETE) {
    +    cleanup(contp, my_state);
    +  } else if (event == TS_EVENT_ERROR) {
    +    TSError("[%s] acme_process_write: Received TS_EVENT_ERROR", PLUGIN_NAME);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +}
    +
    +/* Process the accept event from the SM */
    +static void
    +acme_process_accept(TSCont contp, AcmeState *my_state)
    +{
    +  my_state->req_buffer = TSIOBufferCreate();
    +  my_state->resp_buffer = TSIOBufferCreate();
    +  my_state->resp_reader = TSIOBufferReaderAlloc(my_state->resp_buffer);
    +  my_state->read_vio = TSVConnRead(my_state->net_vc, contp, my_state->req_buffer, INT64_MAX);
    +}
    +
    +/* Imlement the server intercept */
    +static int
    +acme_intercept(TSCont contp, TSEvent event, void *edata)
    +{
    +  AcmeState *my_state = TSContDataGet(contp);
    +
    +  if (event == TS_EVENT_NET_ACCEPT) {
    +    my_state->net_vc = (TSVConn)edata;
    +    acme_process_accept(contp, my_state);
    +  } else if (edata == my_state->read_vio) { /* All read events */
    +    acme_process_read(contp, event, my_state);
    +  } else if (edata == my_state->write_vio) { /* All write events */
    +    acme_process_write(contp, event, my_state);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +
    +  return 0;
    +}
    +
    +/* Read-request header continuation, used to kick off the server intercept if necessary */
    +static int
    +acme_hook(TSCont contp ATS_UNUSED, TSEvent event ATS_UNUSED, void *edata)
    +{
    +  TSMBuffer reqp;
    +  TSMLoc hdr_loc = NULL, url_loc = NULL;
    +  TSCont icontp;
    +  AcmeState *my_state;
    +  TSHttpTxn txnp = (TSHttpTxn)edata;
    +
    +  TSDebug(PLUGIN_NAME, "kicking off ACME hook");
    +
    +  if ((TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &reqp, &hdr_loc)) && (TS_SUCCESS == TSHttpHdrUrlGet(reqp, hdr_loc, &url_loc))) {
    +    int path_len = 0;
    +    const char *path = TSUrlPathGet(reqp, url_loc, &path_len);
    +
    +    /* Short circuit the / path, common case */
    +    if (!path || path_len < (strlen(ACME_WK_PATH) + 2) || *path != '.' || memcmp(path, ACME_WK_PATH, strlen(ACME_WK_PATH))) {
    +      TSDebug(PLUGIN_NAME, "skipping URL path = %.*s", path_len, path);
    +      goto cleanup;
    +    }
    +
    +    TSSkipRemappingSet(txnp, 1); /* not strictly necessary, but speed is everything these days */
    +
    +    /* This request is for us -- register our intercept */
    +    icontp = TSContCreate(acme_intercept, TSMutexCreate());
    +
    +    my_state = make_acme_state();
    +    open_acme_file(my_state, path + strlen(ACME_WK_PATH), path_len - strlen(ACME_WK_PATH));
    --- End diff --
    
    Where is this file going to be created? ``/var/run`` might be a reasonable choice, or possibly ``TS*DirGet()`` though I don't see an obvious candidate there.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59388313
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    +  TSVConnClose(my_state->net_vc);
    +  TSfree(my_state);
    +  TSContDestroy(contp);
    +}
    +
    +/* Add data to the output */
    +inline static int
    +add_data_to_resp(const char *buf, int len, AcmeState *my_state)
    +{
    +  TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +  return len;
    +}
    +
    +static int
    +add_file_to_resp(AcmeState *my_state)
    +{
    +  if (-1 == my_state->fd) {
    +    return add_data_to_resp("\r\n", 2, my_state);
    +  } else {
    +    int ret = 0, len;
    +    char buf[8192];
    +
    +    while (1) {
    +      len = read(my_state->fd, buf, sizeof(buf));
    +      if ((0 == len) || ((-1 == len) && (errno != EAGAIN) && (errno != EINTR))) {
    +        break;
    +      } else {
    +        TSIOBufferWrite(my_state->resp_buffer, buf, len);
    +        ret += len;
    +      }
    +    }
    +    close(my_state->fd);
    +
    +    return ret;
    +  }
    +}
    +
    +/* Process a read event from the SM */
    +static void
    +acme_process_read(TSCont contp, TSEvent event, AcmeState *my_state)
    +{
    +  if (event == TS_EVENT_VCONN_READ_READY) {
    +    if (-1 == my_state->fd) {
    +      my_state->output_bytes = add_data_to_resp(ACME_DENIED_RESP, strlen(ACME_DENIED_RESP), my_state);
    +    } else {
    +      my_state->output_bytes = add_data_to_resp(ACME_OK_RESP, strlen(ACME_OK_RESP), my_state);
    +    }
    +    TSVConnShutdown(my_state->net_vc, 1, 0);
    +    my_state->write_vio = TSVConnWrite(my_state->net_vc, contp, my_state->resp_reader, INT64_MAX);
    +  } else if (event == TS_EVENT_ERROR) {
    +    TSError("[%s] acme_process_read: Received TS_EVENT_ERROR", PLUGIN_NAME);
    +  } else if (event == TS_EVENT_VCONN_EOS) {
    +    /* client may end the connection, simply return */
    +    return;
    +  } else if (event == TS_EVENT_NET_ACCEPT_FAILED) {
    +    TSError("[%s] acme_process_read: Received TS_EVENT_NET_ACCEPT_FAILED", PLUGIN_NAME);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +}
    +
    +/* Process a write event from the SM */
    +static void
    +acme_process_write(TSCont contp, TSEvent event, AcmeState *my_state)
    +{
    +  if (event == TS_EVENT_VCONN_WRITE_READY) {
    +    char buf[64]; /* Plenty of space for CL: header */
    +    int len;
    +
    +    len = snprintf(buf, sizeof(buf) - 1, "Content-Length: %zd\r\n\r\n", my_state->stat_buf.st_size);
    +    my_state->output_bytes += add_data_to_resp(buf, len, my_state);
    +    my_state->output_bytes += add_file_to_resp(my_state);
    +
    +    TSVIONBytesSet(my_state->write_vio, my_state->output_bytes);
    +    TSVIOReenable(my_state->write_vio);
    +  } else if (TS_EVENT_VCONN_WRITE_COMPLETE) {
    +    cleanup(contp, my_state);
    +  } else if (event == TS_EVENT_ERROR) {
    +    TSError("[%s] acme_process_write: Received TS_EVENT_ERROR", PLUGIN_NAME);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +}
    +
    +/* Process the accept event from the SM */
    +static void
    +acme_process_accept(TSCont contp, AcmeState *my_state)
    +{
    +  my_state->req_buffer = TSIOBufferCreate();
    +  my_state->resp_buffer = TSIOBufferCreate();
    +  my_state->resp_reader = TSIOBufferReaderAlloc(my_state->resp_buffer);
    +  my_state->read_vio = TSVConnRead(my_state->net_vc, contp, my_state->req_buffer, INT64_MAX);
    +}
    +
    +/* Imlement the server intercept */
    +static int
    +acme_intercept(TSCont contp, TSEvent event, void *edata)
    +{
    +  AcmeState *my_state = TSContDataGet(contp);
    +
    +  if (event == TS_EVENT_NET_ACCEPT) {
    +    my_state->net_vc = (TSVConn)edata;
    +    acme_process_accept(contp, my_state);
    +  } else if (edata == my_state->read_vio) { /* All read events */
    +    acme_process_read(contp, event, my_state);
    +  } else if (edata == my_state->write_vio) { /* All write events */
    +    acme_process_write(contp, event, my_state);
    +  } else {
    +    TSReleaseAssert(!"Unexpected Event");
    +  }
    +
    +  return 0;
    +}
    +
    +/* Read-request header continuation, used to kick off the server intercept if necessary */
    +static int
    +acme_hook(TSCont contp ATS_UNUSED, TSEvent event ATS_UNUSED, void *edata)
    +{
    +  TSMBuffer reqp;
    +  TSMLoc hdr_loc = NULL, url_loc = NULL;
    +  TSCont icontp;
    +  AcmeState *my_state;
    +  TSHttpTxn txnp = (TSHttpTxn)edata;
    +
    +  TSDebug(PLUGIN_NAME, "kicking off ACME hook");
    +
    +  if ((TS_SUCCESS == TSHttpTxnClientReqGet(txnp, &reqp, &hdr_loc)) && (TS_SUCCESS == TSHttpHdrUrlGet(reqp, hdr_loc, &url_loc))) {
    +    int path_len = 0;
    +    const char *path = TSUrlPathGet(reqp, url_loc, &path_len);
    +
    +    /* Short circuit the / path, common case */
    +    if (!path || path_len < (strlen(ACME_WK_PATH) + 2) || *path != '.' || memcmp(path, ACME_WK_PATH, strlen(ACME_WK_PATH))) {
    --- End diff --
    
    You can replace the ``strlen`` with ``COUNTOF(ACME_WK_PATH) - 1``. 


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59384742
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    --- End diff --
    
    ``struct AcemeState``


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by zwoop <gi...@git.apache.org>.
Github user zwoop commented on the pull request:

    https://github.com/apache/trafficserver/pull/560#issuecomment-208591504
  
    Looks ok, +1.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59386294
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    +  TSVConnClose(my_state->net_vc);
    +  TSfree(my_state);
    +  TSContDestroy(contp);
    +}
    +
    +/* Add data to the output */
    +inline static int
    +add_data_to_resp(const char *buf, int len, AcmeState *my_state)
    --- End diff --
    
    My preference would be to not abbreviate, i.e. ``add_data_to_response()``.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by jpeach <gi...@git.apache.org>.
Github user jpeach commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59386188
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    --- End diff --
    
    Close the file descriptor?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---

[GitHub] trafficserver pull request: TS-4320: Add ACME Plugin

Posted by zwoop <gi...@git.apache.org>.
Github user zwoop commented on a diff in the pull request:

    https://github.com/apache/trafficserver/pull/560#discussion_r59396994
  
    --- Diff: plugins/experimental/acme/acme.c ---
    @@ -0,0 +1,347 @@
    +/** @file
    +
    +@section license
    +
    +Licensed under the Apache License, Version 2.0 (the "License");
    +you may not use this file except in compliance with the License.
    +You may obtain a copy of the License at
    +
    +http://www.apache.org/licenses/LICENSE-2.0
    +
    +Unless required by applicable law or agreed to in writing, software
    +distributed under the License is distributed on an "AS IS" BASIS,
    +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    +See the License for the specific language governing permissions and
    +limitations under the License.
    + */
    +
    +#include <stdio.h>
    +#include <stdlib.h>
    +#include <ctype.h>
    +#include <string.h>
    +#include <sys/types.h>
    +#include <unistd.h>
    +#include <getopt.h>
    +#include <sys/stat.h>
    +
    +#include "ts/ts.h"
    +#include "ts/ink_platform.h"
    +#include "ts/ink_defs.h"
    +
    +static const char PLUGIN_NAME[] = "acme";
    +static const char ACME_WK_PATH[] = ".well-known/acme-challenge/";
    +static const char ACME_OK_RESP[] = "HTTP/1.1 200 OK\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +static const char ACME_DENIED_RESP[] = "HTTP/1.1 404 Not Found\r\nContent-Type: application/jose\r\nCache-Control: no-cache\r\n";
    +
    +#define MAX_PATH_LEN 4096
    +
    +/* This should hold all configurations going forward. */
    +typedef struct AcmeConfig_t {
    +  char *proof;
    +} AcmeConfig;
    +AcmeConfig gConfig;
    +
    +/* State used for the intercept plugin. ToDo: Can this be improved ? */
    +typedef struct AcmeState_t {
    +  TSVConn net_vc;
    +  TSVIO read_vio;
    +  TSVIO write_vio;
    +
    +  TSIOBuffer req_buffer;
    +  TSIOBuffer resp_buffer;
    +  TSIOBufferReader resp_reader;
    +
    +  int output_bytes;
    +  int fd;
    +  struct stat stat_buf;
    +} AcmeState;
    +
    +
    +inline static AcmeState *
    +make_acme_state()
    +{
    +  AcmeState *state = (AcmeState *)TSmalloc(sizeof(AcmeState));
    +
    +  memset(state, 0, sizeof(AcmeState));
    +  state->fd = -1;
    +
    +  return state;
    +}
    +
    +/* Create a safe pathname to the proof-type file, the destination must be sufficiently large. */
    +static int
    +make_absolute_path(char *dest, int dest_len, const char *file, int file_len)
    +{
    +  int i;
    +
    +  for (i = 0; i < file_len; ++i) {
    +    char c = file[i];
    +
    +    /* Assure that only Base64-URL chracter are in the path */
    +    if (!(c == 45 || c == 95 || (c >= 48 && c <= 57) || (c >= 65 && c <= 90) || (c >= 97 && c <= 122))) {
    +      TSDebug(PLUGIN_NAME, "Invalid Base64 character found, error");
    +      return 0;
    +    }
    +  }
    +
    +  return snprintf(dest, dest_len - 1, "%s/%.*s", gConfig.proof, file_len, file);
    +}
    +
    +static void
    +open_acme_file(AcmeState *state, const char *file, int file_len)
    +{
    +  char fname[MAX_PATH_LEN];
    +  int len = make_absolute_path(fname, MAX_PATH_LEN - 1, file, file_len);
    +
    +  /* 1. Make sure the filename is reasonable */
    +  if (!len || (len >= (MAX_PATH_LEN - 1))) {
    +    TSDebug(PLUGIN_NAME, "invalid filename");
    +    return;
    +  }
    +
    +  /* 2. Open and stat() the file */
    +  state->fd = open(fname, O_RDONLY);
    +  if (-1 == state->fd) {
    +    TSDebug(PLUGIN_NAME, "can not open file %s", fname);
    +    return;
    +  } else if (fstat(state->fd, &state->stat_buf)) {
    +    TSDebug(PLUGIN_NAME, "can not stat() file %s", fname);
    +    close(state->fd);
    +    state->fd = -1;
    +
    +    return;
    +  }
    +
    +  TSDebug(PLUGIN_NAME, "opened filename of %s for read()", fname);
    +  return;
    +}
    +
    +
    +/* Cleanup after intercept has completed */
    +static void
    +cleanup(TSCont contp, AcmeState *my_state)
    +{
    +  if (my_state->req_buffer) {
    +    TSIOBufferDestroy(my_state->req_buffer);
    +    my_state->req_buffer = NULL;
    +  }
    +
    +  if (my_state->resp_buffer) {
    +    TSIOBufferDestroy(my_state->resp_buffer);
    +    my_state->resp_buffer = NULL;
    +  }
    +
    --- End diff --
    
    What file descriptor? It should have been closed elsewhere, no ?


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---