diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2020-02-26 15:48:20 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2020-02-27 17:40:57 +0200 |
commit | 3b2d57c14f1ed207c79362b7136e3da4054ad817 (patch) | |
tree | 8f97331e65e3d6cc0d483fc0c3d11a2e668e33be | |
parent | a9a6567bac8ab7729c243b555e7fb3c4bb2c6e51 (diff) | |
download | ping903-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-x | examples/dbload | 4 | ||||
-rwxr-xr-x | examples/inspect | 17 | ||||
-rwxr-xr-x | examples/ipadd | 4 | ||||
-rwxr-xr-x | examples/ipdel | 4 | ||||
-rw-r--r-- | examples/lib/LWP/Ping903.pm | 49 | ||||
-rw-r--r-- | lib/Makefile.am | 1 | ||||
-rw-r--r-- | lib/base64.c | 85 | ||||
-rw-r--r-- | lib/basicauth.c | 44 | ||||
-rw-r--r-- | lib/basicauth.h | 5 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/defs.h | 12 | ||||
-rw-r--r-- | src/mem.c | 9 | ||||
-rw-r--r-- | src/ping903.c | 88 | ||||
-rw-r--r-- | src/ping903q.c | 302 | ||||
-rw-r--r-- | src/strsplit.c | 118 |
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 @@ -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); @@ -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; +} |