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. And
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 just want toget the following timing: - see what you think about it and what I can do better so feel free to leave a comment or an answer.my main function
/*2:2001::124:2144:153:80 #include- <stdnet0.h>000163 ms
#include3:172.217.16.195:80 <net_private- 0.h>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, ip, ip4, ip6, _unix, unixgram, unixpacket} 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:",
"ip:",
"ip4:",
"ip6:",
"unix:",
"unixgram:",
"unixpacket:"
};
/* 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 ip6:
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 ip4:
case tcp4:
case udp4:
((struct sockaddr_in*)_sockaddr_storage)->sin_family = AF_INET;
break;
case _unix: /* not use how to use it but as this format is copied by golang's net package i add it */
case unixpacket:
_sockaddr_storage->ss_family = AF_UNIX;
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 endangeringhandle 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;
printf("%d\n", ipstr_to_addr("tcp:[2001::124:2144:153]:http", &test, &addr));
printf("%d:%s:%d\n", test.proto,clock_t test.addrclock_start, test.port);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\n"%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:8localhost:8080", &test, &addr, &socktype);
clock_end = clock();
printf("%d:%s:%d - ", test.8proto, test.8addr, test.8port);
printf("%f ms\n", (double) (clock_end - clock_start) / CLOCKS_PER_SEC);
clock_start = clock();
ipstr_to_addr("tcp:http"127.129.41.24:463", &test, &addr, &socktype);
clock_end = clock();
printf("%d:%s:%d\n"%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);
}
```