diff --git a/MANIFEST b/MANIFEST index c0a83f51cc..0de11f63c8 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1907,6 +1907,7 @@ t/pmc/schedulermessage.t [test] t/pmc/signal.t [test] t/pmc/sockaddr.t [test] t/pmc/socket.t [test] +t/pmc/socket_ipv6.t [test] t/pmc/string.t [test] t/pmc/stringbuilder.t [test] t/pmc/stringhandle.t [test] @@ -1919,6 +1920,7 @@ t/pmc/testlib/annotations.pir [test] t/pmc/testlib/number.pasm [test] t/pmc/testlib/packfile_common.pir [test] t/pmc/testlib/test_server.pir [test] +t/pmc/testlib/test_server_ipv6.pir [test] t/pmc/threads.t [test] t/pmc/timer.t [test] t/pmc/undef.t [test] diff --git a/config/gen/makefiles/root.in b/config/gen/makefiles/root.in index 68cadf5de1..840eebe669 100644 --- a/config/gen/makefiles/root.in +++ b/config/gen/makefiles/root.in @@ -1621,7 +1621,7 @@ src/platform/generic/math$(O) : src/platform/generic/math.c $(PARROT_H_HEADERS) src/platform/generic/misc$(O) : src/platform/generic/misc.c $(PARROT_H_HEADERS) src/platform/generic/socket$(O) : $(PARROT_H_HEADERS) include/pmc/pmc_socket.h \ - src/io/io_private.h src/platform/generic/socket.c + src/io/io_private.h include/pmc/pmc_sockaddr.h src/platform/generic/socket.c src/platform/generic/stat$(O) : src/platform/generic/stat.c $(PARROT_H_HEADERS) diff --git a/include/parrot/platform_interface.h b/include/parrot/platform_interface.h index 9f832d2a6b..26240793b6 100644 --- a/include/parrot/platform_interface.h +++ b/include/parrot/platform_interface.h @@ -79,12 +79,14 @@ INTVAL Parrot_io_pipe(PARROT_INTERP, ARGMOD(PIOHANDLE *reader), ARGMOD(PIOHANDLE * Socket */ -void Parrot_io_sockaddr_in(PARROT_INTERP, ARGOUT(void *addr), ARGIN(STRING *host), int port); +PMC * +Parrot_io_getaddrinfo(PARROT_INTERP, ARGIN(STRING *addr), INTVAL port, INTVAL protocol, INTVAL family, INTVAL passive); +void Parrot_io_sockaddr_in(PARROT_INTERP, ARGOUT(void *addr), ARGIN(STRING *host), int port, int family); PIOHANDLE Parrot_io_socket(PARROT_INTERP, int fam, int type, int proto); INTVAL Parrot_io_connect(PARROT_INTERP, PIOHANDLE handle, ARGIN(void *addr)); INTVAL Parrot_io_bind(PARROT_INTERP, PIOHANDLE handle, ARGIN(void *addr)); INTVAL Parrot_io_listen(PARROT_INTERP, PIOHANDLE handle, INTVAL sec); -PIOHANDLE Parrot_io_accept(PARROT_INTERP, PIOHANDLE handle, ARGOUT(void *addr)); +PIOHANDLE Parrot_io_accept(PARROT_INTERP, PIOHANDLE handle, ARGOUT(PMC * remote_addr)); INTVAL Parrot_io_send(PARROT_INTERP, PIOHANDLE handle, ARGIN(const char *buf), size_t len); INTVAL Parrot_io_recv(PARROT_INTERP, PIOHANDLE handle, ARGOUT(char *buf), size_t len); INTVAL Parrot_io_poll(PARROT_INTERP, PIOHANDLE handle, int which, int sec, int usec); diff --git a/src/io/socket_api.c b/src/io/socket_api.c index 9c4e03e50b..131cbe61be 100644 --- a/src/io/socket_api.c +++ b/src/io/socket_api.c @@ -294,8 +294,60 @@ INTVAL Parrot_io_connect_handle(PARROT_INTERP, ARGMOD(PMC *pmc), ARGMOD(PMC *address)) { ASSERT_ARGS(Parrot_io_connect_handle) + struct addrinfo *res, *walk; + int fd = -1; + int i = 1; Parrot_Socket_attributes * const io = PARROT_SOCKET(pmc); + /* Connect to an IPv6 addrinfo if an UnManagedStruct was provided as address */ + if (!PMC_IS_NULL(address) && address->vtable->base_type == enum_class_UnManagedStruct) { + res = VTABLE_get_pointer(interp, address); + + for (walk = res; walk != NULL; walk = walk->ai_next) { + fd = socket(walk->ai_family, walk->ai_socktype, walk->ai_protocol); + if (fd < 0) { + /* Cannot create socket. Not necessarily an error, for example not + * on FreeBSD, where getaddrinfo() returns IPv6 addresses even + * when the libc does not offer IPv6 support and thus fails in + * socket(). */ + continue; + } + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) == -1) { + perror("Error setting SO_REUSEADDR:"); + continue; + } + + /* XXX: this effectively overwrites any previously set sockets. is that alright? */ + SETATTR_Socket_os_handle(interp, pmc, fd); + +AGAIN: + if (connect(fd, walk->ai_addr, walk->ai_addrlen) != 0) { + switch (errno) { + case EINTR: + goto AGAIN; + case EINPROGRESS: + goto AGAIN; + case EISCONN: + break; + default: + close(fd); + fd = -1; + continue; + } + } + + PARROT_SOCKET(pmc)->remote = Parrot_pmc_new(interp, enum_class_Sockaddr); + + VTABLE_set_pointer(interp, PARROT_SOCKET(pmc)->remote, walk); + return 0; + } + + if (fd == -1) + return -1; + fprintf(stderr, "huh?!\n"); + } + if (Parrot_io_socket_is_closed(interp, pmc)) return -1; if (PMC_IS_NULL(address)) @@ -323,8 +375,52 @@ INTVAL Parrot_io_bind_handle(PARROT_INTERP, ARGMOD(PMC *pmc), ARGMOD(PMC *address)) { ASSERT_ARGS(Parrot_io_bind_handle) + struct addrinfo *res, *walk; + int fd = -1; + int i = 1; Parrot_Socket_attributes * const io = PARROT_SOCKET(pmc); + /* Bind to an IPv6 address (UnManagedStruct with an struct addrinfo inside */ + if (!PMC_IS_NULL(address) && address->vtable->base_type == enum_class_UnManagedStruct) { + res = VTABLE_get_pointer(interp, address); + + for (walk = res; walk != NULL; walk = walk->ai_next) { + fd = socket(walk->ai_family, walk->ai_socktype, walk->ai_protocol); + if (fd < 0) { + /* Cannot create socket. Not necessarily an error, for example not + * on FreeBSD, where getaddrinfo() returns IPv6 addresses even + * when the libc does not offer IPv6 support and thus fails in + * socket(). */ + continue; + } + + if (walk->ai_family == AF_INET6) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &i, sizeof(i)) == -1) { + perror("Error setting IPV6_V6ONLY:"); + continue; + } + } + + /* XXX: this effectively overwrites any previously set sockets. is that alright? */ + SETATTR_Socket_os_handle(interp, pmc, fd); + + if (bind(fd, walk->ai_addr, walk->ai_addrlen) != 0) { + close(fd); + fd = -1; + continue; + } + + PARROT_SOCKET(pmc)->local = Parrot_pmc_new(interp, enum_class_Sockaddr); + + VTABLE_set_pointer(interp, PARROT_SOCKET(pmc)->local, walk); + return 0; + } + + if (fd == -1) + return -1; + + } + if (Parrot_io_socket_is_closed(interp, pmc)) return -1; if (PMC_IS_NULL(address)) @@ -393,8 +489,7 @@ Parrot_io_accept_handle(PARROT_INTERP, ARGMOD(PMC *pmc)) new_io->local = io->local; new_io->remote = Parrot_pmc_new(interp, enum_class_Sockaddr); - os_handle = Parrot_io_accept(interp, io->os_handle, - VTABLE_get_pointer(interp, new_io->remote)); + os_handle = Parrot_io_accept(interp, io->os_handle, new_io->remote); /* TODO: error check */ new_io->os_handle = os_handle; diff --git a/src/platform/generic/socket.c b/src/platform/generic/socket.c index 037efff5cb..3e474b574f 100644 --- a/src/platform/generic/socket.c +++ b/src/platform/generic/socket.c @@ -18,6 +18,7 @@ src/platform/generic/socket.c - UNIX socket functions #include "parrot/parrot.h" #include "../../io/io_private.h" #include "pmc/pmc_socket.h" +#include "pmc/pmc_sockaddr.h" /* Windows defines file handles as void * and sockets as unsigned int. * We use void * for Windows PIOHANDLEs, so we have to cast them for the @@ -61,6 +62,27 @@ typedef int PIOSOCKET; /* HEADERIZER HFILE: none */ +/* HEADERIZER BEGIN: static */ +/* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */ + +static void get_addrinfo(PARROT_INTERP, + ARGIN(PMC * addrinfo), + ARGIN(const char *host), + int port, + int protocol, + int family, + int passive) + __attribute__nonnull__(1) + __attribute__nonnull__(2) + __attribute__nonnull__(3); + +#define ASSERT_ARGS_get_addrinfo __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ + PARROT_ASSERT_ARG(interp) \ + , PARROT_ASSERT_ARG(addrinfo) \ + , PARROT_ASSERT_ARG(host)) +/* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */ +/* HEADERIZER END: static */ + /* =back @@ -78,6 +100,61 @@ Very minimal stubs for now, maybe someone will run with these. */ + +/* + +=item C + +C calls get_addrinfo() to convert hostnames or IP +addresses to sockaddrs (and more) and returns an Addrinfo PMC which can be +passed to C or C. + +=cut + +*/ + +/* TODO: where to move this to? originally from src/io/socket_api.c */ +static int pio_pf[PIO_PF_MAX+1] = { +#ifdef PF_LOCAL + PF_LOCAL, /* PIO_PF_LOCAL */ +#else + -1, /* PIO_PF_LOCAL */ +#endif +#ifdef PF_UNIX + PF_UNIX, /* PIO_PF_UNIX */ +#else + -1, /* PIO_PF_UNIX */ +#endif +#ifdef PF_INET + PF_INET, /* PIO_PF_INET */ +#else + -1, /* PIO_PF_INET */ +#endif +#ifdef PF_INET6 + PF_INET6, /* PIO_PF_INET6 */ +#else + -1, /* PIO_PF_INET6 */ +#endif +}; + +PARROT_WARN_UNUSED_RESULT +PARROT_CANNOT_RETURN_NULL +PMC * +Parrot_io_getaddrinfo(PARROT_INTERP, ARGIN(STRING *addr), INTVAL port, INTVAL protocol, INTVAL family, INTVAL passive) +{ + char * const s = Parrot_str_to_cstring(interp, addr); + PMC * const addrinfo = Parrot_pmc_new(interp, enum_class_UnManagedStruct); + + /* set family: 0 means any (AF_INET or AF_INET6) for getaddrinfo, so treat + * it specially */ + int fam = (family != 0 ? pio_pf[family] : 0); + + get_addrinfo(interp, addrinfo, s, port, protocol, fam, passive); + Parrot_str_free_cstring(s); + return addrinfo; +} + /* =item C @@ -194,16 +271,21 @@ Accept a new connection and return a newly created C socket. PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL PIOHANDLE -Parrot_io_accept(PARROT_INTERP, PIOHANDLE os_handle, ARGOUT(void *addr)) +Parrot_io_accept(PARROT_INTERP, PIOHANDLE os_handle, ARGOUT(PMC * remote_addr)) { + struct sockaddr * addr = (struct sockaddr *)VTABLE_get_pointer(interp, remote_addr); Parrot_Socklen_t addrlen = sizeof (struct sockaddr_in); PIOSOCKET newsock; - newsock = accept((PIOSOCKET)os_handle, (struct sockaddr *)addr, &addrlen); + newsock = accept((PIOSOCKET)os_handle, addr, &addrlen); if (newsock == PIO_INVALID_SOCKET) return PIO_INVALID_HANDLE; + /* Set the length for the remote sockaddr PMC so that it can distinguish + * between sockaddr_in and sockaddr_in6 */ + PARROT_SOCKADDR(remote_addr)->len = addrlen; + /* XXX FIXME: Need to do a getsockname and getpeername here to * fill in the sockaddr_in structs for local and peer */ @@ -341,7 +423,7 @@ Parrot_io_poll(SHIM_INTERP, PIOHANDLE os_handle, int which, int sec, /* =item C +int port, int family)> Fill in a C structure to connect to the specified host and port. @@ -351,15 +433,16 @@ Fill in a C structure to connect to the specified host and port. void Parrot_io_sockaddr_in(PARROT_INTERP, ARGOUT(void *addr), - ARGIN(STRING *host_str), int port) + ARGIN(STRING *host_str), int port, int family) { char * const host = Parrot_str_to_cstring(interp, host_str); - /* Hard coded to IPv4 for now */ - const int family = AF_INET; int success; struct sockaddr_in * const sa = (struct sockaddr_in*)addr; + /* Hard coded to IPv4 for now */ + family = AF_INET; + #ifdef _WIN32 sa->sin_addr.S_un.S_addr = inet_addr(host); success = sa->sin_addr.S_un.S_addr != -1; @@ -412,6 +495,89 @@ Parrot_io_close_socket(SHIM_INTERP, PIOHANDLE handle) #endif } +/* + +=item C + +C returns the remote address of the given sock +PMC. It can be used to find out to which address the connection was actually +established (in case of the remote server having multiple IPv4 and/or IPv6 +addresses. + +=cut + +*/ +PARROT_WARN_UNUSED_RESULT +PARROT_CANNOT_RETURN_NULL +PMC * +Parrot_io_remote_address(PARROT_INTERP, ARGIN(PMC *sock)) +{ + PMC * const addrinfo = VTABLE_clone(interp, PARROT_SOCKET(sock)->remote); + + return addrinfo; +} + +/* + +=item C + +C returns the local address of the given sock +PMC. It can be used to find out to which address the socket was actually +bound (when binding to "localhost" without explicitly specifying an address +family, for example). + +=cut + +*/ +PARROT_WARN_UNUSED_RESULT +PARROT_CANNOT_RETURN_NULL +PMC * +Parrot_io_local_address(PARROT_INTERP, ARGIN(PMC *sock)) +{ + PMC * const addrinfo = VTABLE_clone(interp, PARROT_SOCKET(sock)->local); + + return addrinfo; +} + +/* + +=item C + +Internal helper function to call getaddrinfo and store the result pointer in +the given PMC (of type UnManagedStruct). + +=cut + +*/ + +static void +get_addrinfo(PARROT_INTERP, ARGIN(PMC * addrinfo), ARGIN(const char *host), int port, int protocol, int family, int passive) +{ + ASSERT_ARGS(get_addrinfo) + + struct addrinfo hints; + struct addrinfo *res; + /* We need to pass the port as a string (because you could also use a + * service specification from /etc/services). The highest port is 65535, + * so we need 5 characters + trailing null-byte. */ + char portstr[6]; + int ret; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_protocol = protocol; + if (passive) + hints.ai_flags = AI_PASSIVE; + hints.ai_family = family; + snprintf(portstr, sizeof(portstr), "%u", port); + + if ((ret = getaddrinfo(host, portstr, &hints, &res)) != 0) { + fprintf(stderr, "getaddrinfo failure: %s\n", gai_strerror(ret)); + return; + } + + VTABLE_set_pointer(interp, addrinfo, res); +} /* diff --git a/src/pmc/sockaddr.pmc b/src/pmc/sockaddr.pmc index 4cbe490685..963ff80546 100644 --- a/src/pmc/sockaddr.pmc +++ b/src/pmc/sockaddr.pmc @@ -3,11 +3,12 @@ Copyright (C) 2008-2009, Parrot Foundation. =head1 NAME -src/pmc/sockaddr.pmc - sockaddr_in holder +src/pmc/sockaddr.pmc - sockaddr_in/sockaddr_in6 holder =head1 DESCRIPTION -The Sockaddr PMC holds raw c-pointer to sockaddr_in +The Sockaddr PMC holds a C (IPv4) or C (IPv6) and +saves its length (to distinguish C and C). =head2 Vtable Functions @@ -23,7 +24,7 @@ These are the vtable functions for the Sockaddr class. #ifdef __cplusplus extern "C" { #endif - struct sockaddr_in; + struct sockaddr_storage; #ifdef __cplusplus } #endif @@ -34,12 +35,13 @@ extern "C" { pmclass Sockaddr auto_attrs { ATTR void *pointer; /* The stored pointer. */ + ATTR INTVAL len; /* Length of the contents of the sockaddr_storage */ /* =item C -Initializes the pointer object. +Initializes the PMC by allocating a C. =cut @@ -50,7 +52,7 @@ Initializes the pointer object. (Parrot_Sockaddr_attributes *) PMC_data(SELF); pdata_struct->pointer = mem_gc_allocate_zeroed_typed(INTERP, - struct sockaddr_in); + struct sockaddr_storage); PObj_custom_destroy_SET(SELF); } @@ -77,7 +79,8 @@ Destroys the PMC and frees all allocated memory. =item C -Creates and returns a clone of the pointer. +Creates a new Sockaddr PMC with the same contents and length as the current +one. =cut @@ -85,16 +88,34 @@ Creates and returns a clone of the pointer. VTABLE PMC *clone() { PMC * const dest = Parrot_pmc_new(INTERP, SELF->vtable->base_type); + memcpy(PARROT_SOCKADDR(dest)->pointer, PARROT_SOCKADDR(SELF)->pointer, - sizeof (struct sockaddr_in)); + sizeof (struct sockaddr_storage)); + PARROT_SOCKADDR(dest)->len = PARROT_SOCKADDR(SELF)->len; return dest; } /* +=item C + +Returns true if the Sockaddr is defined. + +=cut + +*/ + + VTABLE INTVAL get_bool() { + Parrot_Sockaddr_attributes * const data = PARROT_SOCKADDR(SELF); + + return data->pointer ? 1 : 0 ; + } + +/* + =item C -Returns the pointer. +Returns a pointer to the C or C. =cut @@ -107,20 +128,48 @@ Returns the pointer. /* -=item C +=item C -Sets the pointer. +Returns the string representation of this sockaddr by calling C. =cut */ + VTABLE STRING *get_string() { + Parrot_Sockaddr_attributes * const data = PARROT_SOCKADDR(SELF); + struct sockaddr_storage *addr = data->pointer; + /* TODO: get hostname, not only numeric */ + char buf[INET6_ADDRSTRLEN+1]; + /* numeric port maximum is 65535, so 5 chars */ + char portbuf[6]; + + if (!data->pointer) + return Parrot_sprintf_c(interp, "(?)"); + + getnameinfo((struct sockaddr_storage*)data->pointer, data->len, buf, + sizeof(buf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV); + return Parrot_str_format_data(interp, "%s:%s", buf, portbuf); + } + /* + +=item C + +Copies a C or C from the given C pointer +(by accessing its C and C members). + +=cut + +*/ + VTABLE void set_pointer(void *value) { Parrot_Sockaddr_attributes * const data = PARROT_SOCKADDR(SELF); - return data->pointer; + + struct addrinfo *walk = value; + memcpy(data->pointer, walk->ai_addr, walk->ai_addrlen); + data->len = walk->ai_addrlen; } -*/ } diff --git a/src/pmc/socket.pmc b/src/pmc/socket.pmc index 7d122f3709..b97b12fd98 100644 --- a/src/pmc/socket.pmc +++ b/src/pmc/socket.pmc @@ -18,6 +18,7 @@ The Socket PMC performs network I/O operations. */ #include "../src/io/io_private.h" +#include "pmc/pmc_sockaddr.h" #define CHUNK_SIZE 2048 @@ -169,22 +170,73 @@ for readable, two for writeable, and four for exceptions. /* -=item C +=item C C returns an object representing a socket address, generated -from a port number (integer) and an address (string). +from a port number (integer) , address (string) and an optional address +family (integer). If no address family is given, it defaults to IPv4. =cut */ - METHOD sockaddr(STRING * address, INTVAL port) { - PMC * const sockaddr = Parrot_pmc_new(interp, enum_class_Sockaddr); + METHOD sockaddr(STRING * address, INTVAL port, INTVAL family :optional) { + PMC * sockaddr; + + if (!family) + family = AF_INET; + + sockaddr = Parrot_pmc_new(interp, enum_class_Sockaddr); Parrot_io_sockaddr_in(INTERP, - VTABLE_get_pointer(INTERP, sockaddr), address, port); + VTABLE_get_pointer(INTERP, sockaddr), address, port, family); + PARROT_SOCKADDR(sockaddr)->len = sizeof(struct sockaddr_in); RETURN(PMC * sockaddr); } +/* + +=item C + +C returns an object representing the result of the +C function which consists of multiple socket addresses, +including family and protocol. It can be passed to C or C. + +=cut + +*/ + METHOD getaddrinfo(STRING * address, INTVAL port, INTVAL protocol, INTVAL family, INTVAL passive) { + PMC * res = Parrot_io_getaddrinfo(INTERP, address, port, protocol, family, passive); + RETURN(PMC * res); + } + +/* + +=item C + +C returns the remote address of this socket PMC. + +=cut + +*/ + METHOD remote_address() { + PMC * res = Parrot_io_remote_address(INTERP, SELF); + RETURN(PMC * res); + } + +/* + +=item C + +C returns the local address of this socket PMC. + +=cut + +*/ + METHOD local_address() { + PMC * res = Parrot_io_local_address(INTERP, SELF); + RETURN(PMC * res); + } + /* @@ -277,7 +329,7 @@ complete, it invokes the callback, passing it a status object. =item C C binds a socket object to the port and address specified by an -address object (the packed result of C). +address object (the result of C). The asynchronous version takes an additional final PMC callback argument, and only returns a status object. When the bind operation is diff --git a/t/pmc/sockaddr.t b/t/pmc/sockaddr.t index 49ef56bd0c..b69587b5f0 100644 --- a/t/pmc/sockaddr.t +++ b/t/pmc/sockaddr.t @@ -18,10 +18,11 @@ Test the Sockaddr PMC. .sub main :main .include 'test_more.pir' - plan(6) + plan(7) test_basic() test_bool() + test_string() .end .sub test_basic @@ -46,13 +47,13 @@ Test the Sockaddr PMC. .sub test_bool $P0 = new 'Socket' $P1 = $P0."sockaddr"("localhost", 1234) - push_eh handler ok($P1, 'get_bool on a SockAddr') - goto done -handler: - pop_eh - todo(0,'get_bool on SockAddr does not work TT#1822') -done: +.end + +.sub test_string + $P0 = new 'Socket' + $P1 = $P0."sockaddr"("localhost", 1234) + is($P1,"127.0.0.1:1234","sockaddr stringification") .end # Local Variables: diff --git a/t/pmc/socket.t b/t/pmc/socket.t index 180fc2eddd..704fd0fe0e 100644 --- a/t/pmc/socket.t +++ b/t/pmc/socket.t @@ -13,6 +13,9 @@ t/pmc/socket.t - test the Socket PMC Tests the Socket PMC. +The IPv6-related tests in this file do not actually require an IPv6 networking +stack, so we don't need to check if this parrot is IPv6-aware. + =cut .include 'socket.pasm' diff --git a/t/pmc/socket_ipv6.t b/t/pmc/socket_ipv6.t new file mode 100644 index 0000000000..f8358fa548 --- /dev/null +++ b/t/pmc/socket_ipv6.t @@ -0,0 +1,176 @@ +#!./parrot +# Copyright (C) 2010, Parrot Foundation. + +=head1 NAME + +t/pmc/socket_ipv6.t - tests for the Socket PMC that require IPv6 + +=head1 SYNOPSIS + + % prove t/pmc/socket_ipv6.t + +=head1 DESCRIPTION + +IPv6-related tests for the Socket PMC. + +=cut + +.include 'socket.pasm' +.include 'iglobals.pasm' +.include 'errors.pasm' + + + +.sub main :main + .include 'test_more.pir' + + plan(18) + + check_for_ipv6() + + test_tcp_socket6() + test_raw_tcp_socket6() + test_udp_socket6() + test_raw_udp_socket6() + + test_bind() + + test_server() +.end + +.sub test_bind + .local pmc sock, addrinfo + .local string str + .local int result + + sock = new 'Socket' + addrinfo = sock.'getaddrinfo'('localhost', 1234, .PIO_PROTO_TCP, .PIO_PF_INET6, 1) + result = sock.'bind'(addrinfo) + is(result, 0, 'bind ok (IPv6 localhost)') + + str = sock.'local_address'() + is(str, "::1:1234", "local address of bound socket is ::1") + + sock.'close'() + + addrinfo = sock.'getaddrinfo'('localhost', 1234, .PIO_PROTO_TCP, .PIO_PF_INET, 1) + result = sock.'bind'(addrinfo) + is(result, 0, 'bind ok (IPv4 localhost)') + + str = sock.'local_address'() + is(str, "127.0.0.1:1234", "local address of bound socket is 127.0.0.1") + sock.'close'() +.end + +.sub test_server + .local pmc interp, conf, server, sock, address, result + .local string command, str + .local int status + + interp = getinterp + conf = interp[.IGLOBALS_CONFIG_HASH] + + run_tests: + command = '"' + str = conf['build_dir'] + command .= str + str = conf['slash'] + command .= str + command .= 'parrot' + str = conf['exe'] + command .= str + command .= '" t/pmc/testlib/test_server_ipv6.pir' + + server = new 'FileHandle' + server.'open'(command, 'rp') + str = server.'readline'() + is(str, "Server started\n", 'Server process started') + + sock = new 'Socket' + address = sock.'getaddrinfo'('localhost', 1234, .PIO_PROTO_TCP, .PIO_PF_INET6, 0) + status = sock.'connect'(address) + nok(status, 'connect') + + str = server.'readline'() + is(str, "Connection from ::1:1234\n", 'Server got a connection from ::1:1234') + + status = sock.'send'('test message') + is(status, '12', 'send') + str = sock.'recv'() + is(str, 'test message', 'recv') + sock.'close'() + + server.'close'() + status = server.'exit_status'() + nok(status, 'Exit status of server process') +.end + +.sub check_for_ipv6 + $P0 = getinterp + $P1 = $P0[.IGLOBALS_CONFIG_HASH] + + $P2 = $P1['HAS_IPV6'] + $I1 = isnull $P2 + if $I1, no_ipv6 + say '# This Parrot is IPv6-aware' + goto done + + no_ipv6: + diag( 'No IPv6' ) + skip(4) + exit 0 + done: +.end + +.sub test_tcp_socket6 + .local pmc sock, sockaddr + sock = new 'Socket' + + sock.'socket'(.PIO_PF_INET6, .PIO_SOCK_STREAM, .PIO_PROTO_TCP) + sockaddr = sock."sockaddr"("localhost",80, .PIO_PF_INET6) + isa_ok(sockaddr,'Sockaddr',"A TCP ipv6 sockaddr to localhost was set") + + sockaddr = sock."sockaddr"("::1",80, .PIO_PF_INET6) + isa_ok(sockaddr,'Sockaddr',"A TCP ipv6 sockaddr to ::1 was set") +.end + +.sub test_raw_tcp_socket6 + .local pmc sock, sockaddr + sock = new 'Socket' + + sock.'socket'(.PIO_PF_INET6, .PIO_SOCK_RAW, .PIO_PROTO_TCP) + sockaddr = sock."sockaddr"("localhost",80,.PIO_PF_INET6) + isa_ok(sockaddr,'Sockaddr',"A raw TCP ipv6 sockaddr to localhost was set:") + sockaddr = sock."sockaddr"("::1",80,.PIO_PF_INET6) + isa_ok(sockaddr,'Sockaddr',"A raw TCP ipv6 sockaddr to ::1 was set:") +.end + +.sub test_udp_socket6 + .local pmc sock, sockaddr + sock = new 'Socket' + + sock.'socket'(.PIO_PF_INET6, .PIO_SOCK_STREAM, .PIO_PROTO_UDP) + sockaddr = sock."sockaddr"("localhost",80,.PIO_PF_INET6) + isa_ok(sockaddr,'Sockaddr', "A UDP ipv6 sockaddr to localhost was set:") + + sockaddr = sock."sockaddr"("::1",80,.PIO_PF_INET6) + isa_ok(sockaddr,'Sockaddr', "A UDP ipv6 sockaddr to ::1 was set:") +.end + +.sub test_raw_udp_socket6 + .local pmc sock, sockaddr + sock = new 'Socket' + + sock.'socket'(.PIO_PF_INET6, .PIO_SOCK_RAW, .PIO_PROTO_UDP) + sockaddr = sock."sockaddr"("localhost",80,.PIO_PF_INET6) + isa_ok(sockaddr,'Sockaddr', "A raw UDP ipv6 sockaddr to localhost was set: ") + sockaddr = sock."sockaddr"("::1",80,.PIO_PF_INET6) + isa_ok(sockaddr,'Sockaddr', "A raw UDP ipv6 sockaddr to ::1 was set: ") +.end + + +# Local Variables: +# mode: pir +# fill-column: 100 +# End: +# vim: expandtab shiftwidth=4 ft=pir: diff --git a/t/pmc/testlib/test_server_ipv6.pir b/t/pmc/testlib/test_server_ipv6.pir new file mode 100644 index 0000000000..b13ec7fb08 --- /dev/null +++ b/t/pmc/testlib/test_server_ipv6.pir @@ -0,0 +1,67 @@ +#!./parrot +# Copyright (C) 2011, Parrot Foundation. + +=head1 NAME + +t/pmc/testlib/test_server_ipv6.pir - Test server for the Socket PMC (IPv6 version) + +=head1 DESCRIPTION + +This server process is launched from t/pmc/socket.t to test the Socket PMC. + +It listens on localhost:1234 and accepts only one connection. It echoes +everything it reads from that connection back to the client. + +Upon successful startup the string "Server started" is printed to stdout. + +After a timeout of 3 seconds, the process exits so it doesn't wait forever +in case of test failures. + +=cut + +.include 'socket.pasm' + +.sub main :main + .local pmc sock, address, conn + .local string str + .local int len, status + + sock = new 'Socket' + address = sock.'getaddrinfo'('localhost', 1234, .PIO_PROTO_TCP, .PIO_PF_INET6, 1) + status = sock.'bind'(address) + sock.'listen'(5) + + say 'Server started' + + status = sock.'poll'(1, 3, 0) + # timeout + if status == 0 goto conn_done + conn = sock.'accept'() + str = conn.'local_address'() + + print 'Connection from ' + say str + + # echo incoming data + recv_loop: + status = conn.'poll'(1, 3, 0) + # timeout + if status == 0 goto recv_done + str = conn.'recv'() + len = length str + if len == 0 goto recv_done + conn.'send'(str) + goto recv_loop + + recv_done: + conn.'close'() + conn_done: + sock.'close'() +.end + +# Local Variables: +# mode: pir +# fill-column: 100 +# End: +# vim: expandtab shiftwidth=4 ft=pir: +