From 688231c304b1ee922bf1d693ad9e1fd33ebff52e Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Wed, 22 Sep 2010 17:41:58 +0200 Subject: [PATCH] Implement support for IPv6 connect() and bind() use an Addrinfo PMC (contains multiple socket addresses in the right order [RFC 3484]) instead of a Sockaddr PMC. It is no longer necessary to create the socket before calling bind() or connect() as both need to create sockets with the appropriate address family on their own. Also, the Sockaddr PMC internally uses a struct sockaddr_storage in combination with a length field instead of an struct sockaddr_in. This commit also implements the local_address and remote_address methods for the Socket PMC. The Sockaddr PMC can now be converted to string. --- MANIFEST | 1 + MANIFEST.generated | 2 + include/parrot/io_unix.h | 44 ++++++- src/io/socket_unix.c | 292 ++++++++++++++++++++++++++++++++++++++++------ src/pmc/addrinfo.pmc | 140 ++++++++++++++++++++++ src/pmc/sockaddr.pmc | 54 +++++++-- src/pmc/socket.pmc | 46 +++++++- 7 files changed, 525 insertions(+), 54 deletions(-) create mode 100644 src/pmc/addrinfo.pmc diff --git a/MANIFEST b/MANIFEST index c5a941b..90f0095 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1347,6 +1347,7 @@ src/pbc_disassemble.c [] src/pbc_dump.c [] src/pbc_merge.c [] src/pmc.c [] +src/pmc/addrinfo.pmc [] src/pmc/addrregistry.pmc [] src/pmc/arrayiterator.pmc [] src/pmc/bigint.pmc [] diff --git a/MANIFEST.generated b/MANIFEST.generated index f6fadae..365d0f4 100644 --- a/MANIFEST.generated +++ b/MANIFEST.generated @@ -37,6 +37,7 @@ include/parrot/pbcversion.h [devel]include include/parrot/platform.h [main]include include/parrot/platform_limits.h [devel]include include/parrot/vtable.h [main]include +include/pmc/pmc_addrinfo.h [devel]include include/pmc/pmc_addrregistry.h [devel]include include/pmc/pmc_arrayiterator.h [devel]include include/pmc/pmc_bigint.h [devel]include @@ -333,6 +334,7 @@ src/jit_emit.h [] src/nci.c [] src/null_config.c [] src/parrot_config.c [] +src/pmc/addrinfo.dump [devel]src src/pmc/addrregistry.dump [devel]src src/pmc/arrayiterator.dump [devel]src src/pmc/bigint.dump [devel]src diff --git a/include/parrot/io_unix.h b/include/parrot/io_unix.h index 7cc8ee2..b97e288 100644 --- a/include/parrot/io_unix.h +++ b/include/parrot/io_unix.h @@ -182,26 +182,43 @@ PMC * Parrot_io_accept_unix(PARROT_INTERP, ARGMOD(PMC *socket)) FUNC_MODIFIES(*socket); INTVAL Parrot_io_bind_unix(PARROT_INTERP, - ARGMOD(PMC *socket), + ARGMOD(PMC *sock), ARGMOD(PMC *sockaddr)) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3) - FUNC_MODIFIES(*socket) + FUNC_MODIFIES(*sock) FUNC_MODIFIES(*sockaddr); INTVAL Parrot_io_connect_unix(PARROT_INTERP, - ARGMOD(PMC *socket), + ARGMOD(PMC *sock), ARGIN(PMC *r)) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3) - FUNC_MODIFIES(*socket); + FUNC_MODIFIES(*sock); + +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) + __attribute__nonnull__(1) + __attribute__nonnull__(2); INTVAL Parrot_io_listen_unix(SHIM_INTERP, ARGMOD(PMC *socket), INTVAL sec) __attribute__nonnull__(2) FUNC_MODIFIES(*socket); +PARROT_WARN_UNUSED_RESULT +PARROT_CANNOT_RETURN_NULL +PMC * Parrot_io_local_address(PARROT_INTERP, ARGIN(PMC *sock)) + __attribute__nonnull__(1) + __attribute__nonnull__(2); + INTVAL Parrot_io_poll_unix(SHIM_INTERP, ARGMOD(PMC *socket), int which, @@ -219,6 +236,12 @@ INTVAL Parrot_io_recv_unix(PARROT_INTERP, FUNC_MODIFIES(*socket) FUNC_MODIFIES(*s); +PARROT_WARN_UNUSED_RESULT +PARROT_CANNOT_RETURN_NULL +PMC * Parrot_io_remote_address(PARROT_INTERP, ARGIN(PMC *sock)) + __attribute__nonnull__(1) + __attribute__nonnull__(2); + INTVAL Parrot_io_send_unix(SHIM_INTERP, ARGMOD(PMC *socket), ARGMOD(STRING *s)) @@ -248,20 +271,29 @@ INTVAL Parrot_io_socket_unix(PARROT_INTERP, , PARROT_ASSERT_ARG(socket)) #define ASSERT_ARGS_Parrot_io_bind_unix __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(interp) \ - , PARROT_ASSERT_ARG(socket) \ + , PARROT_ASSERT_ARG(sock) \ , PARROT_ASSERT_ARG(sockaddr)) #define ASSERT_ARGS_Parrot_io_connect_unix __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(interp) \ - , PARROT_ASSERT_ARG(socket) \ + , PARROT_ASSERT_ARG(sock) \ , PARROT_ASSERT_ARG(r)) +#define ASSERT_ARGS_Parrot_io_getaddrinfo __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ + PARROT_ASSERT_ARG(interp) \ + , PARROT_ASSERT_ARG(addr)) #define ASSERT_ARGS_Parrot_io_listen_unix __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(socket)) +#define ASSERT_ARGS_Parrot_io_local_address __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ + PARROT_ASSERT_ARG(interp) \ + , PARROT_ASSERT_ARG(sock)) #define ASSERT_ARGS_Parrot_io_poll_unix __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(socket)) #define ASSERT_ARGS_Parrot_io_recv_unix __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(interp) \ , PARROT_ASSERT_ARG(socket) \ , PARROT_ASSERT_ARG(s)) +#define ASSERT_ARGS_Parrot_io_remote_address __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ + PARROT_ASSERT_ARG(interp) \ + , PARROT_ASSERT_ARG(sock)) #define ASSERT_ARGS_Parrot_io_send_unix __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(socket) \ , PARROT_ASSERT_ARG(s)) diff --git a/src/io/socket_unix.c b/src/io/socket_unix.c index 9dcb8f9..cc70398 100644 --- a/src/io/socket_unix.c +++ b/src/io/socket_unix.c @@ -31,6 +31,7 @@ APitUE - W. Richard Stevens, AT&T SFIO, Perl 5 (Nick Ing-Simmons) #include "parrot/parrot.h" #include "io_private.h" #include "pmc/pmc_socket.h" +#include "pmc/pmc_sockaddr.h" #ifdef PIO_OS_UNIX @@ -41,6 +42,17 @@ APitUE - W. Richard Stevens, AT&T SFIO, Perl 5 (Nick Ing-Simmons) /* 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); + static void get_sockaddr_in(PARROT_INTERP, ARGIN(PMC * sockaddr), ARGIN(const char* host), @@ -49,6 +61,10 @@ static void get_sockaddr_in(PARROT_INTERP, __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)) #define ASSERT_ARGS_get_sockaddr_in __attribute__unused__ int _ASSERT_ARGS_CHECK = (\ PARROT_ASSERT_ARG(interp) \ , PARROT_ASSERT_ARG(sockaddr) \ @@ -59,6 +75,8 @@ static void get_sockaddr_in(PARROT_INTERP, /* static void get_sockaddr_in(PARROT_INTERP, ARGIN(PMC * sockaddr), ARGIN(const char* host), ARGIN(int port)); +static void +get_addrinfo(PARROT_INTERP, ARGIN(PMC * addrinfo), ARGIN(const char *host), ARGIN(int port)); */ /* @@ -107,6 +125,111 @@ Parrot_io_sockaddr_in(PARROT_INTERP, ARGIN(STRING *addr), INTVAL port) return sockaddr; } +/* + +=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) +{ + ASSERT_ARGS(Parrot_io_getaddrinfo) + + char * const s = Parrot_str_to_cstring(interp, addr); + PMC * const addrinfo = Parrot_pmc_new(interp, enum_class_Addrinfo); + + /* 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 + +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)) +{ + ASSERT_ARGS(Parrot_io_remote_address) + + 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)) +{ + ASSERT_ARGS(Parrot_io_local_address) + + PMC * const addrinfo = VTABLE_clone(interp, PARROT_SOCKET(sock)->local); + + return addrinfo; +} + /* @@ -139,73 +262,135 @@ Parrot_io_socket_unix(PARROT_INTERP, ARGIN(PMC *s), int fam, int type, int proto /* -=item C +=item C -Connects C<*io>'s socket to address C<*r>. +Takes the Addrinfo PMC C<*r> and tries to establish a connection. A new socket +PMC will be created because the Addrinfo may contain addresses of multiple +families (IPv4 and IPv6). =cut */ INTVAL -Parrot_io_connect_unix(PARROT_INTERP, ARGMOD(PMC *socket), ARGIN(PMC *r)) +Parrot_io_connect_unix(PARROT_INTERP, ARGMOD(PMC *sock), ARGIN(PMC *r)) { ASSERT_ARGS(Parrot_io_connect_unix) - const Parrot_Socket_attributes * const io = PARROT_SOCKET(socket); + const Parrot_Socket_attributes * const io = PARROT_SOCKET(sock); + struct addrinfo *res = VTABLE_get_pointer(interp, r); + struct addrinfo *walk; + int fd = -1; + int i = 1; if (!r) return -1; - PARROT_SOCKET(socket)->remote = r; + + 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? */ + Parrot_io_set_os_handle(interp, sock, fd); AGAIN: - if ((connect(io->os_handle, (struct sockaddr *)SOCKADDR_REMOTE(socket), - sizeof (struct sockaddr_in))) != 0) { - switch (errno) { - case EINTR: - goto AGAIN; - case EINPROGRESS: - goto AGAIN; - case EISCONN: - return 0; - default: - return -1; + 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(sock)->remote = Parrot_pmc_new(interp, enum_class_Sockaddr); + + VTABLE_set_pointer(interp, PARROT_SOCKET(sock)->remote, walk); + return 0; } - return 0; + if (fd == -1) + return -1; } /* -=item C +=item C -Binds C<*io>'s socket to the local address and port specified by C<*l>. +Takes the Addrinfo PMC C<*sockaddr> and creates a listening socket. A new +socket needs to be created because C<*sockaddr> may contain addresses of +multiple families (IPv4 and IPv6). An example is binding to "localhost" +which resolves to ::1 and 127.0.0.1. If you are on FreeBSD and have no +IPv6 support, the first attempt to create a socket and bind it would fail. =cut */ INTVAL -Parrot_io_bind_unix(PARROT_INTERP, ARGMOD(PMC *socket), ARGMOD(PMC *sockaddr)) +Parrot_io_bind_unix(PARROT_INTERP, ARGMOD(PMC *sock), ARGMOD(PMC *sockaddr)) { ASSERT_ARGS(Parrot_io_bind_unix) - const Parrot_Socket_attributes * const io = PARROT_SOCKET(socket); - struct sockaddr_in * saddr; + const Parrot_Socket_attributes * const io = PARROT_SOCKET(sock); + struct addrinfo *res = VTABLE_get_pointer(interp, sockaddr); + struct addrinfo *walk; + int fd = -1; + int i = 1; if (!sockaddr) return -1; - PARROT_SOCKET(socket)->local = sockaddr; + 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; + } - saddr = SOCKADDR_LOCAL(socket); + if (walk->ai_family == AF_INET6) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &i, sizeof(i)) == -1) { + perror("Error setting IPV6_V6ONLY:"); + continue; + } + } - if ((bind(io->os_handle, (struct sockaddr *) saddr, - sizeof (struct sockaddr_in))) == -1) { - return -1; + /* XXX: this effectively overwrites any previously set sockets. is that alright? */ + Parrot_io_set_os_handle(interp, sock, fd); + + if (bind(fd, walk->ai_addr, walk->ai_addrlen) != 0) { + close(fd); + fd = -1; + continue; + } + + PARROT_SOCKET(sock)->local = Parrot_pmc_new(interp, enum_class_Sockaddr); + + VTABLE_set_pointer(interp, PARROT_SOCKET(sock)->local, walk); + return 0; } - return 0; + if (fd == -1) + return -1; } /* @@ -249,8 +434,9 @@ Parrot_io_accept_unix(PARROT_INTERP, ARGMOD(PMC *socket)) Parrot_Socket_attributes * io = PARROT_SOCKET(socket); PMC * newio = Parrot_io_new_socket_pmc(interp, PIO_F_SOCKET | PIO_F_READ|PIO_F_WRITE); - Parrot_Socklen_t addrlen = sizeof (struct sockaddr_in); - struct sockaddr_in *saddr; + Parrot_Socklen_t addrlen = sizeof (struct sockaddr_storage); + Parrot_Sockaddr_attributes *remotedata; + struct sockaddr_storage *saddr; int newsock; PARROT_SOCKET(newio)->local = PARROT_SOCKET(socket)->local; @@ -263,13 +449,22 @@ Parrot_io_accept_unix(PARROT_INTERP, ARGMOD(PMC *socket)) return PMCNULL; } - PARROT_SOCKET(newio)->os_handle = newsock; + /* Set the length for the remote sockaddr PMC so that it can distinguish + * between sockaddr_in and sockaddr_in6 */ + remotedata = PARROT_SOCKADDR(PARROT_SOCKET(newio)->remote); + remotedata->len = addrlen; - /* XXX FIXME: Need to do a getsockname and getpeername here to - * fill in the sockaddr_in structs for local and peer */ + PARROT_SOCKET(newio)->os_handle = newsock; - /* Optionally do a gethostyaddr() to resolve remote IP address. - * This should be based on an option set in the master socket */ + /* Optionally do a getaddrinfo() to resolve remote IP address. + * This should be based on an option set in the master socket. + * + * XXX: instead of resolving here, we should use the flags for + * getnameinfo() in the VTABLE get_string of the Sockaddr PMC. + * At the moment, it uses NI_NUMERICHOST, but when called + * differently, it will resolve the address. The advantage is + * that we only resolve when someone actually accesses the + * name. -- Michael Stapelberg */ return newio; } @@ -480,6 +675,33 @@ get_sockaddr_in(PARROT_INTERP, ARGIN(PMC * sockaddr), ARGIN(const char* host), sa->sin_port = htons(port); } +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); +} #endif /* PIO_OS_UNIX */ diff --git a/src/pmc/addrinfo.pmc b/src/pmc/addrinfo.pmc new file mode 100644 index 0000000..4618fc0 --- /dev/null +++ b/src/pmc/addrinfo.pmc @@ -0,0 +1,140 @@ +/* +Copyright (C) 2008-2009, Parrot Foundation. +$Id$ + +=head1 NAME + +src/pmc/addrinfo.pmc - addrinfo holder + +=head1 DESCRIPTION + +The Addrinfo PMC holds a C pointer to a C. + + +=head2 Vtable Functions + +These are the vtable functions for the Addrinfo class. + +=over 4 + +=cut + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + struct addrinfo; +#ifdef __cplusplus +} +#endif + +/* HEADERIZER HFILE: none */ +/* HEADERIZER BEGIN: static */ +/* HEADERIZER END: static */ + +pmclass Addrinfo auto_attrs { + ATTR void *pointer; /* The stored pointer. */ + +/* + +=item C + +Initializes the pointer object. + +=cut + +*/ + + VTABLE void init() { + Parrot_Addrinfo_attributes * const pdata_struct = + (Parrot_Addrinfo_attributes *) PMC_data(SELF); + + pdata_struct->pointer = NULL; + PObj_custom_destroy_SET(SELF); + } + +/* + +=item C + +Destroys the PMC and frees all allocated memory. + +=cut + +*/ + + VTABLE void destroy() { + Parrot_Addrinfo_attributes * const data = PARROT_ADDRINFO(SELF); + + if (data) { + if (data->pointer) + freeaddrinfo(data->pointer); + data->pointer = NULL; + } + } + +/* + +=item C + +Creates and returns a clone of the pointer. + +=cut + +*/ + + VTABLE PMC *clone() { + PMC * const dest = Parrot_pmc_new(INTERP, SELF->vtable->base_type); + memcpy(PARROT_ADDRINFO(dest)->pointer, PARROT_ADDRINFO(SELF)->pointer, + sizeof (struct sockaddr_storage)); + return dest; + } + +/* + +=item C + +Returns the pointer. + +=cut + +*/ + + VTABLE void *get_pointer() { + Parrot_Addrinfo_attributes * const data = PARROT_ADDRINFO(SELF); + return data->pointer; + } + +/* + +=item C + +Sets the pointer. + +=cut + +*/ + + VTABLE void set_pointer(void *value) { + Parrot_Addrinfo_attributes * const data = PARROT_ADDRINFO(SELF); + data->pointer = value; + } + +} + +/* + +=back + +=cut + +*/ + +/* + * Local variables: + * c-file-style: "parrot" + * End: + * vim: expandtab shiftwidth=4: + */ + diff --git a/src/pmc/sockaddr.pmc b/src/pmc/sockaddr.pmc index 6161557..dc1117d 100644 --- a/src/pmc/sockaddr.pmc +++ b/src/pmc/sockaddr.pmc @@ -4,11 +4,12 @@ $Id$ =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 @@ -24,7 +25,7 @@ These are the vtable functions for the Sockaddr class. #ifdef __cplusplus extern "C" { #endif - struct sockaddr_in; + struct sockaddr_storage; #ifdef __cplusplus } #endif @@ -35,12 +36,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 @@ -51,7 +53,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); } @@ -78,7 +80,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 @@ -86,8 +89,10 @@ 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; } @@ -95,7 +100,7 @@ Creates and returns a clone of the pointer. =item C -Returns the pointer. +Returns a pointer to the C or C. =cut @@ -108,20 +113,45 @@ 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); + if (!data->pointer) + return Parrot_sprintf_c(interp, "(?)"); + + struct sockaddr_storage *addr = data->pointer; + /* TODO: get hostname, not only numeric */ + char buf[INET6_ADDRSTRLEN+1]; + getnameinfo((struct sockaddr_storage*)data->pointer, data->len, buf, + sizeof(buf), NULL, 0, NI_NUMERICHOST); + return Parrot_str_new(interp, buf, 0); + } + /* + +=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 1c8971a..cd3a61e 100644 --- a/src/pmc/socket.pmc +++ b/src/pmc/socket.pmc @@ -177,6 +177,50 @@ from a port number (integer) and an address (string). /* +=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); + } + +/* + =item C Connects a socket object to an address. @@ -265,7 +309,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 -- 1.6.5