# But I couldn't make check work with AC_SEARCH_LIBS, and (probably due to
# typical obscure bullshit autotools reasoning) I have no idea why.
PKG_CHECK_MODULES([JANSSON], [jansson])
+PKG_CHECK_MODULES([CURL], [libcurl])
PKG_CHECK_MODULES([CHECK], [check], [usetests=yes], [usetests=no])
AM_CONDITIONAL([USE_TESTS], [test "x$usetests" = "xyes"])
1. [jansson](http://www.digip.org/jansson/)
2. libcrypto (Either [LibreSSL](http://www.libressl.org/) or [OpenSSL](https://www.openssl.org/) >= 1.1)
3. [rsync](http://rsync.samba.org/)
+4. [libcurl](https://curl.haxx.se/libcurl/)
Fort is currently supported in *64-bit* OS. A 32-bit OS may face the [Year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) when handling dates at certificates, and currently there's no work around for this.
### Debian version
{% highlight bash %}
-sudo apt install autoconf automake build-essential libjansson-dev libssl-dev pkg-config rsync
+sudo apt install autoconf automake build-essential libjansson-dev libssl-dev pkg-config rsync libcurl4
wget https://github.com/NICMx/FORT-validator/releases/download/v{{ site.fort-latest-version }}/fort-{{ site.fort-latest-version }}.tar.gz
tar xvzf fort-{{ site.fort-latest-version }}.tar.gz
The following example is the processo to clone, compile and install in Debian OS.
{% highlight bash %}
-sudo apt install autoconf automake build-essential git libjansson-dev libssl-dev pkg-config rsync
+sudo apt install autoconf automake build-essential git libjansson-dev libssl-dev pkg-config rsync libcurl4-openssl-dev
git clone https://github.com/NICMx/FORT-validator.git
cd FORT-validator/
19. [`--log.output`](#--logoutput)
20. [`--log.color-output`](#--logcolor-output)
21. [`--log.file-name-format`](#--logfile-name-format)
- 22. [`--output.roa`](#--outputroa)
- 23. [`--output.bgpsec`](#--outputbgpsec)
- 24. [`--asn1-decode-max-stack`](#--asn1-decode-max-stack)
- 25. [`--configuration-file`](#--configuration-file)
- 26. [`rsync.program`](#rsyncprogram)
- 27. [`rsync.arguments-recursive`](#rsyncarguments-recursive)
- 28. [`rsync.arguments-flat`](#rsyncarguments-flat)
- 29. [`incidences`](#incidences)
+ 22. [`--http.user-agent`](#--httpuser-agent)
+ 23. [`--http.connect-timeout`](#--httpconnect-timeout)
+ 24. [`--http.transfer-timeout`](#--httptransfer-timeout)
+ 25. [`--http.ca-path`](#--httpca-path)
+ 26. [`--output.roa`](#--outputroa)
+ 27. [`--output.bgpsec`](#--outputbgpsec)
+ 28. [`--asn1-decode-max-stack`](#--asn1-decode-max-stack)
+ 29. [`--configuration-file`](#--configuration-file)
+ 30. [`rsync.program`](#rsyncprogram)
+ 31. [`rsync.arguments-recursive`](#rsyncarguments-recursive)
+ 32. [`rsync.arguments-flat`](#rsyncarguments-flat)
+ 33. [`incidences`](#incidences)
## Syntax
[--log.output=syslog|console]
[--log.color-output]
[--log.file-name-format=global-url|local-path|file-name]
+ [--http.user-agent=<string>]
+ [--http.connect-timeout=<unsigned integer>]
+ [--http.transfer-timeout=<unsigned integer>]
+ [--http.ca-path=<directory>]
[--output.roa=<file>]
[--output.bgpsec=<file>]
```
This flag affects any of the log output configured at [`--log.output`](#--logoutput) (`syslog` and `console`).
+### `--http.user-agent`
+
+- **Type:** String
+- **Availability:** `argv` and JSON
+- **Default:** `{{ page.command }}/{{ site.fort-latest-version }}`
+
+_**All requests are made using HTTPS, verifying the peer and the certificate name vs host**_
+
+User-Agent to use at HTTP requests.
+
+The value specified (either by the argument or the default value) is utilized in libcurl's option [CURLOPT_USERAGENT](https://curl.haxx.se/libcurl/c/CURLOPT_USERAGENT.html).
+
+### `--http.connect-timeout`
+
+- **Type:** Integer
+- **Availability:** `argv` and JSON
+- **Default:** 30
+- **Range:** 1--[`UINT_MAX`](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
+
+_**All requests are made using HTTPS, verifying the peer and the certificate name vs host**_
+
+Timeout (in seconds) for the connect phase.
+
+Whenever an HTTP connection will try to be established, the validator will wait a maximum of `http.connect-timeout` for the peer to respond to the connection request; if the timeout is reached, the connection attempt will be ceased.
+
+The value specified (either by the argument or the default value) is utilized in libcurl's option [CURLOPT_CONNECTTIMEOUT](https://curl.haxx.se/libcurl/c/CURLOPT_CONNECTTIMEOUT.html).
+
+### `--http.transfer-timeout`
+
+- **Type:** Integer
+- **Availability:** `argv` and JSON
+- **Default:** 30
+- **Range:** 0--[`UINT_MAX`](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
+
+_**All requests are made using HTTPS, verifying the peer and the certificate name vs host**_
+
+Maximum time in seconds (once the connection is established) that the request can last.
+
+Once the connection is established with the server, the request will last a maximum of `http.transfer-timeout` seconds. A value of 0 means unlimited time (use with caution).
+
+The value specified (either by the argument or the default value) is utilized in libcurl's option [CURLOPT_TIMEOUT](https://curl.haxx.se/libcurl/c/CURLOPT_TIMEOUT.html).
+
+### `--http.ca-path`
+
+- **Type:** String (Path to directory)
+- **Availability:** `argv` and JSON
+
+_**All requests are made using HTTPS, verifying the peer and the certificate name vs host**_
+
+Local path where the CA's utilized to verify the peers are located.
+
+Useful when the CA from the peer isn't located at the default OS certificate bundle. If specified, the peer certificate will be verified using the CAs at the path. The directory MUST be prepared using the `rehash` utility from the SSL library:
+- OpenSSL command (with help): `$ openssl rehash -h`
+- LibreSSL command (with help): `$ openssl certhash -h`
+
+The value specified is utilized in libcurl's option [CURLOPT_CAPATH](https://curl.haxx.se/libcurl/c/CURLOPT_CAPATH.html).
+
### `--output.roa`
- **Type:** String (Path to file)
"<a href="#--logfile-name-format">file-name-format</a>": "file-name"
},
+ "http": {
+ "<a href="#--httpuser-agent">user-agent</a>": "{{ page.command }}/{{ site.fort-latest-version }}",
+ "<a href="#--httpconnect-timeout">connect-timeout</a>": 30,
+ "<a href="#--httptransfer-timeout">transfer-timeout</a>": 30,
+ "<a href="#--httpca-path">ca-path</a>": "/usr/local/ssl/certs"
+ },
+
"rsync": {
"<a href="#rsyncprogram">program</a>": "rsync",
"<a href="#rsyncarguments-recursive">arguments-recursive</a>": [
"color-output": false,
"file-name-format": "global-url"
},
+ "http": {
+ "user-agent": "fort/1.2.0",
+ "connect-timeout": 30,
+ "transfer-timeout": 30,
+ "ca-path": "/usr/local/ssl/certs"
+ },
"rsync": {
"program": "rsync",
"arguments-recursive": [
.P
.RE
+.BR \-\-http.user\-agent=\fISTRING\fR
+.RS 4
+User-Agent to use at HTTP requests.
+.P
+The value specified (either by the argument or the default value) is utilized
+in libcurl’s option \fICURLOPT_USERAGENT\fR.
+.P
+By default, the value is \fIfort/<current-version>\fR.
+.RE
+.P
+
+.B \-\-http.connect\-timeout=\fIUNSIGNED_INTEGER\fR
+.RS 4
+Timeout (in seconds) for the connect phase.
+.P
+Whenever an HTTP connection will try to be established, the validator will wait
+a maximum of \fBhttp.connect-timeout\fR seconds for the peer to respond to the
+connection request; if the timeout is reached, the connection attempt will be
+ceased.
+.P
+The value specified (either by the argument or the default value) is utilized
+in libcurl’s option \fICURLOPT_CONNECTTIMEOUT\fR.
+.P
+By default, it has a value of \fI30\fR. The minimum allowed value is \fI1\fR.
+.RE
+.P
+
+.B \-\-http.transfer\-timeout=\fIUNSIGNED_INTEGER\fR
+.RS 4
+Maximum time in seconds (once the connection is established) that the request
+can last.
+.P
+Once the connection is established with the server, the request will last a
+maximum of \fBhttp.transfer-timeout\fR seconds. A value of \fI0\fR means
+unlimited time (use with caution).
+.P
+The value specified (either by the argument or the default value) is utilized
+in libcurl’s option \fICURLOPT_TIMEOUT\fR.
+.P
+By default, it has a value of \fI30\fR. The minimum allowed value is \fI0\fR.
+.RE
+.P
+
+.B \-\-http.ca-path=\fIDIRECTORY\fR
+.RS 4
+Local path where the CA’s utilized to verify the peers are located.
+.P
+Useful when the CA from the peer isn’t located at the default OS certificate
+bundle. If specified, the peer certificate will be verified using the CAs at
+the path. The directory MUST be prepared using the \fIrehash\fR utility from
+the SSL library:
+.RS 4
+.br
+\- OpenSSL command (with help):
+.B $ openssl rehash \-h
+.br
+\- LibreSSL command (with help):
+.B $ openssl certhash \-h
+.RE
+.P
+The value specified is utilized in libcurl’s option \fICURLOPT_CAPATH\fR.
+.P
+By default, the path has a NULL value.
+.RE
+.P
+
.B \-\-output.roa=\fIFILE\fR
.RS 4
File where the ROAs will be printed in CSV format.
"color-output": true,
"file-name-format": "local-path"
},
+ "http": {
+ "user-agent": "fort/1.2.0",
+ "connect-timeout": 30,
+ "transfer-timeout": 30,
+ "ca-path": "/usr/local/ssl/certs"
+ },
"rsync": {
"program": "rsync",
"arguments-recursive": [
fort_SOURCES += data_structure/uthash.h
fort_SOURCES += data_structure/uthash_nonfatal.h
+fort_SOURCES += http/http.h http/http.c
+
fort_SOURCES += incidence/incidence.h incidence/incidence.c
fort_SOURCES += object/bgpsec.h object/bgpsec.c
#fort_CFLAGS += $(GCC_WARNS)
fort_CFLAGS += -std=gnu11 -O2 -g $(FORT_FLAGS)
fort_LDFLAGS = $(LDFLAGS_DEBUG)
-fort_LDADD = ${JANSSON_LIBS}
+fort_LDADD = ${JANSSON_LIBS} ${CURL_LIBS}
# I'm tired of scrolling up, but feel free to comment this out.
GCC_WARNS = -fmax-errors=1
{
return inet_ntop(AF_INET6, addr, buffer, INET6_ADDRSTRLEN);
}
+
+static int
+dir_exists(char const *path, bool *result)
+{
+ struct stat _stat;
+ char *last_slash;
+
+ last_slash = strrchr(path, '/');
+ if (last_slash == NULL) {
+ /*
+ * Simply because create_dir_recursive() has nothing meaningful
+ * to do when this happens. It's a pretty strange error.
+ */
+ *result = true;
+ return 0;
+ }
+
+ *last_slash = '\0';
+
+ if (stat(path, &_stat) == 0) {
+ if (!S_ISDIR(_stat.st_mode)) {
+ return pr_err("Path '%s' exists and is not a directory.",
+ path);
+ }
+ *result = true;
+ } else if (errno == ENOENT) {
+ *result = false;
+ } else {
+ return pr_errno(errno, "stat() failed");
+ }
+
+ *last_slash = '/';
+ return 0;
+}
+
+static int
+create_dir(char *path)
+{
+ int error;
+
+ error = mkdir(path, 0777);
+
+ if (error && errno != EEXIST)
+ return pr_errno(errno, "Error while making directory '%s'",
+ path);
+
+ return 0;
+}
+
+/**
+ * Apparently, RSYNC does not like to create parent directories.
+ * This function fixes that.
+ */
+int
+create_dir_recursive(char const *path)
+{
+ char *localuri;
+ int i, error;
+ bool exist = false;
+
+ error = dir_exists(path, &exist);
+ if (error)
+ return error;
+ if (exist)
+ return 0;
+
+ localuri = strdup(path);
+ if (localuri == NULL)
+ return pr_enomem();
+
+ for (i = 1; localuri[i] != '\0'; i++) {
+ if (localuri[i] == '/') {
+ localuri[i] = '\0';
+ error = create_dir(localuri);
+ localuri[i] = '/';
+ if (error) {
+ /* error msg already printed */
+ free(localuri);
+ return error;
+ }
+ }
+ }
+
+ free(localuri);
+ return 0;
+}
* start supporting them.)
*/
#define ENOTRSYNC 3174
-
+/*
+ * "URI was not HTTPS; ignore it."
+ * Not necessarily an error (just as ENOTRSYNC), since both type of URIs can
+ * still coexist in most scenarios.
+ */
+#define ENOTHTTPS 3175
/*
* If you're wondering why I'm not using -abs(error), it's because abs(INT_MIN)
* overflows, so gcc complains sometimes.
void rwlock_unlock(pthread_rwlock_t *);
/** Also boilerplate. */
-void close_thread(pthread_t thread, char const *what);
+void close_thread(pthread_t thread, char const *);
typedef int (*process_file_cb)(char const *, void *);
int process_file_or_dir(char const *, char const *, process_file_cb, void *);
char const *addr2str4(struct in_addr const *, char *);
char const *addr2str6(struct in6_addr const *, char *);
+int create_dir_recursive(char const *);
+
#endif /* SRC_RTR_COMMON_H_ */
} args;
} rsync;
+ struct {
+ /* User-Agent header set at requests */
+ char *user_agent;
+ /* Timeout in seconds for the connect phase */
+ unsigned int connect_timeout;
+ /* Maximum allowed time that a request can take */
+ unsigned int transfer_timeout;
+ /* Directory where CA certs to verify peers are found */
+ char *ca_path;
+ } http;
+
struct {
/** Print ANSI color codes? */
bool color;
.availability = AVAILABILITY_JSON,
},
+ /* HTTP requests parameters */
+ {
+ .id = 9000,
+ .name = "http.user-agent",
+ .type = >_string,
+ .offset = offsetof(struct rpki_config, http.user_agent),
+ .doc = "User-Agent to use at HTTP requests, eg. Fort Validator Local/1.0",
+ },
+ {
+ .id = 9001,
+ .name = "http.connect-timeout",
+ .type = >_uint,
+ .offset = offsetof(struct rpki_config, http.connect_timeout),
+ .doc = "Timeout for the connect phase",
+ .min = 1,
+ .max = UINT_MAX,
+ },
+ {
+ .id = 9002,
+ .name = "http.transfer-timeout",
+ .type = >_uint,
+ .offset = offsetof(struct rpki_config, http.transfer_timeout),
+ .doc = "Maximum request time (once the connection is established) before dropping the connection",
+ .min = 0,
+ .max = UINT_MAX,
+ },
+ {
+ .id = 9003,
+ .name = "http.ca-path",
+ .type = >_string,
+ .offset = offsetof(struct rpki_config, http.ca_path),
+ .doc = "Directory where CA certificates are found, used to verify the peer",
+ .arg_doc = "<directory>",
+ },
+
/* Logging fields */
{
.id = 'c',
if (error)
goto revert_recursive_array;
+ rpki_config.http.user_agent = strdup(PACKAGE_NAME "/" PACKAGE_VERSION);
+ if (rpki_config.http.user_agent == NULL) {
+ error = pr_enomem();
+ goto revert_recursive_array;
+ }
+ rpki_config.http.connect_timeout = 30;
+ rpki_config.http.transfer_timeout = 30;
+ rpki_config.http.ca_path = NULL; /* Use system default */
+
rpki_config.log.color = false;
rpki_config.log.filename_format = FNF_GLOBAL;
rpki_config.log.level = LOG_WARNING;
pr_crit("Invalid sync strategy: '%u'", rpki_config.sync_strategy);
}
+char const *
+config_get_http_user_agent(void)
+{
+ return rpki_config.http.user_agent;
+}
+
+unsigned int
+config_get_http_connect_timeout(void)
+{
+ return rpki_config.http.connect_timeout;
+}
+
+unsigned int
+config_get_http_transfer_timeout(void)
+{
+ return rpki_config.http.transfer_timeout;
+}
+
+char const *
+config_get_http_ca_path(void)
+{
+ return rpki_config.http.ca_path;
+}
+
char const *
config_get_output_roa(void)
{
enum mode config_get_mode(void);
bool config_get_color_output(void);
enum filename_format config_get_filename_format(void);
+char const *config_get_http_user_agent(void);
+unsigned int config_get_http_connect_timeout(void);
+unsigned int config_get_http_transfer_timeout(void);
+char const *config_get_http_ca_path(void);
uint8_t config_get_log_level(void);
enum log_output config_get_log_output(void);
char *config_get_rsync_program(void);
--- /dev/null
+#include "http.h"
+
+#include <curl/curl.h>
+#include <sys/stat.h>
+#include "common.h"
+#include "config.h"
+#include "file.h"
+#include "log.h"
+
+struct http_handler {
+ CURL *curl;
+ char errbuf[CURL_ERROR_SIZE];
+};
+
+int
+http_init(void)
+{
+ CURLcode res;
+ res = curl_global_init(CURL_GLOBAL_SSL);
+ if (res != CURLE_OK)
+ return pr_err("Error initializing global curl (%s)",
+ curl_easy_strerror(res));
+
+ return 0;
+}
+
+void
+http_cleanup(void)
+{
+ curl_global_cleanup();
+}
+
+static int
+http_easy_init(struct http_handler *handler)
+{
+ CURL *tmp;
+
+ tmp = curl_easy_init();
+ if (tmp == NULL)
+ return pr_enomem();
+
+ /* Use header always */
+ if (config_get_http_user_agent() != NULL)
+ curl_easy_setopt(tmp, CURLOPT_USERAGENT,
+ config_get_http_user_agent());
+ /* Only utilizes if indicated, otherwise use system default */
+ if (config_get_http_ca_path() != NULL)
+ curl_easy_setopt(tmp, CURLOPT_CAPATH,
+ config_get_http_ca_path());
+
+ curl_easy_setopt(tmp, CURLOPT_CONNECTTIMEOUT,
+ config_get_http_connect_timeout());
+ curl_easy_setopt(tmp, CURLOPT_TIMEOUT,
+ config_get_http_transfer_timeout());
+ curl_easy_setopt(tmp, CURLOPT_NOSIGNAL, 1);
+
+ /* Always expect HTTPS usage */
+ curl_easy_setopt(tmp, CURLOPT_SSL_VERIFYHOST, 2);
+ curl_easy_setopt(tmp, CURLOPT_SSL_VERIFYPEER, 1);
+
+ /* Currently all requests use GET */
+ curl_easy_setopt(tmp, CURLOPT_HTTPGET, 1);
+
+ /* Refer to its error buffer */
+ curl_easy_setopt(tmp, CURLOPT_ERRORBUFFER, handler->errbuf);
+
+ handler->curl = tmp;
+
+ return 0;
+}
+
+/*
+ * Fetch data from @uri and write result using @cb (which will receive @arg).
+ */
+static int
+http_fetch(struct http_handler *handler, char const *uri, http_write_cb cb,
+ void *arg)
+{
+ CURLcode res;
+
+ handler->errbuf[0] = 0;
+ curl_easy_setopt(handler->curl, CURLOPT_URL, uri);
+ curl_easy_setopt(handler->curl, CURLOPT_WRITEFUNCTION, cb);
+ curl_easy_setopt(handler->curl, CURLOPT_WRITEDATA, arg);
+
+ pr_debug("HTTP GET from '%s'.", uri);
+ res = curl_easy_perform(handler->curl);
+ if (res != CURLE_OK)
+ return pr_err("Error requesting URL %s: %s", uri,
+ strlen(handler->errbuf) > 0 ?
+ handler->errbuf : curl_easy_strerror(res));
+
+ return 0;
+}
+
+static void
+http_easy_cleanup(struct http_handler *handler)
+{
+ curl_easy_cleanup(handler->curl);
+}
+
+/*
+ * Try to download from global @uri into a local directory structure created
+ * from local @uri. The @cb should be utilized to write into a file; the file
+ * will be sent to @cb as the last argument (its a FILE reference).
+ */
+int
+http_download_file(struct rpki_uri *uri, http_write_cb cb)
+{
+ struct http_handler handler;
+ struct stat stat;
+ FILE *out;
+ int error;
+
+ error = create_dir_recursive(uri_get_local(uri));
+ if (error)
+ return error;
+
+ error = file_write(uri_get_local(uri), &out, &stat);
+ if (error)
+ return error;
+
+ error = http_easy_init(&handler);
+ if (error)
+ goto close_file;
+
+ error = http_fetch(&handler, uri_get_global(uri), cb, out);
+ http_easy_cleanup(&handler);
+ file_close(out);
+
+ /* Error 0 it's ok */
+ return error;
+close_file:
+ file_close(out);
+ return error;
+}
--- /dev/null
+#ifndef SRC_HTTP_HTTP_H_
+#define SRC_HTTP_HTTP_H_
+
+#include <stddef.h>
+#include "uri.h"
+
+int http_init(void);
+void http_cleanup(void);
+
+typedef size_t (http_write_cb)(unsigned char *, size_t, size_t, void *);
+int http_download_file(struct rpki_uri *, http_write_cb);
+
+#endif /* SRC_HTTP_HTTP_H_ */
#include "extension.h"
#include "nid.h"
#include "thread_var.h"
+#include "http/http.h"
#include "rtr/rtr.h"
#include "rtr/db/vrps.h"
if (error)
goto revert_nid;
+ error = http_init();
+ if (error)
+ goto revert_nid;
+
error = start_rtr_server();
+ http_cleanup();
revert_nid:
nid_destroy();
revert_config:
#include <sys/stat.h>
#include <sys/wait.h>
+#include "common.h"
#include "config.h"
#include "log.h"
#include "str.h"
pr_crit("Invalid sync strategy: %u", config_get_sync_strategy());
}
-static int
-dir_exists(char const *path, bool *result)
-{
- struct stat _stat;
- char *last_slash;
-
- last_slash = strrchr(path, '/');
- if (last_slash == NULL) {
- /*
- * Simply because create_dir_recursive() has nothing meaningful
- * to do when this happens. It's a pretty strange error.
- */
- *result = true;
- return 0;
- }
-
- *last_slash = '\0';
-
- if (stat(path, &_stat) == 0) {
- if (!S_ISDIR(_stat.st_mode)) {
- return pr_err("Path '%s' exists and is not a directory.",
- path);
- }
- *result = true;
- } else if (errno == ENOENT) {
- *result = false;
- } else {
- return pr_errno(errno, "stat() failed");
- }
-
- *last_slash = '/';
- return 0;
-}
-
-static int
-create_dir(char *path)
-{
- int error;
-
- error = mkdir(path, 0777);
-
- if (error && errno != EEXIST)
- return pr_errno(errno, "Error while making directory '%s'",
- path);
-
- return 0;
-}
-
-/**
- * Apparently, RSYNC does not like to create parent directories.
- * This function fixes that.
- */
-static int
-create_dir_recursive(struct rpki_uri *uri)
-{
- char *localuri;
- int i, error;
- bool exist = false;
-
- error = dir_exists(uri_get_local(uri), &exist);
- if (error)
- return error;
- if (exist)
- return 0;
-
- localuri = strdup(uri_get_local(uri));
- if (localuri == NULL)
- return pr_enomem();
-
- for (i = 1; localuri[i] != '\0'; i++) {
- if (localuri[i] == '/') {
- localuri[i] = '\0';
- error = create_dir(localuri);
- localuri[i] = '/';
- if (error) {
- /* error msg already printed */
- free(localuri);
- return error;
- }
- }
- }
-
- free(localuri);
- return 0;
-}
-
static void
handle_child_thread(struct rpki_uri *uri, bool is_ta)
{
int error;
child_status = 0;
- error = create_dir_recursive(uri);
+ error = create_dir_recursive(uri_get_local(uri));
if (error)
return error;
check_PROGRAMS = address.test
check_PROGRAMS += clients.test
check_PROGRAMS += db_table.test
+check_PROGRAMS += http.test
check_PROGRAMS += line_file.test
check_PROGRAMS += pdu_handler.test
check_PROGRAMS += rsync.test
db_table_test_SOURCES = rtr/db/db_table_test.c
db_table_test_LDADD = ${MY_LDADD}
+http_test_SOURCES = http_test.c
+http_test_LDADD = ${MY_LDADD} ${CURL_LIBS}
+
line_file_test_SOURCES = line_file_test.c
line_file_test_LDADD = ${MY_LDADD}
--- /dev/null
+#include <check.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "common.c"
+#include "file.c"
+#include "impersonator.c"
+#include "log.c"
+#include "uri.c"
+#include "http/http.c"
+
+struct response {
+ unsigned char *content;
+ size_t size;
+};
+
+static void
+init_response(struct response *resp)
+{
+ resp->size = 0;
+ resp->content = malloc(sizeof(char));
+}
+
+static size_t
+write_cb(unsigned char *content, size_t size, size_t nmemb, void *arg)
+{
+ struct response *resp = arg;
+ unsigned char *tmp;
+ size_t read = size * nmemb;
+
+ tmp = realloc(resp->content, resp->size + read + 1);
+ if (tmp == NULL)
+ return -EINVAL;
+
+ resp->content = tmp;
+ memcpy(&resp->content[resp->size], content, read);
+ resp->size += read;
+ resp->content[resp->size] = 0;
+
+ return read;
+}
+
+static int
+local_download(char const *url, struct response *resp)
+{
+ struct http_handler handler;
+ int error;
+
+ error = http_easy_init(&handler);
+ if (error)
+ return error;
+
+ error = http_fetch(&handler, url, write_cb, resp);
+ http_easy_cleanup(&handler);
+ return error;
+}
+
+START_TEST(http_fetch_normal)
+{
+ struct response resp;
+ char const *url = "https://rrdp.ripe.net/notification.xml";
+
+ init_response(&resp);
+
+ ck_assert_int_eq(http_init(), 0);
+ ck_assert_int_eq(local_download(url, &resp), 0);
+ ck_assert_int_gt(resp.size, 0);
+
+ http_cleanup();
+ free(resp.content);
+}
+END_TEST
+
+Suite *http_load_suite(void)
+{
+ Suite *suite;
+ TCase *fetch;
+
+ fetch = tcase_create("Fetch");
+ tcase_add_test(fetch, http_fetch_normal);
+ tcase_set_timeout(fetch, 60);
+
+ suite = suite_create("http_test()");
+ suite_add_tcase(suite, fetch);
+
+ return suite;
+}
+
+int main(void)
+{
+ Suite *suite;
+ SRunner *runner;
+ int tests_failed;
+
+ suite = http_load_suite();
+
+ runner = srunner_create(suite);
+ srunner_run_all(runner, CK_NORMAL);
+ tests_failed = srunner_ntests_failed(runner);
+ srunner_free(runner);
+
+ return (tests_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
{
/* Nothing needed here */
}
+
+/* Impersonate HTTP config */
+char const *
+config_get_http_user_agent(void)
+{
+ return "Test/0.1";
+}
+
+unsigned int
+config_get_http_connect_timeout(void)
+{
+ return 30;
+}
+
+unsigned int
+config_get_http_transfer_timeout(void)
+{
+ return 30;
+}
+char const *
+config_get_http_ca_path(void)
+{
+ return NULL;
+}