I am writing a wrapper library for network functionality in C and wanted to let the user let the user initialize a struct sockaddr_storage with a format string.
Format ist like: "proto:host:port/servicename" Example: "tcp:localhost:8080", "udp:8.8.8.8:dns"
Possible Protocols Strings: tcp, tcp4, tcp6, udp, udp4, udp6
Some questions I'v got now.
- Am I using to many comments?
 - Am I using to much register variables?
 - Is this code readable?
 - Do you see parts of the code that I can reduce?
 
I get the following timing: - see my main function
2:2001::124:2144:153:80 - 0.000163 ms
3:172.217.16.195:80 - 0.000135 ms
0:127.0.0.1:8080 - 0.000013 ms
0:127.129.41.24:463 - 0.000005 ms
Why is the time going rapidly down?
Code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include <time.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <netdb.h>
typedef enum {tcp, tcp4, tcp6, udp, udp4, udp6} network_id;
typedef struct Addr {
    network_id proto;       /* protocol (can be): tcp, tcp4, tcp6, udp, udp4, udp6, _unix (unix), unixgram, unixpacket */
    char addr[40];          /* string containng either ipv4 or ipv6 address */
    int port;               /* port number */
} Addr;
#ifndef strncpy_s
int strncpy_s(char *dest, size_t destsz, const char *src, size_t n)
{
    if(dest == NULL)
        return -1;
    while(destsz-- && n-- && *src != '\0')
        *dest++ = *src++;
    *dest = '\0';
    return 0;
}
#endif
/* compare functions that compared until delimiter is found either in str1 or in str2 */
int compare_until_delim(const char *str1, const char *str2, char delim)
{
    /* *str1 != delim xor *str2 == 0 and *str2 != delim xor *str2 == 0 */
    while((!(*str1 != delim) != !(*str1 == '\0')) && (!(*str2 != delim) != !(*str2 == '\0'))) {
        if(*str1 != *str2)
            return false;
        str1++;
        str2++;
    }
    if(*str1 != *str2)
        return false;
    return true;
}
/* protocol table containing supported protocol for socket configuration */
const char *protocol_table[] = 
{
    "tcp:",
    "tcp4:",
    "tcp6:",
    "udp:",
    "udp4:",
    "upd6:",
};
/* simple compare until dilimiter check for the protocol table above */
int check_protocol(const char *str)
{
    int i, table_size;
    table_size = sizeof(protocol_table) / sizeof(*protocol_table);
    for(i = 0; i < table_size ; i++) {
        if(compare_until_delim(protocol_table[i], str, ':'))
            return i;
    }
    return -1; /* not found */
}
 
/** ipstr_to_addr - initializes struct Addr and struct sockaddr_in by string containing specific ip address format
 *  return@int:         (0 on success, -2 = inet_pton error, -1 = parsing error at start index 0, 1...n = error at index)
 *  param@ipstr:        format string like format "proto:host:service" 
 *  param@addr:         struct Addr (internal use) (contains printable addr infos, except protocol) if NULL only the _sockaddr_storage
 *  param@_sockaddr_in: struct sockaddr_in, will be setup regarding to the format string
*/
int ipstr_to_addr(const char *ipstr, Addr *addr, struct sockaddr_storage *_sockaddr_storage, int *socktype)
{
    assert(_sockaddr_storage != NULL);
    register const char *start_ip, *end_ipv6;
    register const char *service_start;
    register char *domain_p;
    struct Addr local;
    char domain_buffer[256]; /* will temporary hold ipv4 and domain format to convert later from */
    bool ipv6;
    int proto, port, err;
    if(addr == NULL)
        addr = &local;
    domain_p = domain_buffer;
    /* set service start to beginning of ipstr and use it as a seperator to service/port */
    service_start = ipstr;
    ipv6 = false;
    err = 0;
    
    /* get internal protocol id  */
    proto = check_protocol(ipstr);
    if(proto == -1) {
        err = -1;
        goto cleanup_error;
    }
    service_start = service_start + strlen(protocol_table[proto]);
    /* if first char after protocol is [ check for ] to say it is an ipv6 address */
    if(*service_start == '[') {
        /* go latest until null byte */
        start_ip = service_start + 1;
        while(*service_start != '\0') {
            /* if enclosure bracket ] was found set ipv6 true and increment service_start */
            if(*service_start == ']') {
                end_ipv6 = service_start;
                ipv6 = true;
                service_start++;
                break;
            }
            service_start++;
        }
        /* service_start should point to ':' in format string right before service/port */
    }
    /* ip not ipv6 assume ipv4 or domain name */
    if(ipv6 == false) {
        /* go latest until null byte */
        while(*service_start != '\0') {
            /* service_start points to seperator ':', increment domain_p first and set to null byte */
            if(*service_start == ':') {
                *domain_p = '\0';
                break;
            }
            /* write a copy to domain_buffer */
            *domain_p++ = *service_start++;
        }
        /* service_start should point to ':' in format string right before service/port too */
    }
    /* if not ':' we should be at the end of the string which leads to incomplete format, return position of error */
    if(*service_start != ':') {
        err = service_start - ipstr;
        goto cleanup_error;
    }
    service_start++; /* increase by one to get the real service start position */
    port = atoi(service_start);   /* try to get port number after ':' */
    /* if port port was set to 0, try get port by name */
    if(port == 0) {
        /* NOTE: maybe use getservent_r for thread safety */
        struct servent *service = getservbyname(service_start, NULL);
        if(service == NULL) {
            err = service_start - ipstr;
            goto cleanup_error;
        }
        port = htons(service->s_port); /* convert to host byte order */
    }
     /*********************/
    /* setup struct Addr */
    addr->proto = proto;
    addr->port = port;
    if(ipv6) {
        ipstr++;
        strncpy_s(addr->addr, sizeof(addr->addr), start_ip, end_ipv6 - start_ip);
    }
    else {
        ///* add check for ipv6 protocol later
        struct hostent *entry = gethostbyname(domain_buffer);
        strncpy(addr->addr, inet_ntoa(*(struct in_addr*)entry->h_addr_list[0]), sizeof(addr->addr));
    }
    if(ipv6 && addr->proto == tcp)
        addr->proto = tcp6;
    else if(ipv6 && addr->proto == udp)
        addr->proto = udp6;
     /****************************/
    /* setup struct sockaddr_in */
    switch(addr->proto) {
        case tcp6:
        case udp6:
            ((struct sockaddr_in6*)_sockaddr_storage)->sin6_family = AF_INET6;
            break;
        case tcp:   /* NOTE: add later should only be usable for listener if calls like 'tcp::port' */
        case udp:   /* NOTE: same as tcp */ 
        case tcp4:
        case udp4:
            ((struct sockaddr_in*)_sockaddr_storage)->sin_family = AF_INET;
            break;
        default:
            err = -3; /* if this happens and internal error occured because this function should check strict and returns if something not parsed correctly*/
            goto cleanup_error;
            break;
    }
    
    /* set port */
    ((struct sockaddr_in*)_sockaddr_storage)->sin_port = htons(addr->port);
    
    if(ipv6) {
        if(inet_pton(((struct sockaddr_in6*)_sockaddr_storage)->sin6_family, addr->addr, &((struct sockaddr_in6*)_sockaddr_storage)->sin6_addr) != 1) {
            err = -2;
            goto cleanup_error;
        }
    }
    else { /* ipv4 */
        if(inet_pton(((struct sockaddr_in*)_sockaddr_storage)->sin_family, addr->addr, &((struct sockaddr_in*)_sockaddr_storage)->sin_addr) != 1) {
            err = -2;
            goto cleanup_error;
        }
    }
    /* set correct socket type */
    if(addr->proto >= tcp && addr->proto <= tcp6)
        *socktype = SOCK_STREAM; /* socket is tcp socket */
    else if(addr->proto >= udp && addr->proto <= udp6)
        *socktype = SOCK_DGRAM;  /* socket is udp socket */
    return err; /* error is 0 (success) */
    /* if some error occure you land here handle the struct Addr properly */
    cleanup_error: {
        *addr->addr = '\0';
        addr->port = -1;
        addr->proto = -1;
        return err;
    }
}
int main(void)
{
    Addr test;
    struct sockaddr_storage addr;
    int socktype;
    clock_t clock_start, clock_end;
    clock_start = clock();
    printf("%d\n", ipstr_to_addr("tcp:[2001::124:2144:153]:http", &test, &addr, &socktype));
    clock_end = clock();
    printf("%d:%s:%d - ", test.proto, test.addr, test.port);
    printf("%f ms\n", (double) (clock_end - clock_start) / CLOCKS_PER_SEC);
    
    clock_start = clock();
    ipstr_to_addr("udp:www.google.de:http", &test, &addr, &socktype);
    clock_end = clock();
    printf("%d:%s:%d - ", test.proto, test.addr, test.port);
    printf("%f ms\n", (double) (clock_end - clock_start) / CLOCKS_PER_SEC);
    clock_start = clock();
    ipstr_to_addr("tcp:localhost:8080", &test, &addr, &socktype);
    clock_end = clock();
    printf("%d:%s:%d - ", test.proto, test.addr, test.port);
    printf("%f ms\n", (double) (clock_end - clock_start) / CLOCKS_PER_SEC);
    clock_start = clock();
    ipstr_to_addr("tcp:127.129.41.24:463", &test, &addr, &socktype);
    clock_end = clock();
    printf("%d:%s:%d - ", test.proto, test.addr, test.port);
    printf("%f ms\n", (double) (clock_end - clock_start) / CLOCKS_PER_SEC);
    /* open listener with netcat and run this */
    int sock = socket(addr.ss_family, SOCK_STREAM, 0);
    if(sock == -1) {
        perror("socket error");
        exit(1);
    }
    if(connect(sock, (struct sockaddr*) &addr, sizeof addr)) {
        perror("connection failed");
        exit(1);
    }
    send(sock, "Hallo Meister\n", 15, 0);
    close(sock);
}
```