First working version
parent
c70bcae6af
commit
26694240c7
@ -0,0 +1,15 @@
|
|||||||
|
CC=gcc
|
||||||
|
CFLAGS=-I.
|
||||||
|
DEPS = utils.h
|
||||||
|
OBJ = utils.o main.o
|
||||||
|
|
||||||
|
%.o: %.c $(DEPS)
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
parse-ssh-cl: $(OBJ)
|
||||||
|
$(CC) -o $@ $^ $(CFLAGS)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *~ core *~
|
@ -0,0 +1,82 @@
|
|||||||
|
# SSH Command Line Parser
|
||||||
|
|
||||||
|
## What.
|
||||||
|
|
||||||
|
This program is called exactly like the `ssh` binary from OpenSSH, except
|
||||||
|
instead of opening a remote shell it will print the username (if specified),
|
||||||
|
host, and port (if specified) given on the command line.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./parse-ssh-cl -J bastion -X estelle@myhost:1234
|
||||||
|
User: estelle
|
||||||
|
Host: myhost
|
||||||
|
Port: 1234
|
||||||
|
Command:
|
||||||
|
|
||||||
|
./parse-ssh-cl -4 -q -L 8080:localhost:8080 app@prod
|
||||||
|
User: app
|
||||||
|
Host: prod
|
||||||
|
Port:
|
||||||
|
Command:
|
||||||
|
|
||||||
|
./parse-ssh-cl ssh://dev
|
||||||
|
User:
|
||||||
|
Host: dev
|
||||||
|
Port:
|
||||||
|
Command:
|
||||||
|
|
||||||
|
./parse-ssh-cl host ls -l
|
||||||
|
User:
|
||||||
|
Host: host
|
||||||
|
Port:
|
||||||
|
Command: ls -l
|
||||||
|
```
|
||||||
|
|
||||||
|
## Why?
|
||||||
|
|
||||||
|
Because when I use tmux I want to be able to open a shell on a remote host and
|
||||||
|
have the window title show the host I'm connected to. Something that can take an
|
||||||
|
SSH command line string and output the host is an important piece of that
|
||||||
|
puzzle.
|
||||||
|
|
||||||
|
## How?
|
||||||
|
|
||||||
|
* I looked at the source of OpenSSH (`ssh.c`) and saw two functions
|
||||||
|
`parse_ssh_uri` and `parse_user_host_port` that seemed like they did what I
|
||||||
|
wanted so I just copied them over and tried to compile them standalone.
|
||||||
|
|
||||||
|
* GCC yelled at me about all these missing function definitions so I copied
|
||||||
|
them from around the code base (mostly `misc.c`).
|
||||||
|
|
||||||
|
* If the function wasn't there I Googled around to find out what random header I
|
||||||
|
needed.
|
||||||
|
|
||||||
|
* I unearthed some heated drama surrounding `strlcpy` so I decided to copy the
|
||||||
|
implementation from OpenBSD inline rather than link against libbsd.
|
||||||
|
|
||||||
|
* I found [greymd/ssh_opt_parse](https://github.com/greymd/ssh_opt_parse) which
|
||||||
|
is honestly better than this project in every conceivable way.
|
||||||
|
|
||||||
|
* I copied the `getopt` string so that I could ignore all of the options, ran my
|
||||||
|
two functions that now work, and boom, command line parsing just how upstream
|
||||||
|
does it.
|
||||||
|
|
||||||
|
## Do.
|
||||||
|
|
||||||
|
Are you sure you really want to use this thing? Just run `make` in the source
|
||||||
|
directory and then do whatever with the `parse-ssh-cl` binary it spits out.
|
||||||
|
|
||||||
|
As far as dependencies go just about any distribution's development tools should
|
||||||
|
be more than enough to build and use. It pretty just uses libc.
|
||||||
|
|
||||||
|
## Never Asked Questions.
|
||||||
|
|
||||||
|
Do you support `-l` and `-p` parsing? Not yet. That's a great idea me.
|
||||||
|
|
||||||
|
## License.
|
||||||
|
|
||||||
|
Pretty much all of this code is straight up copy/pasted from
|
||||||
|
[OpenSSH](https://github.com/openssh/openssh-portable) and the one function from
|
||||||
|
[OpenBSD](https://github.com/openbsd/src). After reading the entire LICENSE file
|
||||||
|
from OpenSSH I have concluded that I have no idea if and how I'm supposed to
|
||||||
|
give attribution.
|
@ -0,0 +1,57 @@
|
|||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[]) {
|
||||||
|
int opt;
|
||||||
|
while ((opt = getopt(argc, argv, "-1246ab:c:e:fgi:kl:m:no:p:qstvx"
|
||||||
|
"AB:CD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) {
|
||||||
|
// Stop processing options after the first positional argument and set
|
||||||
|
// optind back to the location of that argument.
|
||||||
|
if (opt == 1) {
|
||||||
|
optind -= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optind >= argc) {
|
||||||
|
fprintf(stderr, "No destination found.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *destination = argv[optind];
|
||||||
|
char *user;
|
||||||
|
char *host;
|
||||||
|
int port = -1;
|
||||||
|
|
||||||
|
if (parse_ssh_uri(destination, &user, &host, &port) == 0
|
||||||
|
|| parse_user_host_port(destination, &user, &host, &port) == 0) {
|
||||||
|
|
||||||
|
if (user != NULL) {
|
||||||
|
printf("User: %s\n", user);
|
||||||
|
} else {
|
||||||
|
printf("User:\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Host: %s\n", host);
|
||||||
|
|
||||||
|
if (port != -1) {
|
||||||
|
printf("Port: %d\n", port);
|
||||||
|
} else {
|
||||||
|
printf("Port:\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(user);
|
||||||
|
free(host);
|
||||||
|
|
||||||
|
printf("Command:");
|
||||||
|
for (int i = ++optind; i < argc; i++) {
|
||||||
|
printf(" %s", argv[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Could not parse destination host.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,454 @@
|
|||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
long long
|
||||||
|
strtonum(const char *numstr, long long minval, long long maxval,
|
||||||
|
const char **errstrp)
|
||||||
|
{
|
||||||
|
long long ll = 0;
|
||||||
|
char *ep;
|
||||||
|
int error = 0;
|
||||||
|
struct errval {
|
||||||
|
const char *errstr;
|
||||||
|
int err;
|
||||||
|
} ev[4] = {
|
||||||
|
{ NULL, 0 },
|
||||||
|
{ "invalid", EINVAL },
|
||||||
|
{ "too small", ERANGE },
|
||||||
|
{ "too large", ERANGE },
|
||||||
|
};
|
||||||
|
|
||||||
|
ev[0].err = errno;
|
||||||
|
errno = 0;
|
||||||
|
if (minval > maxval)
|
||||||
|
error = INVALID;
|
||||||
|
else {
|
||||||
|
ll = strtoll(numstr, &ep, 10);
|
||||||
|
if (numstr == ep || *ep != '\0')
|
||||||
|
error = INVALID;
|
||||||
|
else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
|
||||||
|
error = TOOSMALL;
|
||||||
|
else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
|
||||||
|
error = TOOLARGE;
|
||||||
|
}
|
||||||
|
if (errstrp != NULL)
|
||||||
|
*errstrp = ev[error].errstr;
|
||||||
|
errno = ev[error].err;
|
||||||
|
if (error)
|
||||||
|
ll = 0;
|
||||||
|
|
||||||
|
return (ll);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
xmalloc(size_t size)
|
||||||
|
{
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
fprintf(stderr, "xmalloc: zero size");
|
||||||
|
ptr = malloc(size);
|
||||||
|
if (ptr == NULL)
|
||||||
|
fprintf(stderr, "xmalloc: out of memory (allocating %zu bytes)", size);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Converts a two-byte hex string to decimal.
|
||||||
|
* Returns the decimal value or -1 for invalid input.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
hexchar(const char *s)
|
||||||
|
{
|
||||||
|
unsigned char result[2];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
if (s[i] >= '0' && s[i] <= '9')
|
||||||
|
result[i] = (unsigned char)(s[i] - '0');
|
||||||
|
else if (s[i] >= 'a' && s[i] <= 'f')
|
||||||
|
result[i] = (unsigned char)(s[i] - 'a') + 10;
|
||||||
|
else if (s[i] >= 'A' && s[i] <= 'F')
|
||||||
|
result[i] = (unsigned char)(s[i] - 'A') + 10;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return (result[0] << 4) | result[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert ASCII string to TCP/IP port number.
|
||||||
|
* Port must be >=0 and <=65535.
|
||||||
|
* Return -1 if invalid.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
a2port(const char *s)
|
||||||
|
{
|
||||||
|
struct servent *se;
|
||||||
|
long long port;
|
||||||
|
const char *errstr;
|
||||||
|
|
||||||
|
port = strtonum(s, 0, 65535, &errstr);
|
||||||
|
if (errstr == NULL)
|
||||||
|
return (int)port;
|
||||||
|
if ((se = getservbyname(s, "tcp")) != NULL)
|
||||||
|
return ntohs(se->s_port);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode an url-encoded string.
|
||||||
|
* Returns a newly allocated string on success or NULL on failure.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
urldecode(const char *src)
|
||||||
|
{
|
||||||
|
char *ret, *dst;
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
ret = xmalloc(strlen(src) + 1);
|
||||||
|
for (dst = ret; *src != '\0'; src++) {
|
||||||
|
switch (*src) {
|
||||||
|
case '+':
|
||||||
|
*dst++ = ' ';
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
if (!isxdigit((unsigned char)src[1]) ||
|
||||||
|
!isxdigit((unsigned char)src[2]) ||
|
||||||
|
(ch = hexchar(src + 1)) == -1) {
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
*dst++ = ch;
|
||||||
|
src += 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*dst++ = *src;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*dst = '\0';
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Search for next delimiter between hostnames/addresses and ports.
|
||||||
|
* Argument may be modified (for termination).
|
||||||
|
* Returns *cp if parsing succeeds.
|
||||||
|
* *cp is set to the start of the next field, if one was found.
|
||||||
|
* The delimiter char, if present, is stored in delim.
|
||||||
|
* If this is the last field, *cp is set to NULL.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
hpdelim2(char **cp, char *delim)
|
||||||
|
{
|
||||||
|
char *s, *old;
|
||||||
|
|
||||||
|
if (cp == NULL || *cp == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
old = s = *cp;
|
||||||
|
if (*s == '[') {
|
||||||
|
if ((s = strchr(s, ']')) == NULL)
|
||||||
|
return NULL;
|
||||||
|
else
|
||||||
|
s++;
|
||||||
|
} else if ((s = strpbrk(s, ":/")) == NULL)
|
||||||
|
s = *cp + strlen(*cp); /* skip to end (see first case below) */
|
||||||
|
|
||||||
|
switch (*s) {
|
||||||
|
case '\0':
|
||||||
|
*cp = NULL; /* no more fields*/
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ':':
|
||||||
|
case '/':
|
||||||
|
if (delim != NULL)
|
||||||
|
*delim = *s;
|
||||||
|
*s = '\0'; /* terminate */
|
||||||
|
*cp = s + 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check and optionally lowercase a domain name, also removes trailing '.'
|
||||||
|
* Returns 1 on success and 0 on failure, storing an error message in errstr.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
valid_domain(char *name, int makelower, const char **errstr)
|
||||||
|
{
|
||||||
|
size_t i, l = strlen(name);
|
||||||
|
u_char c, last = '\0';
|
||||||
|
static char errbuf[256];
|
||||||
|
|
||||||
|
if (l == 0) {
|
||||||
|
strlcpy(errbuf, "empty domain name", sizeof(errbuf));
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) {
|
||||||
|
snprintf(errbuf, sizeof(errbuf), "domain name \"%.100s\" "
|
||||||
|
"starts with invalid character", name);
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
for (i = 0; i < l; i++) {
|
||||||
|
c = tolower((u_char)name[i]);
|
||||||
|
if (makelower)
|
||||||
|
name[i] = (char)c;
|
||||||
|
if (last == '.' && c == '.') {
|
||||||
|
snprintf(errbuf, sizeof(errbuf), "domain name "
|
||||||
|
"\"%.100s\" contains consecutive separators", name);
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
if (c != '.' && c != '-' && !isalnum(c) &&
|
||||||
|
c != '_') /* technically invalid, but common */ {
|
||||||
|
snprintf(errbuf, sizeof(errbuf), "domain name "
|
||||||
|
"\"%.100s\" contains invalid characters", name);
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
last = c;
|
||||||
|
}
|
||||||
|
if (name[l - 1] == '.')
|
||||||
|
name[l - 1] = '\0';
|
||||||
|
if (errstr != NULL)
|
||||||
|
*errstr = NULL;
|
||||||
|
return 1;
|
||||||
|
bad:
|
||||||
|
if (errstr != NULL)
|
||||||
|
*errstr = errbuf;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *
|
||||||
|
cleanhostname(char *host)
|
||||||
|
{
|
||||||
|
if (*host == '[' && host[strlen(host) - 1] == ']') {
|
||||||
|
host[strlen(host) - 1] = '\0';
|
||||||
|
return (host + 1);
|
||||||
|
} else
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
xstrdup(const char *str)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
char *cp;
|
||||||
|
|
||||||
|
len = strlen(str) + 1;
|
||||||
|
cp = xmalloc(len);
|
||||||
|
strlcpy(cp, str, len);
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse an (scp|ssh|sftp)://[user@]host[:port][/path] URI.
|
||||||
|
* See https://tools.ietf.org/html/draft-ietf-secsh-scp-sftp-ssh-uri-04
|
||||||
|
* Either user or path may be url-encoded (but not host or port).
|
||||||
|
* Caller must free returned user, host and path.
|
||||||
|
* Any of the pointer return arguments may be NULL (useful for syntax checking)
|
||||||
|
* but the scheme must always be specified.
|
||||||
|
* If user was not specified then *userp will be set to NULL.
|
||||||
|
* If port was not specified then *portp will be -1.
|
||||||
|
* If path was not specified then *pathp will be set to NULL.
|
||||||
|
* Returns 0 on success, 1 if non-uri/wrong scheme, -1 on error/invalid uri.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
parse_uri(const char *scheme, const char *uri, char **userp, char **hostp,
|
||||||
|
int *portp, char **pathp)
|
||||||
|
{
|
||||||
|
char *uridup, *cp, *tmp, ch;
|
||||||
|
char *user = NULL, *host = NULL, *path = NULL;
|
||||||
|
int port = -1, ret = -1;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = strlen(scheme);
|
||||||
|
if (strncmp(uri, scheme, len) != 0 || strncmp(uri + len, "://", 3) != 0)
|
||||||
|
return 1;
|
||||||
|
uri += len + 3;
|
||||||
|
|
||||||
|
if (userp != NULL)
|
||||||
|
*userp = NULL;
|
||||||
|
if (hostp != NULL)
|
||||||
|
*hostp = NULL;
|
||||||
|
if (portp != NULL)
|
||||||
|
*portp = -1;
|
||||||
|
if (pathp != NULL)
|
||||||
|
*pathp = NULL;
|
||||||
|
|
||||||
|
uridup = tmp = xstrdup(uri);
|
||||||
|
|
||||||
|
/* Extract optional ssh-info (username + connection params) */
|
||||||
|
if ((cp = strchr(tmp, '@')) != NULL) {
|
||||||
|
char *delim;
|
||||||
|
|
||||||
|
*cp = '\0';
|
||||||
|
/* Extract username and connection params */
|
||||||
|
if ((delim = strchr(tmp, ';')) != NULL) {
|
||||||
|
/* Just ignore connection params for now */
|
||||||
|
*delim = '\0';
|
||||||
|
}
|
||||||
|
if (*tmp == '\0') {
|
||||||
|
/* Empty username */
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if ((user = urldecode(tmp)) == NULL)
|
||||||
|
goto out;
|
||||||
|
tmp = cp + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract mandatory hostname */
|
||||||
|
if ((cp = hpdelim2(&tmp, &ch)) == NULL || *cp == '\0')
|
||||||
|
goto out;
|
||||||
|
host = xstrdup(cleanhostname(cp));
|
||||||
|
if (!valid_domain(host, 0, NULL))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (tmp != NULL && *tmp != '\0') {
|
||||||
|
if (ch == ':') {
|
||||||
|
/* Convert and verify port. */
|
||||||
|
if ((cp = strchr(tmp, '/')) != NULL)
|
||||||
|
*cp = '\0';
|
||||||
|
if ((port = a2port(tmp)) <= 0)
|
||||||
|
goto out;
|
||||||
|
tmp = cp ? cp + 1 : NULL;
|
||||||
|
}
|
||||||
|
if (tmp != NULL && *tmp != '\0') {
|
||||||
|
/* Extract optional path */
|
||||||
|
if ((path = urldecode(tmp)) == NULL)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success */
|
||||||
|
if (userp != NULL) {
|
||||||
|
*userp = user;
|
||||||
|
user = NULL;
|
||||||
|
}
|
||||||
|
if (hostp != NULL) {
|
||||||
|
*hostp = host;
|
||||||
|
host = NULL;
|
||||||
|
}
|
||||||
|
if (portp != NULL)
|
||||||
|
*portp = port;
|
||||||
|
if (pathp != NULL) {
|
||||||
|
*pathp = path;
|
||||||
|
path = NULL;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
free(uridup);
|
||||||
|
free(user);
|
||||||
|
free(host);
|
||||||
|
free(path);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
parse_ssh_uri(const char *uri, char **userp, char **hostp, int *portp)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = parse_uri("ssh", uri, userp, hostp, portp, &path);
|
||||||
|
if (r == 0 && path != NULL)
|
||||||
|
r = -1; /* path not allowed */
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a [user@]host[:port] string.
|
||||||
|
* Caller must free returned user and host.
|
||||||
|
* Any of the pointer return arguments may be NULL (useful for syntax checking).
|
||||||
|
* If user was not specified then *userp will be set to NULL.
|
||||||
|
* If port was not specified then *portp will be -1.
|
||||||
|
* Returns 0 on success, -1 on failure.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
parse_user_host_port(const char *s, char **userp, char **hostp, int *portp)
|
||||||
|
{
|
||||||
|
char *sdup, *cp, *tmp;
|
||||||
|
char *user = NULL, *host = NULL;
|
||||||
|
int port = -1, ret = -1;
|
||||||
|
|
||||||
|
if (userp != NULL)
|
||||||
|
*userp = NULL;
|
||||||
|
if (hostp != NULL)
|
||||||
|
*hostp = NULL;
|
||||||
|
if (portp != NULL)
|
||||||
|
*portp = -1;
|
||||||
|
|
||||||
|
if ((sdup = tmp = strdup(s)) == NULL)
|
||||||
|
return -1;
|
||||||
|
/* Extract optional username */
|
||||||
|
if ((cp = strrchr(tmp, '@')) != NULL) {
|
||||||
|
*cp = '\0';
|
||||||
|
if (*tmp == '\0')
|
||||||
|
goto out;
|
||||||
|
if ((user = strdup(tmp)) == NULL)
|
||||||
|
goto out;
|
||||||
|
tmp = cp + 1;
|
||||||
|
}
|
||||||
|
/* Extract mandatory hostname */
|
||||||
|
if ((cp = hpdelim2(&tmp, NULL)) == NULL || *cp == '\0')
|
||||||
|
goto out;
|
||||||
|
host = xstrdup(cleanhostname(cp));
|
||||||
|
/* Convert and verify optional port */
|
||||||
|
if (tmp != NULL && *tmp != '\0') {
|
||||||
|
if ((port = a2port(tmp)) <= 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
/* Success */
|
||||||
|
if (userp != NULL) {
|
||||||
|
*userp = user;
|
||||||
|
user = NULL;
|
||||||
|
}
|
||||||
|
if (hostp != NULL) {
|
||||||
|
*hostp = host;
|
||||||
|
host = NULL;
|
||||||
|
}
|
||||||
|
if (portp != NULL)
|
||||||
|
*portp = port;
|
||||||
|
ret = 0;
|
||||||
|
out:
|
||||||
|
free(sdup);
|
||||||
|
free(user);
|
||||||
|
free(host);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy string src to buffer dst of size dsize. At most dsize-1
|
||||||
|
* chars will be copied. Always NUL terminates (unless dsize == 0).
|
||||||
|
* Returns strlen(src); if retval >= dsize, truncation occurred.
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize)
|
||||||
|
{
|
||||||
|
const char *osrc = src;
|
||||||
|
size_t nleft = dsize;
|
||||||
|
|
||||||
|
/* Copy as many bytes as will fit. */
|
||||||
|
if (nleft != 0) {
|
||||||
|
while (--nleft != 0) {
|
||||||
|
if ((*dst++ = *src++) == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not enough room in dst, add NUL and traverse rest of src. */
|
||||||
|
if (nleft == 0) {
|
||||||
|
if (dsize != 0)
|
||||||
|
*dst = '\0'; /* NUL-terminate dst */
|
||||||
|
while (*src++)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
return(src - osrc - 1); /* count does not include NUL */
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define INVALID 1
|
||||||
|
#define TOOSMALL 2
|
||||||
|
#define TOOLARGE 3
|
||||||
|
|
||||||
|
long long
|
||||||
|
strtonum(const char *numstr, long long minval, long long maxval,
|
||||||
|
const char **errstrp);
|
||||||
|
|
||||||
|
void *
|
||||||
|
xmalloc(size_t size);
|
||||||
|
|
||||||
|
static int
|
||||||
|
hexchar(const char *s);
|
||||||
|
|
||||||
|
int
|
||||||
|
a2port(const char *s);
|
||||||
|
|
||||||
|
static char *
|
||||||
|
urldecode(const char *src);
|
||||||
|
|
||||||
|
char *
|
||||||
|
hpdelim2(char **cp, char *delim);
|
||||||
|
|
||||||
|
int
|
||||||
|
valid_domain(char *name, int makelower, const char **errstr);
|
||||||
|
|
||||||
|
char *
|
||||||
|
cleanhostname(char *host);
|
||||||
|
|
||||||
|
char *
|
||||||
|
xstrdup(const char *str);
|
||||||
|
|
||||||
|
int
|
||||||
|
parse_uri(const char *scheme, const char *uri, char **userp, char **hostp,
|
||||||
|
int *portp, char **pathp);
|
||||||
|
|
||||||
|
int
|
||||||
|
parse_ssh_uri(const char *uri, char **userp, char **hostp, int *portp);
|
||||||
|
|
||||||
|
int
|
||||||
|
parse_user_host_port(const char *s, char **userp, char **hostp, int *portp);
|
||||||
|
|
||||||
|
size_t
|
||||||
|
strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize);
|
Loading…
Reference in New Issue