aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2020-02-26 15:48:20 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2020-02-27 17:40:57 +0200
commit3b2d57c14f1ed207c79362b7136e3da4054ad817 (patch)
tree8f97331e65e3d6cc0d483fc0c3d11a2e668e33be
parenta9a6567bac8ab7729c243b555e7fb3c4bb2c6e51 (diff)
downloadping903-3b2d57c14f1ed207c79362b7136e3da4054ad817.tar.gz
ping903-3b2d57c14f1ed207c79362b7136e3da4054ad817.tar.bz2
Implememt client-side basic auth
* lib/Makefile.am: Add base64.c * lib/base64.c: New file. * lib/basicauth.c: Move base64 support to a separate source. * lib/basicauth.h (base64_encode,base64_decode): New protos. * src/strsplit.c: New file. * src/Makefile.am (libping903_a_SOURCES): Add strsplit.c * src/defs.h (CRED_FILE_NAME): New macro. (ecalloc,strsplit,argcv_free): New proto. * src/mem.c (ecalloc): New function. * src/ping903.c (strsplit): Remove. (cf_auth): Use modified strsplit. * src/ping903q.c (http_query): Attempt to authenticate if basic auth is required. * examples/lib/LWP/Ping903.pm: New file. * examples/dbload: Use LWP::Ping903 * examples/inspect: Likewise. * examples/ipadd: Likewise. * examples/ipdel: Likewise.
-rwxr-xr-xexamples/dbload4
-rwxr-xr-xexamples/inspect17
-rwxr-xr-xexamples/ipadd4
-rwxr-xr-xexamples/ipdel4
-rw-r--r--examples/lib/LWP/Ping903.pm49
-rw-r--r--lib/Makefile.am1
-rw-r--r--lib/base64.c85
-rw-r--r--lib/basicauth.c44
-rw-r--r--lib/basicauth.h5
-rw-r--r--src/Makefile.am2
-rw-r--r--src/defs.h12
-rw-r--r--src/mem.c9
-rw-r--r--src/ping903.c88
-rw-r--r--src/ping903q.c302
-rw-r--r--src/strsplit.c118
15 files changed, 599 insertions, 145 deletions
diff --git a/examples/dbload b/examples/dbload
index 6b776e4..f3e9ec2 100755
--- a/examples/dbload
+++ b/examples/dbload
@@ -164,7 +164,7 @@ L<DBI>.
use strict;
use warnings;
use JSON;
-use LWP::UserAgent;
+use LWP::Ping903;
use DBI;
use Getopt::Long qw(:config gnu_getopt no_ignore_case);
use File::Spec;
@@ -238,7 +238,7 @@ my $json_text = JSON->new->encode({
'ip-list' => [ map { $_->[0] } @$res ]
});
-my $ua = new LWP::UserAgent;
+my $ua = new LWP::Ping903;
my $response = $ua->post("$baseurl/config/ip-list",
'Content-Type' => 'application/json',
diff --git a/examples/inspect b/examples/inspect
index beebc17..6aacad4 100755
--- a/examples/inspect
+++ b/examples/inspect
@@ -57,13 +57,16 @@ L<DBI>.
use strict;
use warnings;
-use LWP::UserAgent;
+use LWP::Ping903;
+use HTTP::Status qw(:constants);
use JSON;
use Getopt::Long qw(:config gnu_getopt no_ignore_case);
use Pod::Usage;
use Pod::Man;
my $baseurl = 'http://localhost:8080';
+my $user;
+my $password;
GetOptions(
'U|url=s' => \$baseurl,
@@ -80,9 +83,17 @@ GetOptions(
) or exit(1);
die "too many arguments; try `$0 --help' for more info\n" if @ARGV;
-my $ua = new LWP::UserAgent;
+my $ua = new LWP::Ping903;
+
my $response = $ua->get("$baseurl/config");
unless ($response->is_success) {
+ if ($response->code eq HTTP_UNAUTHORIZED) {
+ my $s = $response->header('WWW-Authenticate');
+ $s =~ s/Basic realm=//;
+ $s =~ s/^"(.*)"$/$1/;
+ $s =~ s/\\([\\\"])/$1/g;
+ die "$s: not authorized";
+ }
die $response->status_line;
}
@@ -111,5 +122,3 @@ if (@{$resp->{'ip-list'}}) {
}
print "EOF\n";
}
-
-
diff --git a/examples/ipadd b/examples/ipadd
index f001950..d6195da 100755
--- a/examples/ipadd
+++ b/examples/ipadd
@@ -57,7 +57,7 @@ L<DBI>.
use strict;
use warnings;
-use LWP::UserAgent;
+use LWP::Ping903;
use JSON;
use Getopt::Long qw(:config gnu_getopt no_ignore_case);
use Pod::Usage;
@@ -82,7 +82,7 @@ GetOptions(
my $ip = shift @ARGV or die "not enough arguments";
die "too many arguments; try `$0 --help' for more info\n" if @ARGV;
-my $ua = new LWP::UserAgent;
+my $ua = new LWP::Ping903;
my $response = $ua->put("$baseurl/config/ip-list/$ip");
unless ($response->is_success) {
print $response->status_line,"\n";
diff --git a/examples/ipdel b/examples/ipdel
index fa4cb8f..8eed7eb 100755
--- a/examples/ipdel
+++ b/examples/ipdel
@@ -57,7 +57,7 @@ L<DBI>.
use strict;
use warnings;
-use LWP::UserAgent;
+use LWP::Ping903;
use JSON;
use Getopt::Long qw(:config gnu_getopt no_ignore_case);
use Pod::Usage;
@@ -83,7 +83,7 @@ my $ip = shift @ARGV or die "not enough arguments";
die "too many arguments; try `$0 --help' for more info\n" if @ARGV;
-my $ua = new LWP::UserAgent;
+my $ua = new LWP::Ping903;
my $response = $ua->delete("$baseurl/config/ip-list/$ip");
unless ($response->is_success) {
print $response->status_line,"\n";
diff --git a/examples/lib/LWP/Ping903.pm b/examples/lib/LWP/Ping903.pm
new file mode 100644
index 0000000..8fc398e
--- /dev/null
+++ b/examples/lib/LWP/Ping903.pm
@@ -0,0 +1,49 @@
+package LWP::Ping903;
+use parent 'LWP::UserAgent';
+use File::Spec;
+use HTTP::Status qw(:constants);
+use strict;
+use warnings;
+use Carp;
+
+my $VERSION = '0.3';
+
+sub new {
+ my $class = shift;
+ my $self = bless $class->SUPER::new(@_), $class;
+ $self->agent("$class/$VERSION");
+ return $self;
+}
+
+sub get_basic_credentials {
+ my ($self, $realm, $uri, $isproxy) = @_;
+ my $cf = File::Spec->catfile($ENV{HOME}, ".ping903.cred");
+ if (open(my $fh, '<', $cf)) {
+ while (<$fh>) {
+ chomp;
+ s/^\s+//;
+ s/\s+$//;
+ next if /^(#.*)?$/;
+ my @words;
+ while ($_) {
+ no warnings 'uninitialized';
+ if (s/^"(.*?)(?<!\\)"(?:\s+(.*))?$/$2/) {
+ (my $s = $1) =~ s/\\([\\\"])/$1/g;
+ push @words, $s;
+ } else {
+ s/^(.+?)(?:\s+(.+))?$/$2/;
+ push @words, $1;
+ }
+ }
+ if (@words == 4) {
+ my($h,$p) = split /:/, $words[0], 2;
+ if (($h eq '*' || $h eq $uri->host)
+ && (!$p || $p eq '*' || $p eq $uri->port)
+ && ($words[1] eq $realm || $words[1] eq '*')) {
+ return @words[2..3];
+ }
+ }
+ }
+ }
+ return ();
+}
diff --git a/lib/Makefile.am b/lib/Makefile.am
index dd9386d..03ce8e9 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,6 +1,7 @@
noinst_LIBRARIES = libdigest.a
libdigest_a_SOURCES = \
apr.c\
+ base64.c\
basicauth.c\
md5.c\
sha1.c
diff --git a/lib/base64.c b/lib/base64.c
new file mode 100644
index 0000000..ad07a5a
--- /dev/null
+++ b/lib/base64.c
@@ -0,0 +1,85 @@
+#include <config.h>
+#include <stdlib.h>
+#include <errno.h>
+
+static char b64tab[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int
+base64_encode(const unsigned char *input, size_t input_len,
+ unsigned char **output, size_t *output_len)
+{
+ size_t olen = 4 * (input_len + 2) / 3 + 1;
+ unsigned char *out = malloc(olen);
+
+ if (!out)
+ return -1;
+ *output = out;
+ while (input_len >= 3) {
+ *out++ = b64tab[input[0] >> 2];
+ *out++ = b64tab[((input[0] << 4) & 0x30) | (input[1] >> 4)];
+ *out++ = b64tab[((input[1] << 2) & 0x3c) | (input[2] >> 6)];
+ *out++ = b64tab[input[2] & 0x3f];
+ input_len -= 3;
+ input += 3;
+ }
+
+ if (input_len > 0) {
+ unsigned char c = (input[0] << 4) & 0x30;
+ *out++ = b64tab[input[0] >> 2];
+ if (input_len > 1)
+ c |= input[1] >> 4;
+ *out++ = b64tab[c];
+ *out++ = (input_len < 2) ?
+ '=' : b64tab[(input[1] << 2) & 0x3c];
+ *out++ = '=';
+ }
+ *output_len = out - *output;
+ *out = 0;
+ return 0;
+}
+
+static int b64val[128] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
+};
+
+int
+base64_decode(const unsigned char *input, size_t input_len,
+ unsigned char *output, size_t output_len)
+{
+ unsigned char *out = output;
+#define AC(c) do { if (output_len-- == 0) return -1; *out++ = (c); } while (0)
+
+ if (!out)
+ return -1;
+
+ do {
+ if (input[0] > 127 || b64val[input[0]] == -1 ||
+ input[1] > 127 || b64val[input[1]] == -1 ||
+ input[2] > 127 ||
+ ((input[2] != '=') && (b64val[input[2]] == -1)) ||
+ input[3] > 127 ||
+ ((input[3] != '=') && (b64val[input[3]] == -1))) {
+ errno = EINVAL;
+ return -1;
+ }
+ AC((b64val[input[0]] << 2) | (b64val[input[1]] >> 4));
+ if (input[2] != '=') {
+ AC(((b64val[input[1]] << 4) & 0xf0) |
+ (b64val[input[2]] >> 2));
+ if (input[3] != '=')
+ AC(((b64val[input[2]] << 6) & 0xc0) |
+ b64val[input[3]]);
+ }
+ input += 4;
+ input_len -= 4;
+ } while (input_len > 0);
+ return out - output;
+}
diff --git a/lib/basicauth.c b/lib/basicauth.c
index 4d313e6..4a94726 100644
--- a/lib/basicauth.c
+++ b/lib/basicauth.c
@@ -7,51 +7,7 @@
#include "md5.h"
#include "basicauth.h"
-static int b64val[128] = {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
- -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
- -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
- 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
-};
-static int
-base64_decode(const unsigned char *input, size_t input_len,
- unsigned char *output, size_t output_len)
-{
- unsigned char *out = output;
-#define AC(c) do { if (output_len-- == 0) return -1; *out++ = (c); } while (0)
-
- if (!out)
- return -1;
-
- do {
- if (input[0] > 127 || b64val[input[0]] == -1 ||
- input[1] > 127 || b64val[input[1]] == -1 ||
- input[2] > 127 ||
- ((input[2] != '=') && (b64val[input[2]] == -1)) ||
- input[3] > 127 ||
- ((input[3] != '=') && (b64val[input[3]] == -1))) {
- errno = EINVAL;
- return -1;
- }
- AC((b64val[input[0]] << 2) | (b64val[input[1]] >> 4));
- if (input[2] != '=') {
- AC(((b64val[input[1]] << 4) & 0xf0) |
- (b64val[input[2]] >> 2));
- if (input[3] != '=')
- AC(((b64val[input[2]] << 6) & 0xc0) |
- b64val[input[3]]);
- }
- input += 4;
- input_len -= 4;
- } while (input_len > 0);
- return out - output;
-}
-
static pthread_mutex_t crypt_mutex = PTHREAD_MUTEX_INITIALIZER;
static int
diff --git a/lib/basicauth.h b/lib/basicauth.h
index 49b504f..bf6d71c 100644
--- a/lib/basicauth.h
+++ b/lib/basicauth.h
@@ -6,3 +6,8 @@ typedef enum {
} BASICAUTH_RESULT;
BASICAUTH_RESULT basicauth(char const *file, char const *input);
+
+int base64_encode(const unsigned char *input, size_t input_len,
+ unsigned char **output, size_t *output_len);
+int base64_decode(const unsigned char *input, size_t input_len,
+ unsigned char *output, size_t output_len);
diff --git a/src/Makefile.am b/src/Makefile.am
index baac48f..6dfd73a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -34,7 +34,7 @@ ping903q_SOURCES = ping903q.c
LDADD = ./libping903.a ../lib/libdigest.a -lcrypt
noinst_LIBRARIES = libping903.a
-libping903_a_SOURCES = json.c json.h mem.c logger.c defs.h
+libping903_a_SOURCES = json.c json.h mem.c logger.c strsplit.c defs.h
EXTRA_DIST = ping903.conf
diff --git a/src/defs.h b/src/defs.h
index 17b1b1b..3b4dbaf 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -17,6 +17,7 @@
#endif
#define DEFAULT_CONFIG_FILE SYSCONFDIR "/" PACKAGE ".conf"
#define LOCAL_IP_LIST_FILE PACKAGESTATEDIR "/ip-list"
+#define CRED_FILE_NAME ".ping903.cred"
#define COPYLEFT "\
Copyright (C) 2020 Sergey Poznyakoff\n\
@@ -27,6 +28,7 @@ There is NO WARRANTY, to the extent permitted by law.\n\
void emalloc_die(void);
void *emalloc(size_t s);
+void *ecalloc(size_t n, size_t s);
char *estrdup(char const *s);
void *e2nrealloc(void *p, size_t *pn, size_t s);
@@ -39,3 +41,13 @@ void info(char const *fmt, ...);
void syslog_enable(void);
void set_progname(char const *arg);
int set_log_facility(char const *arg);
+
+enum {
+ STRSPLIT_OK,
+ STRSPLIT_NOMEM,
+ STRSPLIT_ERR
+};
+
+int strsplit(char const *str, int max, int *ret_ac, char ***ret_av,
+ char **endp);
+void argcv_free(int ac, char **av);
diff --git a/src/mem.c b/src/mem.c
index 4312308..744671e 100644
--- a/src/mem.c
+++ b/src/mem.c
@@ -21,6 +21,15 @@ emalloc(size_t s)
}
void *
+ecalloc(size_t n, size_t s)
+{
+ void *p = calloc(n, s);
+ if (!p)
+ emalloc_die();
+ return p;
+}
+
+void *
e2nrealloc(void *p, size_t *pn, size_t s)
{
char *n = json_2nrealloc(p, pn, s);
diff --git a/src/ping903.c b/src/ping903.c
index 3aee44a..3e5aadd 100644
--- a/src/ping903.c
+++ b/src/ping903.c
@@ -530,70 +530,6 @@ ept_ip_match(struct MHD_Connection *conn,
return ret;
}
-static void
-argv_free(int ac, char **av)
-{
- int i;
- for (i = 0; i < ac; i++)
- free(av[i]);
- free(av);
-}
-
-static char **
-strsplit(char const *str, int max, int *ret_n)
-{
- char **av;
- int ac;
- char const *p;
- int inword;
-
- av = calloc(max+1, sizeof(*av));
- if (!av)
- return NULL;
- ac = 0;
-
-#define ISWS(c) ((c)==' '||(c)=='\t')
-
- inword = !ISWS(*str);
- p = str;
- for (;;) {
- if (inword) {
- if (*p == 0 || (ac < max-1 && ISWS(*p))) {
- size_t len;
-
- if (ac == max-1) {
- while (p > str && ISWS(*p))
- p--;
- if (p == str)
- break;
- }
- len = p - str;
- av[ac] = malloc(len + 1);
- if (!av[ac])
- goto err;
- memcpy(av[ac], str, len);
- av[ac][len] = 0;
- ac++;
- if (ac == max || *p == 0)
- break;
- inword = 0;
- }
- } else if (*p == 0) {
- break;
- } else if (!ISWS(*p)) {
- str = p;
- inword = 1;
- }
- p++;
- }
- av[ac] = NULL;
- *ret_n = ac;
- return av;
-err:
- argv_free(ac, av);
- return NULL;
-}
-
struct auth_location {
char *url;
size_t ulen;
@@ -611,6 +547,7 @@ cf_auth(int mode, union cf_callback_arg *arg, void *data)
{
int ac;
char **av;
+ char *endp;
struct auth_location *loc;
if (mode != CF_PARSE)
@@ -618,24 +555,39 @@ cf_auth(int mode, union cf_callback_arg *arg, void *data)
// auth basic METHOD URL [FILE [REALM]]
enum { i_type, i_method, i_url, i_file, i_realm };
-
- av = strsplit(arg->input.val, 5, &ac);
- if (!av)
+
+ switch (strsplit(arg->input.val, 5, &ac, &av, &endp)) {
+ case STRSPLIT_OK:
+ if (!*endp)
+ break;
+ /* fall through */
+ case STRSPLIT_ERR:
+ error("%s:%d: syntax error near %s",
+ arg->input.file, arg->input.line, endp);
+ argcv_free(ac, av);
+ return CF_RET_FAIL;
+
+ case STRSPLIT_NOMEM:
emalloc_die();
+ }
+
if (ac < 3) {
error("%s:%d: syntax error",
arg->input.file, arg->input.line);
+ argcv_free(ac, av);
return CF_RET_FAIL;
}
if (!auth_head && ac < 5) {
error("%s:%d: realm or password file name missing",
arg->input.file, arg->input.line);
+ argcv_free(ac, av);
return CF_RET_FAIL;
}
if (strcmp(av[i_type], "basic")) {
error("%s:%d: unsupported authentication method",
arg->input.file, arg->input.line);
+ argcv_free(ac, av);
return CF_RET_FAIL;
}
@@ -656,7 +608,7 @@ cf_auth(int mode, union cf_callback_arg *arg, void *data)
/* All elements except 1st were transferred to loc, hence 1 as the
value of the first parameter. */
- argv_free(1, av);
+ argcv_free(1, av);
return CF_RET_OK;
}
diff --git a/src/ping903q.c b/src/ping903q.c
index e1faacd..85388f8 100644
--- a/src/ping903q.c
+++ b/src/ping903q.c
@@ -20,6 +20,8 @@ int verbose;
int resolve_ip;
char *nagios_prefix_format = "PING";
char const http_version[] = "HTTP/1.1";
+char *cred_file_name;
+char *nodename, *service;
enum {
EX_NAGIOS_OK = 0,
@@ -362,7 +364,7 @@ http_recv(struct http_resp *resp)
}
static void
-http_query(char const *meth, char const *url, char **hdr)
+http_query_primitive(char const *meth, char const *url, char **hdr)
{
size_t i;
int host = 0;
@@ -380,6 +382,272 @@ http_query(char const *meth, char const *url, char **hdr)
fprintf(http, "\r\n");
}
+static char *std_headers[] = {
+ "Accept: application/json",
+ "User-Agent: ping903q (" PACKAGE_STRING ")",
+ NULL
+};
+
+#define BASICPREF "Basic realm=\""
+#define BASICLEN (sizeof(BASICPREF)-1)
+
+static char const *
+get_cred_file_name(void)
+{
+ if (!cred_file_name) {
+ char const *home = getenv("HOME");
+ size_t len = strlen(home) + sizeof(CRED_FILE_NAME);
+ cred_file_name = emalloc(len + 1);
+ strcpy(cred_file_name, home);
+ strcat(cred_file_name, "/");
+ strcat(cred_file_name, CRED_FILE_NAME);
+ }
+ return cred_file_name;
+}
+
+static int
+addrinfocmp(struct addrinfo *aptr, struct addrinfo *bptr)
+{
+ for (; aptr; aptr = aptr->ai_next) {
+ for (; bptr; bptr = bptr->ai_next) {
+ if (aptr->ai_addrlen == bptr->ai_addrlen
+ && memcmp(aptr->ai_addr, bptr->ai_addr,
+ bptr->ai_addrlen) == 0)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+nodecmp(char const *a, char const *b)
+{
+ struct addrinfo hints, *ares, *bres;
+ int rc;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ rc = getaddrinfo(a, NULL, &hints, &ares);
+ if (rc)
+ return -1;
+
+ rc = getaddrinfo(b, NULL, &hints, &bres);
+ if (rc) {
+ freeaddrinfo(ares);
+ return -1;
+ }
+
+ rc = addrinfocmp(ares, bres);
+ freeaddrinfo(ares);
+ freeaddrinfo(bres);
+ return rc;
+}
+
+static int
+machinecmp(char const *pat)
+{
+ size_t slen;
+
+ if (strcmp(pat, "*") == 0)
+ return 0;
+ if (pat[0] == '*' && pat[1] == ':') {
+ pat += 2;
+ } else {
+ size_t len = strcspn(pat, ":");
+ if (!(len == strlen(nodename)
+ && memcmp(pat, nodename, len) == 0)) {
+ int rc;
+ char *pcopy = emalloc(len + 1);
+ memcpy(pcopy, pat, len);
+ pcopy[len] = 0;
+ rc = nodecmp(pcopy, nodename);
+ free(pcopy);
+ if (rc)
+ return -1;
+ }
+ pat += len;
+ if (*pat == 0)
+ return 0;
+ pat++;
+ }
+
+ if (strcmp(pat, "*") == 0)
+ return 0;
+ return strcmp(pat, service);
+}
+
+static int
+get_auth_creds(char const *auth, char **retval)
+{
+ char *realm, *p;
+ size_t len;
+ char const *cfname;
+ FILE *fp;
+ char buf[1024];
+ int ln = 0;
+ int skip_to_eol = 0;
+ int ret;
+ int i;
+
+ if (!auth || strncmp(auth, BASICPREF, BASICLEN))
+ return -1;
+ auth += BASICLEN;
+ len = strlen(auth);
+ if (len == 0 || auth[len-1] != '"')
+ return -1;
+ len--;
+ if (auth[len-1] == '\\')
+ return -1;
+ realm = emalloc(len + 1);
+ memcpy(realm, auth, len);
+ for (i = 0; i < len; ) {
+ if (*auth == '\\')
+ auth++;
+ realm[i++] = *auth++;
+ }
+ realm[i] = 0;
+
+ cfname = get_cred_file_name();
+ fp = fopen(cfname, "r");
+ if (!fp) {
+ free(realm);
+ return -1;
+ }
+
+ ret = -1;
+ while (fgets(buf, sizeof(buf), fp)) {
+ char *p;
+ int ac;
+ char **av;
+ char *endp;
+
+ ln++;
+ len = strlen(buf);
+ if (len == 0)
+ continue;
+
+ if (skip_to_eol) {
+ skip_to_eol = buf[len-1] != '\n';
+ continue;
+ }
+
+ if (buf[len-1] == '\n')
+ buf[--len] = 0;
+ else if (!feof(fp)) {
+ error("%s:%d: line too long", cfname, ln);
+ skip_to_eol = 1;
+ continue;
+ }
+
+ while (len > 0 && isspace(buf[len-1]))
+ buf[--len] = 0;
+
+ if (len == 0)
+ continue;
+
+ for (p = buf; (*p == ' ' || *p == '\t'); p++)
+ ;
+ if (*p == 0 || *p == '#')
+ continue;
+
+ switch (strsplit(p, 4, &ac, &av, &endp)) {
+ case STRSPLIT_OK:
+ if (!*endp)
+ break;
+ /* fall through */
+ case STRSPLIT_ERR:
+ error("%s:%d: syntax error near %s",
+ cfname, ln, endp);
+ argcv_free(ac, av);
+ goto err;
+
+ case STRSPLIT_NOMEM:
+ emalloc_die();
+ }
+
+ if (ac != 4) {
+ error("%s:%d: not enough fields", cfname, ln);
+ argcv_free(ac, av);
+ goto err;
+ }
+
+ if (machinecmp(av[0]) == 0
+ && (strcmp(av[1], "*") == 0
+ || strcmp(av[1], realm) == 0)) {
+ len = strlen(av[2]) + strlen(av[3]) + 2;
+ char *p = emalloc(len);
+ strcpy(p, av[2]);
+ strcat(p, ":");
+ strcat(p, av[3]);
+ *retval = p;
+ ret = 0;
+ argcv_free(ac, av);
+ break;
+ } else {
+ argcv_free(ac, av);
+ }
+ }
+err:
+ fclose(fp);
+ free(realm);
+ return ret;
+}
+
+#define HTTP_AUTHORIZATION "Authorization: Basic "
+#define HTTP_AUTHORIZATION_LEN (sizeof(HTTP_AUTHORIZATION)-1)
+
+static void
+http_query(char const *meth, char const *url, char **hdr,
+ struct http_resp *resp)
+{
+ http_query_primitive(meth, url, hdr);
+ http_recv(resp);
+ if (resp->code != 200) {
+ if (resp->code == 401) {
+ char *creds;
+ char const *auth =
+ http_resp_get_header(resp,
+ "WWW-Authenticate");
+ if (get_auth_creds(auth, &creds) == 0) {
+ char *b64creds;
+ size_t b64creds_len;
+ char **new_hdr;
+ size_t i, nhdr;
+ char *auth_hdr;
+
+ for (nhdr = 0; hdr[nhdr]; nhdr++)
+ ;
+ new_hdr = ecalloc(nhdr + 2,
+ sizeof(new_hdr[0]));
+ for (i = 0; i < nhdr; i++)
+ new_hdr[i] = hdr[i];
+
+ if (base64_encode(creds, strlen(creds),
+ &b64creds, &b64creds_len))
+ emalloc_die();
+ free(creds);
+
+ auth_hdr = emalloc(HTTP_AUTHORIZATION_LEN
+ + b64creds_len
+ + 1);
+ strcpy(auth_hdr, HTTP_AUTHORIZATION);
+ strcat(auth_hdr, b64creds);
+ free(b64creds);
+ new_hdr[i] = auth_hdr;
+ new_hdr[i+1] = NULL;
+
+ http_query_primitive(meth, url, new_hdr);
+ http_recv(resp);
+
+ free(new_hdr);
+ free(auth_hdr);
+ }
+ }
+ }
+}
+
static struct json_value *
ejson_get(struct json_value *obj, char *name, int type)
{
@@ -460,12 +728,6 @@ resolve_host(char const *name)
return estrdup(hbuf);
}
-static char *std_headers[] = {
- "Accept: application/json",
- "User-Agent: ping903q (" PACKAGE_STRING ")",
- NULL
-};
-
static void
query_host(char const *name, int (*report)(struct json_value *, void *),
void *report_data)
@@ -477,8 +739,8 @@ query_host(char const *name, int (*report)(struct json_value *, void *),
char const *hval;
char *p;
char *ipstr = resolve_host(name);
- ssize_t n;
-
+ ssize_t n;
+
if (resolve_ip) {
n = snprintf(url, sizeof(url), "/ip/%s", ipstr);
} else {
@@ -488,12 +750,10 @@ query_host(char const *name, int (*report)(struct json_value *, void *),
if (n < 0 || n == sizeof(url)) {
abend("bad host name or IP");
}
- http_query("GET", url, std_headers);
http_resp_init(&resp);
- http_recv(&resp);
- if (resp.code != 200) {
+ http_query("GET", url, std_headers, &resp);
+ if (resp.code != 200)
abend("%s", resp.reason);
- }
hval = http_resp_get_header(&resp, "content-type");
if (!hval || strcmp(hval, "application/json")) {
@@ -526,9 +786,8 @@ match_host(char const *name)
if (n == -1 || n == sizeof(url)) {
abend("url buffer overflow");
}
- http_query("GET", url, std_headers);
http_resp_init(&resp);
- http_recv(&resp);
+ http_query("GET", url, std_headers, &resp);
if (resp.code != 200) {
abend("%s", resp.reason);
}
@@ -575,9 +834,8 @@ query_all(void)
size_t len;
size_t i;
- http_query("GET", "/host", NULL);
http_resp_init(&resp);
- http_recv(&resp);
+ http_query("GET", "/host", std_headers, &resp);
if (resp.code != 200) {
abend("%s", resp.reason);
}
@@ -783,7 +1041,7 @@ int
main(int argc, char **argv)
{
int c;
- char *p, *node, *service;
+ char *p;
char const *host = NULL;
char *c_opt = NULL;
char *w_opt = NULL;
@@ -866,12 +1124,12 @@ main(int argc, char **argv)
break;
}
- p = node = read_listen(&service);
- if (!node || !node[0])
- node = DEFAULT_ADDRESS;
+ p = nodename = read_listen(&service);
+ if (!nodename || !nodename[0])
+ nodename = DEFAULT_ADDRESS;
if (!service)
service = DEFAULT_SERVICE;
- http_connect(node, service);
+ http_connect(nodename, service);
free(p);
switch (mode) {
diff --git a/src/strsplit.c b/src/strsplit.c
new file mode 100644
index 0000000..c2e1eac
--- /dev/null
+++ b/src/strsplit.c
@@ -0,0 +1,118 @@
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include "defs.h"
+
+void
+argcv_free(int ac, char **av)
+{
+ int i;
+ for (i = 0; i < ac; i++)
+ free(av[i]);
+ free(av);
+}
+
+int
+strsplit(char const *str, int max, int *ret_ac, char ***ret_av, char **endp)
+{
+ char **av = NULL;
+ int ac;
+ char const *p;
+ enum { Stop, Error, Inspace, Inquote, Inword } state;
+
+ av = calloc(max+1, sizeof(*av));
+ if (!av)
+ return STRSPLIT_NOMEM;
+ ac = 0;
+
+#define ISWS(c) ((c)==' '||(c)=='\t')
+#define UNESCAPE(p) ((p)[0] == '\\' && ((p)[1] == '\\' || (p)[1] == '"'))
+
+ if (ISWS(*str))
+ state = Inspace;
+ else if (*str == '"') {
+ state = Inquote;
+ str++;
+ } else
+ state = Inword;
+
+ p = str;
+ while (state != Stop && state != Error) {
+ switch (state) {
+ case Inquote:
+ if (*p == 0) {
+ p = str;
+ state = Error;
+ } else if (UNESCAPE(p))
+ p += 2;
+ else if (*p == '"') {
+ char *q;
+ size_t len = p - str;
+ av[ac] = malloc(len + 1);
+ if (!av[ac])
+ goto err;
+ q = av[ac];
+ while (str < p) {
+ if (UNESCAPE(str))
+ str++;
+ *q++ = *str++;
+ }
+ *q = 0;
+ ac++;
+ str++;
+ p = str;
+ if (*p == 0)
+ state = Stop;
+ else if (ISWS(*str))
+ state = Inspace;
+ else
+ state = Error;
+ } else
+ p++;
+ break;
+
+ case Inword:
+ if (*p == 0 || ISWS(*p)) {
+ size_t len = p - str;
+ av[ac] = malloc(len + 1);
+ if (!av[ac])
+ goto err;
+ memcpy(av[ac], str, len);
+ av[ac][len] = 0;
+ ac++;
+ state = *p ? Inspace : Stop;
+ } else
+ p++;
+ break;
+
+ case Inspace:
+ if (*p == 0) {
+ state = Stop;
+ break;
+ } else if (*p == '"') {
+ state = Inquote;
+ p++;
+ str = p;
+ } else if (!ISWS(*p)) {
+ state = Inword;
+ str = p;
+ } else
+ p++;
+ if (ac == max && state != Inspace) {
+ state = Stop;
+ }
+ break;
+
+ default:
+ abort();
+ }
+ }
+ av[ac] = NULL;
+ *ret_av = av;
+ *ret_ac = ac;
+ *endp = (char*) p;
+ return state == Stop ? STRSPLIT_OK : STRSPLIT_ERR;
+err:
+ argcv_free(ac, av);
+ return STRSPLIT_NOMEM;
+}

Return to:

Send suggestions and report system problems to the System administrator.