config.c
author Tero Marttila <terom@fixme.fi>
Wed, 27 Aug 2008 21:30:32 +0300
changeset 41 540737bf6bac
parent 26 6d615203d963
permissions -rw-r--r--
sending requests, and partial support for receiving -- incomplete, not tested
#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;
}