From 0d22e6fc6f3b52d2b0f8940076d202e42a193586 Mon Sep 17 00:00:00 2001 From: "git@daemon.de" Date: Fri, 24 Apr 2015 21:22:16 +0200 Subject: [PATCH] added v6 support, untested, may not work yet --- host.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ host.h | 54 +++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 host.c create mode 100644 host.h diff --git a/host.c b/host.c new file mode 100644 index 0000000..b111381 --- /dev/null +++ b/host.c @@ -0,0 +1,132 @@ +/* + This file is part of udpxd. + + Copyright (C) 2015 T.v.Dein. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + You can contact me by mail: . +*/ + +#include "host.h" + +/* fill a generic struct depending of what we already know, + which is easier to pass between functions, + maybe v4 or v6, filled from existing structs or from strings, + which create the sockaddr* structs */ +host_t *get_host(char *ip, int port, struct sockaddr_in *v4, struct sockaddr_in6 *v6) { + host_t *host = malloc(sizeof(host_t)); + host->sock = NULL; + host->is_v6 = 0; + host->port = port; + + if(ip != NULL) { + if(is_v6(ip)) { + struct sockaddr_in6 *tmp = malloc(sizeof(struct sockaddr_in6)); + memset(tmp, 0, sizeof(struct sockaddr_in6)); + + inet_pton(AF_INET6, ip, (struct in6_addr*)&tmp->sin6_addr); + + tmp->sin6_family = AF_INET6; + tmp->sin6_port = htons( port ); + + if (is_linklocal((struct in6_addr*)&tmp->sin6_addr)) + tmp->sin6_scope_id = get_v6_scope(ip); + else + tmp->sin6_scope_id = 0; + + host->is_v6 = 1; + host->sock = (struct sockaddr*)tmp; + host->size = sizeof(struct sockaddr_in6); + } + else { + struct sockaddr_in *tmp = malloc(sizeof(struct sockaddr_in)); + memset(tmp, 0, sizeof(struct sockaddr_in)); + tmp->sin_family = AF_INET; + tmp->sin_addr.s_addr = inet_addr( ip ); + tmp->sin_port = htons( port ); + host->sock = (struct sockaddr*)tmp; + host->size = sizeof(struct sockaddr_in); + } + } + else if(v4 != NULL) { + struct sockaddr_in *tmp = malloc(sizeof(struct sockaddr_in)); + memcpy(tmp, v4, sizeof(struct sockaddr_in)); + host->ip = malloc(INET_ADDRSTRLEN); + inet_ntop(AF_INET, (struct in_addr *)&tmp->sin_addr, host->ip, INET_ADDRSTRLEN); + host->port = ntohs(tmp->sin_port); + host->sock = (struct sockaddr*)tmp; + host->size = sizeof(struct sockaddr_in); + } + else if(v6 != NULL) { + struct sockaddr_in6 *tmp = malloc(sizeof(struct sockaddr_in6)); + memcpy(tmp, v6, sizeof(struct sockaddr_in6)); + host->ip = malloc(INET6_ADDRSTRLEN); + inet_ntop(AF_INET, (struct in6_addr *)&tmp->sin6_addr, host->ip, INET6_ADDRSTRLEN); + host->port = ntohs(tmp->sin6_port); + host->sock = (struct sockaddr*)tmp; + host->is_v6 = 1; + host->size = sizeof(struct sockaddr_in6); + } + else { + fprintf(stderr, "call invalid!\n"); + exit(1); /* shall not happen */ + } + + return host; +} + +char *is_v6(char *ip) { + return strchr(ip, ':'); +} + +/* via http://stackoverflow.com/questions/13504934/binding-sockets-to-ipv6-addresses + return the interface index (aka scope) of an ipv6 address, which is required + in order to bind to it. +*/ +unsigned get_v6_scope(const char *ip){ + struct ifaddrs *addrs, *addr; + char ipAddress[NI_MAXHOST]; + uint32_t scope=0; + int i; + + // walk over the list of all interface addresses + getifaddrs(&addrs); + for(addr=addrs;addr;addr=addr->ifa_next){ + if (addr->ifa_addr && addr->ifa_addr->sa_family==AF_INET6){ // only interested in ipv6 ones + getnameinfo(addr->ifa_addr,sizeof(struct sockaddr_in6),ipAddress,sizeof(ipAddress),NULL,0,NI_NUMERICHOST); + // result actually contains the interface name, so strip it + for(i=0;ipAddress[i];i++){ + if(ipAddress[i]=='%'){ + ipAddress[i]='\0'; + break; + } + } + // if the ip matches, convert the interface name to a scope index + if(strcmp(ipAddress,ip)==0){ + scope=if_nametoindex(addr->ifa_name); + break; + } + } + } + freeifaddrs(addrs); + fprintf(stderr, "scope: %d\n", scope); + return scope; +} + +/* this is the contents of the makro IN6_IS_ADDR_LINKLOCAL, + which doesn't compile, when used directly, for whatever reasons */ +int is_linklocal(struct in6_addr *a) { + return ((a->s6_addr[0] == 0xfe) && ((a->s6_addr[1] & 0xc0) == 0x80)); +} diff --git a/host.h b/host.h new file mode 100644 index 0000000..0241f51 --- /dev/null +++ b/host.h @@ -0,0 +1,54 @@ +/* + This file is part of udpxd. + + Copyright (C) 2015 T.v.Dein. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + You can contact me by mail: . +*/ + +#ifndef _HAVE_HOST_H +#define _HAVE_HOST_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include // if_nametoindex() +#include + +struct _host_t { + int is_v6; + struct sockaddr *sock; + size_t size; + char *ip; + int port; +}; +typedef struct _host_t host_t; + +unsigned get_v6_scope(const char *ip); +int is_linklocal(struct in6_addr *a); +host_t *get_host(char *ip, int port, struct sockaddr_in *v4, struct sockaddr_in6 *v6); +char *is_v6(char *ip); + +#endif