#include <stdio.h>
#include <ctype.h>
#include "config.h"
#include "common.h"
void endpoint_init (struct config_endpoint *endpoint, unsigned short default_port) {
// snprintf the default port as a string
snprintf(endpoint->_data.default_port, PORT_TEXT_LEN, "%hu", default_port);
// set up defaults
endpoint->family = PF_INET;
endpoint->af.inet.addr = NULL;
endpoint->af.inet.port = endpoint->_data.default_port;
}
/*
* Parse an endpoint address in human-readable format into the given endpoint. You can then use the socket_*
* functions to work with endpoints and sockets.
*
* Formats supported:
* =foo.sock, =/quux/bar.sock
* hostname, 12.34.56.78, [::1]
* hostname:port, 12.34.56.78:port, [::1]:port
*/
int endpoint_parse (struct config_endpoint *endpoint, const char *addr_spec) {
// for empty addr specs, just return
if (!addr_spec || *addr_spec == '\0')
return 1;
if (sscanf(addr_spec, "=%" PATH_TEXT_LEN_STR "s", endpoint->_data.path) == 1) {
// local unix socket
endpoint->family = PF_LOCAL;
endpoint->af.local.path = endpoint->_data.path;
} else if (
sscanf(addr_spec, "[%" ADDR_TEXT_LEN_STR "[^]] ]:%" PORT_TEXT_LEN_STR "s", endpoint->_data.addr, endpoint->_data.port) == 2
|| sscanf(addr_spec, "%" ADDR_TEXT_LEN_STR "[^:] :%" PORT_TEXT_LEN_STR "s", endpoint->_data.addr, endpoint->_data.port) == 2
) {
endpoint->family = PF_INET;
endpoint->af.inet.addr = endpoint->_data.addr;
endpoint->af.inet.port = endpoint->_data.port;
} else if (
sscanf(addr_spec, "[%" ADDR_TEXT_LEN_STR "[^]] ]", endpoint->_data.addr) == 1
|| sscanf(addr_spec, "%" ADDR_TEXT_LEN_STR "[^:]", endpoint->_data.addr) == 1
) {
endpoint->family = PF_INET;
endpoint->af.inet.addr = endpoint->_data.addr;
} else {
// unknown foramt
ERROR("unkown address format: %s", addr_spec);
}
return 0;
error:
return -1;
}
int parse_hostport (char *hostport, char **host, char **port) {
char *c;
int brace = 0, colon = 0;
*host = *port = NULL;
for (c = hostport; *c != '\0'; c++) {
if (isspace(*c))
continue;
switch (*c) {
case '[':
if (c > hostport) {
error("parse_hostport: %c must be first char, if any: %s", *c, hostport);
return -1;
}
brace = *c;
break;
case ']':
if (!brace) {
error("parse_hostport: %c without matching brace: %s", *c, hostport);
return -1;
}
if (!*host) {
error("parse_hostport: empty hostname: %s", hostport);
return -1;
}
brace = 0;
break;
case ':':
if (brace) {
// colons inside braces are left as-is
continue;
}
if (!*host) {
error("parse_hostport: colon before hostname: %s", hostport);
return -1;
}
if (*port) {
error("parse_hostport: colon after port: %s", hostport);
return -1;
}
if (colon) {
error("parse_hostport: too many colons: %s", hostport);
return -1;
};
// finished parsing the host part, move on to the port part
colon = ':';
*c = '\0';
break;
default:
if (!*host) {
// first char of the host
*host = c;
} else if (colon && !*port) {
// first char of the port
*port = c;
} // else a part of either, don't care about it
break;
}
}
if (!*host) {
error("parse_hostport: missing hostname: %s", hostport);
return -1;
}
if (brace) {
error("parse_hostport: missing close-brace for %c: %s", brace, hostport);
return -1;
}
if (colon && !*port) {
error("parse_hostport: missing port: %s", hostport);
return -1;
}
return 0;
}