implement ipv6

This commit is contained in:
David du Colombier 2012-08-01 04:00:00 +02:00
parent e82890a2aa
commit f5ea2e72ba
8 changed files with 339 additions and 84 deletions

View File

@ -12,10 +12,11 @@ OFILES=\
qlock.o\
rendez.o\
task.o\
ip.o\
all: $(LIB) primes tcpproxy testdelay
$(OFILES): taskimpl.h task.h 386-ucontext.h power-ucontext.h
$(OFILES): taskimpl.h task.h 386-ucontext.h power-ucontext.h ip.h
AS=gcc -c
CC=gcc

View File

@ -9,10 +9,11 @@ OFILES=\
qlock.o\
rendez.o\
task.o\
ip.o\
all: $(LIB) primes tcpproxy testdelay
$(OFILES): taskimpl.h task.h 386-ucontext.h power-ucontext.h
$(OFILES): taskimpl.h task.h 386-ucontext.h power-ucontext.h ip.h
AS=as
CC=cc

4
README
View File

@ -131,10 +131,10 @@ int netaccept(int fd, char *server, int *port)
Get the next connection that comes in to the listener fd.
Returns a fd to use to talk to the guy who just connected.
If server is not null, it must point at a buffer of at least
16 bytes that is filled in with the remote IP address.
46 bytes that is filled in with the remote IP address.
If port is not null, it is filled in with the report port.
Example:
char server[16];
char server[46];
int port;
if(netaccept(fd, server, &port) >= 0)

211
ip.c Normal file
View File

@ -0,0 +1,211 @@
#include "taskimpl.h"
#include <ctype.h>
#include <string.h>
#include "ip.h"
void
hnputl(void *p, uint v)
{
uchar *a;
a = p;
a[0] = v>>24;
a[1] = v>>16;
a[2] = v>>8;
a[3] = v;
}
void
hnputs(void *p, ushort v)
{
uchar *a;
a = p;
a[0] = v>>8;
a[1] = v;
}
uint32_t
nhgetl(void *p)
{
unsigned char *a;
a = p;
return (a[0]<<24)|(a[1]<<16)|(a[2]<<8)|(a[3]<<0);
}
uint16_t
nhgets(void *p)
{
unsigned char *a;
a = p;
return (a[0]<<8)|(a[1]<<0);
}
char*
v4parseip(unsigned char *to, char *from)
{
int i;
char *p;
p = from;
for(i = 0; i < 4 && *p; i++){
to[i] = strtoul(p, &p, 0);
if(*p == '.')
p++;
}
switch(CLASS(to)){
case 0: /* class A - 1 uchar net */
case 1:
if(i == 3){
to[3] = to[2];
to[2] = to[1];
to[1] = 0;
} else if (i == 2){
to[3] = to[1];
to[1] = 0;
}
break;
case 2: /* class B - 2 uchar net */
if(i == 3){
to[3] = to[2];
to[2] = 0;
}
break;
}
return p;
}
static int
ipcharok(int c)
{
return c == '.' || c == ':' || (isascii(c) && isxdigit(c));
}
static int
delimchar(int c)
{
if(c == '\0')
return 1;
if(c == '.' || c == ':' || (isascii(c) && isalnum(c)))
return 0;
return 1;
}
uint32_t
parseip(uchar *to, char *from)
{
int i, elipsis = 0, v4 = 1;
uint32_t x;
char *p, *op;
memset(to, 0, IPaddrlen);
p = from;
for(i = 0; i < IPaddrlen && ipcharok(*p); i+=2){
op = p;
x = strtoul(p, &p, 16);
if(*p == '.' || (*p == 0 && i == 0)){ /* ends with v4? */
p = v4parseip(to+i, op);
i += 4;
break;
}
/* v6: at most 4 hex digits, followed by colon or delim */
if(x != (ushort)x || (*p != ':' && !delimchar(*p))) {
memset(to, 0, IPaddrlen);
return -1; /* parse error */
}
to[i] = x>>8;
to[i+1] = x;
if(*p == ':'){
v4 = 0;
if(*++p == ':'){ /* :: is elided zero short(s) */
if (elipsis) {
memset(to, 0, IPaddrlen);
return -1; /* second :: */
}
elipsis = i+2;
p++;
}
} else if (p == op) /* strtoul made no progress? */
break;
}
if (p == from || !delimchar(*p)) {
memset(to, 0, IPaddrlen);
return -1; /* parse error */
}
if(i < IPaddrlen){
memmove(&to[elipsis+IPaddrlen-i], &to[elipsis], i-elipsis);
memset(&to[elipsis], 0, IPaddrlen-i);
}
if(v4){
to[10] = to[11] = 0xff;
return nhgetl(to + IPv4off);
} else
return 6;
}
uchar IPnoaddr[IPaddrlen];
uchar v4prefix[IPaddrlen] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0xff, 0xff,
0, 0, 0, 0
};
int
isv4(uchar *ip)
{
return memcmp(ip, v4prefix, IPv4off) == 0;
}
void
v4tov6(unsigned char *v6, unsigned char *v4)
{
v6[0] = 0;
v6[1] = 0;
v6[2] = 0;
v6[3] = 0;
v6[4] = 0;
v6[5] = 0;
v6[6] = 0;
v6[7] = 0;
v6[8] = 0;
v6[9] = 0;
v6[10] = 0xff;
v6[11] = 0xff;
v6[12] = v4[0];
v6[13] = v4[1];
v6[14] = v4[2];
v6[15] = v4[3];
}
int
v6tov4(unsigned char *v4, unsigned char *v6)
{
if(v6[0] == 0
&& v6[1] == 0
&& v6[2] == 0
&& v6[3] == 0
&& v6[4] == 0
&& v6[5] == 0
&& v6[6] == 0
&& v6[7] == 0
&& v6[8] == 0
&& v6[9] == 0
&& v6[10] == 0xff
&& v6[11] == 0xff)
{
v4[0] = v6[12];
v4[1] = v6[13];
v4[2] = v6[14];
v4[3] = v6[15];
return 0;
} else {
memset(v4, 0, 4);
if(memcmp(v6, IPnoaddr, IPaddrlen) == 0)
return 0;
return -1;
}
}

32
ip.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef _IP_H_
#define _IP_H_ 1
#ifdef __cplusplus
extern "C" {
#endif
enum
{
IPaddrlen = 16,
IPv4addrlen = 4,
IPv4off = 12
};
int isv4(unsigned char*);
uint32_t parseip(unsigned char*, char*);
char* v4parseip(unsigned char*, char*);
void hnputl(void*, uint32_t);
void hnputs(void*, uint16_t);
uint32_t nhgetl(void*);
uint16_t nhgets(void*);
int v6tov4(unsigned char*, unsigned char*);
void v4tov6(unsigned char*, unsigned char*);
#define CLASS(p) ((*(uchar*)(p))>>6)
#ifdef __cplusplus
}
#endif
#endif

166
net.c
View File

@ -4,29 +4,56 @@
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/poll.h>
#include "ip.h"
static int
family(unsigned char *addr)
{
if(isv4(addr))
return AF_INET;
return AF_INET6;
}
int
netannounce(int istcp, char *server, int port)
{
int fd, n, proto;
struct sockaddr_in sa;
struct sockaddr_storage ss;
socklen_t sn;
uint32_t ip;
unsigned char ip[IPaddrlen];
taskstate("netannounce");
proto = istcp ? SOCK_STREAM : SOCK_DGRAM;
memset(&sa, 0, sizeof sa);
sa.sin_family = AF_INET;
memset(&ss, 0, sizeof ss);
if(server != nil && strcmp(server, "*") != 0){
if(netlookup(server, &ip) < 0){
if(netlookup(server, ip) < 0){
taskstate("netlookup failed");
return -1;
}
memmove(&sa.sin_addr, &ip, 4);
ss.ss_family = family(ip);
switch(ss.ss_family){
case AF_INET:
v6tov4((unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr, ip);
break;
case AF_INET6:
memcpy(&((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr, ip, sizeof(struct in6_addr));
break;
}
}else{
ss.ss_family = AF_INET6;
((struct sockaddr_in6*)&ss)->sin6_addr = in6addr_any;
}
sa.sin_port = htons(port);
if((fd = socket(AF_INET, proto, 0)) < 0){
switch(ss.ss_family){
case AF_INET:
hnputs(&((struct sockaddr_in*)&ss)->sin_port, port);
break;
case AF_INET6:
hnputs(&((struct sockaddr_in6*)&ss)->sin6_port, port);
break;
}
if((fd = socket(ss.ss_family, proto, 0)) < 0){
taskstate("socket failed");
return -1;
}
@ -37,7 +64,7 @@ netannounce(int istcp, char *server, int port)
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n);
}
if(bind(fd, (struct sockaddr*)&sa, sizeof sa) < 0){
if(bind(fd, (struct sockaddr*)&ss, sizeof ss) < 0){
taskstate("bind failed");
close(fd);
return -1;
@ -55,24 +82,32 @@ int
netaccept(int fd, char *server, int *port)
{
int cfd, one;
struct sockaddr_in sa;
uchar *ip;
struct sockaddr_storage ss;
socklen_t len;
fdwait(fd, 'r');
taskstate("netaccept");
len = sizeof sa;
if((cfd = accept(fd, (void*)&sa, &len)) < 0){
len = sizeof ss;
if((cfd = accept(fd, (void*)&ss, &len)) < 0){
taskstate("accept failed");
return -1;
}
if(server){
ip = (uchar*)&sa.sin_addr;
snprint(server, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
switch(ss.ss_family){
case AF_INET:
if(server)
inet_ntop(AF_INET, &((struct sockaddr_in*)&ss)->sin_addr.s_addr, server, INET_ADDRSTRLEN);
if(port)
*port = nhgets(&((struct sockaddr_in*)&ss)->sin_port);
break;
case AF_INET6:
if(server)
inet_ntop(AF_INET6, &((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr, server, INET6_ADDRSTRLEN);
if(port)
*port = nhgets(&((struct sockaddr_in6*)&ss)->sin6_port);
break;
}
if(port)
*port = ntohs(sa.sin_port);
fdnoblock(cfd);
one = 1;
setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
@ -80,64 +115,30 @@ netaccept(int fd, char *server, int *port)
return cfd;
}
#define CLASS(p) ((*(unsigned char*)(p))>>6)
static int
parseip(char *name, uint32_t *ip)
{
unsigned char addr[4];
char *p;
int i, x;
p = name;
for(i=0; i<4 && *p; i++){
x = strtoul(p, &p, 0);
if(x < 0 || x >= 256)
return -1;
if(*p != '.' && *p != 0)
return -1;
if(*p == '.')
p++;
addr[i] = x;
}
switch(CLASS(addr)){
case 0:
case 1:
if(i == 3){
addr[3] = addr[2];
addr[2] = addr[1];
addr[1] = 0;
}else if(i == 2){
addr[3] = addr[1];
addr[2] = 0;
addr[1] = 0;
}else if(i != 4)
return -1;
break;
case 2:
if(i == 3){
addr[3] = addr[2];
addr[2] = 0;
}else if(i != 4)
return -1;
break;
}
*ip = *(uint32_t*)addr;
return 0;
}
int
netlookup(char *name, uint32_t *ip)
netlookup(char *name, unsigned char *ip)
{
struct hostent *he;
struct addrinfo *result;
if(parseip(name, ip) >= 0)
if(parseip(ip, name) == 0)
return 0;
/* BUG - Name resolution blocks. Need a non-blocking DNS. */
taskstate("netlookup");
if((he = gethostbyname(name)) != 0){
*ip = *(uint32_t*)he->h_addr;
ip = (unsigned char*)he->h_addr;
taskstate("netlookup succeeded");
return 0;
}else if(getaddrinfo(name, NULL, NULL, &result) == 0) {
switch (result->ai_family) {
case AF_INET:
v4tov6(ip, (unsigned char*)&((struct sockaddr_in*)result->ai_addr)->sin_addr.s_addr);
break;
case AF_INET6:
memcpy(ip, (unsigned char*)&((struct sockaddr_in6*)result->ai_addr)->sin6_addr.s6_addr, sizeof(struct in6_addr));
break;
}
taskstate("netlookup succeeded");
return 0;
}
@ -150,16 +151,16 @@ int
netdial(int istcp, char *server, int port)
{
int proto, fd, n;
uint32_t ip;
struct sockaddr_in sa;
unsigned char ip[IPaddrlen];
struct sockaddr_storage ss;
socklen_t sn;
if(netlookup(server, &ip) < 0)
if(netlookup(server, ip) < 0)
return -1;
taskstate("netdial");
proto = istcp ? SOCK_STREAM : SOCK_DGRAM;
if((fd = socket(AF_INET, proto, 0)) < 0){
if((fd = socket(family(ip), proto, 0)) < 0){
taskstate("socket failed");
return -1;
}
@ -172,11 +173,20 @@ netdial(int istcp, char *server, int port)
}
/* start connecting */
memset(&sa, 0, sizeof sa);
memmove(&sa.sin_addr, &ip, 4);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
if(connect(fd, (struct sockaddr*)&sa, sizeof sa) < 0 && errno != EINPROGRESS){
memset(&ss, 0, sizeof ss);
ss.ss_family = family(ip);
switch(ss.ss_family){
case AF_INET:
v6tov4((unsigned char*)&((struct sockaddr_in*)&ss)->sin_addr.s_addr, ip);
hnputs(&((struct sockaddr_in*)&ss)->sin_port, port);
break;
case AF_INET6:
memcpy(&((struct sockaddr_in6*)&ss)->sin6_addr.s6_addr, ip, sizeof(struct in6_addr));
hnputs(&((struct sockaddr_in6*)&ss)->sin6_port, port);
break;
}
if(connect(fd, (struct sockaddr*)&ss, sizeof ss) < 0 && errno != EINPROGRESS){
taskstate("connect failed");
close(fd);
return -1;
@ -184,8 +194,8 @@ netdial(int istcp, char *server, int port)
/* wait for finish */
fdwait(fd, 'w');
sn = sizeof sa;
if(getpeername(fd, (struct sockaddr*)&sa, &sn) >= 0){
sn = sizeof ss;
if(getpeername(fd, (struct sockaddr*)&ss, &sn) >= 0){
taskstate("connect succeeded");
return fd;
}

2
task.h
View File

@ -172,7 +172,7 @@ enum
int netannounce(int, char*, int);
int netaccept(int, char*, int*);
int netdial(int, char*, int);
int netlookup(char*, uint32_t*); /* blocks entire program! */
int netlookup(char*, unsigned char*); /* blocks entire program! */
int netdial(int, char*, int);
#ifdef __cplusplus

View File

@ -38,7 +38,7 @@ taskmain(int argc, char **argv)
{
int cfd, fd;
int rport;
char remote[16];
char remote[46];
if(argc != 4){
fprintf(stderr, "usage: tcpproxy localport server remoteport\n");