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\ qlock.o\
rendez.o\ rendez.o\
task.o\ task.o\
ip.o\
all: $(LIB) primes tcpproxy testdelay 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 AS=gcc -c
CC=gcc CC=gcc

View File

@ -9,10 +9,11 @@ OFILES=\
qlock.o\ qlock.o\
rendez.o\ rendez.o\
task.o\ task.o\
ip.o\
all: $(LIB) primes tcpproxy testdelay 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 AS=as
CC=cc 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. Get the next connection that comes in to the listener fd.
Returns a fd to use to talk to the guy who just connected. 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 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. If port is not null, it is filled in with the report port.
Example: Example:
char server[16]; char server[46];
int port; int port;
if(netaccept(fd, server, &port) >= 0) 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 <netdb.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/poll.h> #include <sys/poll.h>
#include "ip.h"
static int
family(unsigned char *addr)
{
if(isv4(addr))
return AF_INET;
return AF_INET6;
}
int int
netannounce(int istcp, char *server, int port) netannounce(int istcp, char *server, int port)
{ {
int fd, n, proto; int fd, n, proto;
struct sockaddr_in sa; struct sockaddr_storage ss;
socklen_t sn; socklen_t sn;
uint32_t ip; unsigned char ip[IPaddrlen];
taskstate("netannounce"); taskstate("netannounce");
proto = istcp ? SOCK_STREAM : SOCK_DGRAM; proto = istcp ? SOCK_STREAM : SOCK_DGRAM;
memset(&sa, 0, sizeof sa); memset(&ss, 0, sizeof ss);
sa.sin_family = AF_INET;
if(server != nil && strcmp(server, "*") != 0){ if(server != nil && strcmp(server, "*") != 0){
if(netlookup(server, &ip) < 0){ if(netlookup(server, ip) < 0){
taskstate("netlookup failed"); taskstate("netlookup failed");
return -1; 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;
} }
sa.sin_port = htons(port); }else{
if((fd = socket(AF_INET, proto, 0)) < 0){ ss.ss_family = AF_INET6;
((struct sockaddr_in6*)&ss)->sin6_addr = in6addr_any;
}
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"); taskstate("socket failed");
return -1; return -1;
} }
@ -37,7 +64,7 @@ netannounce(int istcp, char *server, int port)
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&n, sizeof n); 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"); taskstate("bind failed");
close(fd); close(fd);
return -1; return -1;
@ -55,24 +82,32 @@ int
netaccept(int fd, char *server, int *port) netaccept(int fd, char *server, int *port)
{ {
int cfd, one; int cfd, one;
struct sockaddr_in sa; struct sockaddr_storage ss;
uchar *ip;
socklen_t len; socklen_t len;
fdwait(fd, 'r'); fdwait(fd, 'r');
taskstate("netaccept"); taskstate("netaccept");
len = sizeof sa; len = sizeof ss;
if((cfd = accept(fd, (void*)&sa, &len)) < 0){ if((cfd = accept(fd, (void*)&ss, &len)) < 0){
taskstate("accept failed"); taskstate("accept failed");
return -1; return -1;
} }
if(server){
ip = (uchar*)&sa.sin_addr; switch(ss.ss_family){
snprint(server, 16, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); case AF_INET:
} if(server)
inet_ntop(AF_INET, &((struct sockaddr_in*)&ss)->sin_addr.s_addr, server, INET_ADDRSTRLEN);
if(port) if(port)
*port = ntohs(sa.sin_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;
}
fdnoblock(cfd); fdnoblock(cfd);
one = 1; one = 1;
setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one); setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof one);
@ -80,64 +115,30 @@ netaccept(int fd, char *server, int *port)
return cfd; 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 int
netlookup(char *name, uint32_t *ip) netlookup(char *name, unsigned char *ip)
{ {
struct hostent *he; struct hostent *he;
struct addrinfo *result;
if(parseip(name, ip) >= 0) if(parseip(ip, name) == 0)
return 0; return 0;
/* BUG - Name resolution blocks. Need a non-blocking DNS. */ /* BUG - Name resolution blocks. Need a non-blocking DNS. */
taskstate("netlookup"); taskstate("netlookup");
if((he = gethostbyname(name)) != 0){ 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"); taskstate("netlookup succeeded");
return 0; return 0;
} }
@ -150,16 +151,16 @@ int
netdial(int istcp, char *server, int port) netdial(int istcp, char *server, int port)
{ {
int proto, fd, n; int proto, fd, n;
uint32_t ip; unsigned char ip[IPaddrlen];
struct sockaddr_in sa; struct sockaddr_storage ss;
socklen_t sn; socklen_t sn;
if(netlookup(server, &ip) < 0) if(netlookup(server, ip) < 0)
return -1; return -1;
taskstate("netdial"); taskstate("netdial");
proto = istcp ? SOCK_STREAM : SOCK_DGRAM; 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"); taskstate("socket failed");
return -1; return -1;
} }
@ -172,11 +173,20 @@ netdial(int istcp, char *server, int port)
} }
/* start connecting */ /* start connecting */
memset(&sa, 0, sizeof sa); memset(&ss, 0, sizeof ss);
memmove(&sa.sin_addr, &ip, 4); ss.ss_family = family(ip);
sa.sin_family = AF_INET; switch(ss.ss_family){
sa.sin_port = htons(port); case AF_INET:
if(connect(fd, (struct sockaddr*)&sa, sizeof sa) < 0 && errno != EINPROGRESS){ 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"); taskstate("connect failed");
close(fd); close(fd);
return -1; return -1;
@ -184,8 +194,8 @@ netdial(int istcp, char *server, int port)
/* wait for finish */ /* wait for finish */
fdwait(fd, 'w'); fdwait(fd, 'w');
sn = sizeof sa; sn = sizeof ss;
if(getpeername(fd, (struct sockaddr*)&sa, &sn) >= 0){ if(getpeername(fd, (struct sockaddr*)&ss, &sn) >= 0){
taskstate("connect succeeded"); taskstate("connect succeeded");
return fd; return fd;
} }

2
task.h
View File

@ -172,7 +172,7 @@ enum
int netannounce(int, char*, int); int netannounce(int, char*, int);
int netaccept(int, char*, int*); int netaccept(int, char*, int*);
int netdial(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); int netdial(int, char*, int);
#ifdef __cplusplus #ifdef __cplusplus

View File

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