From: Jarkko Hietaniemi Date: Sat, 8 Sep 2001 18:50:07 +0000 (+0000) Subject: Still more Socket portability tweaking. X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=21bbcf2048880336da62f6a373941a2b966fda79;p=p5sagit%2Fp5-mst-13.2.git Still more Socket portability tweaking. Now Socket.t seems to be happy in all of Linux/x86, Tru64/alpha, Solaris/sparc, IRIX/MIPS (both 32-bit and 64-bit), and UNICOS/mk/alpha. Addendum: it seems that io_multihomed, io_sock, and hostent were broken in little-endians (linux/x86 and tru64/alpha) because of this change. Argh. p4raw-id: //depot/perl@11948 --- diff --git a/ext/Socket/Socket.xs b/ext/Socket/Socket.xs index bc6711b..938d8ae 100644 --- a/ext/Socket/Socket.xs +++ b/ext/Socket/Socket.xs @@ -181,6 +181,36 @@ not_here(char *s) #include "constants.c" +#define PERL_IN_ADDR_S_ADDR_SIZE 4 + +/* + * Bad assumptions possible here. + * + * Bad Assumption 1: struct in_addr has no other fields + * than the s_addr (which is the field we care about + * in here, really). However, we can be fed either 4-byte + * addresses (from pack("N", ...), or va.b.c.d, or ...), + * or full struct in_addrs (from e.g. pack_sockaddr_in()), + * which may or may not be 4 bytes in size. + * + * Bad Assumption 2: the s_addr field is a simple type + * (such as an int, u_int32_t). It can be a bit field, + * in which case using & (address-of) on it or taking sizeof() + * wouldn't go over too well. (Those are not attempted + * now but in case someone thinks to change the below code + * to use addr.s_addr instead of addr, you have been warned.) + * + * Bad Assumption 3: the s_addr is the first field in + * an in_addr, or that its bytes are the first bytes in + * an in_addr. + * + * These bad assumptions are wrong in UNICOS which has + * struct in_addr { struct { u_long st_addr:32; } s_da }; + * #define s_addr s_da.st_addr + * and u_long is 64 bits. + * + * --jhi */ + MODULE = Socket PACKAGE = Socket INCLUDE: constants.xs @@ -190,18 +220,26 @@ inet_aton(host) char * host CODE: { - struct in_addr ip_address; + struct in_addr addr; + char saddr[PERL_IN_ADDR_S_ADDR_SIZE]; struct hostent * phe; - int ok = inet_aton(host, &ip_address); + int ok; - if (!ok && (phe = gethostbyname(host))) { - Copy( phe->h_addr, &ip_address, phe->h_length, char ); + ok = inet_aton(host, &addr); + if (ok) { + saddr[0] = (addr.s_addr >> 24) & 0xFF; + saddr[1] = (addr.s_addr >> 16) & 0xFF; + saddr[2] = (addr.s_addr >> 8) & 0xFF; + saddr[3] = (addr.s_addr ) & 0xFF; + } + else if (phe = gethostbyname(host)) { + Copy( phe->h_addr, saddr, phe->h_length, char ); ok = 1; } ST(0) = sv_newmortal(); if (ok) { - sv_setpvn( ST(0), (char *)&ip_address, sizeof ip_address ); + sv_setpvn( ST(0), saddr, PERL_IN_ADDR_S_ADDR_SIZE); } } @@ -217,34 +255,6 @@ inet_ntoa(ip_address_sv) if (DO_UTF8(ip_address_sv) && !sv_utf8_downgrade(ip_address_sv, 1)) croak("Wide character in Socket::inet_ntoa"); ip_address = SvPV(ip_address_sv, addrlen); - /* - * Bad assumptions possible here. - * - * Bad Assumption 1: struct in_addr has no other fields - * than the s_addr (which is the field we care about - * in here, really). However, we can be fed either 4-byte - * addresses (from pack("N", ...), or va.b.c.d, or ...), - * or full struct in_addrs (from e.g. pack_sockaddr_in()), - * which may or may not be 4 bytes in size. - * - * Bad Assumption 2: the s_addr field is a simple type - * (such as an int, u_int32_t). It can be a bit field, - * in which case using & (address-of) on it or taking sizeof() - * wouldn't go over too well. (Those are not attempted - * now but in case someone thinks to change the below code - * to use addr.s_addr instead of addr, you have been warned.) - * - * Bad Assumption 3: the s_addr is the first field in - * an in_addr, or that its bytes are the first bytes in - * an in_addr. - * - * These bad assumptions are wrong in UNICOS which has - * struct in_addr { struct { u_long st_addr:32; } s_da }; - * #define s_addr s_da.st_addr - * and u_long is 64 bits. - * - * --jhi */ -#define PERL_IN_ADDR_S_ADDR_SIZE 4 if (addrlen == PERL_IN_ADDR_S_ADDR_SIZE) { /* It must be (better be) a network-order 32-bit integer. * We can't use any fancy casting of ip_address to pointer ofn @@ -255,25 +265,25 @@ inet_ntoa(ip_address_sv) addr.s_addr |= (ip_address[2] & 0xFF) << 8; addr.s_addr |= (ip_address[3] & 0xFF); } - else { - /* It could be a struct in_addr that is not 32 bits. */ - - if (addrlen == sizeof(addr)) - /* This branch could be optimized away if we knew - * during compile time what size is struct in_addr. - * If it's four, the above code should have worked. */ - Copy( ip_address, &addr, sizeof addr, char ); - else { - if (PERL_IN_ADDR_S_ADDR_SIZE == sizeof(addr)) - croak("Bad arg length for %s, length is %d, should be %d", - "Socket::inet_ntoa", - addrlen, PERL_IN_ADDR_S_ADDR_SIZE); - else - croak("Bad arg length for %s, length is %d, should be %d or %d", - "Socket::inet_ntoa", - addrlen, PERL_IN_ADDR_S_ADDR_SIZE, sizeof(addr)); - } - } + else { + /* It could be a struct in_addr that is not 32 bits. */ + + if (addrlen == sizeof(addr)) + /* This branch could be optimized away if we knew + * during compile time what size is struct in_addr. + * If it's four, the above code should have worked. */ + Copy( ip_address, &addr, sizeof addr, char ); + else { + if (PERL_IN_ADDR_S_ADDR_SIZE == sizeof(addr)) + croak("Bad arg length for %s, length is %d, should be %d", + "Socket::inet_ntoa", + addrlen, PERL_IN_ADDR_S_ADDR_SIZE); + else + croak("Bad arg length for %s, length is %d, should be %d or %d", + "Socket::inet_ntoa", + addrlen, PERL_IN_ADDR_S_ADDR_SIZE, sizeof(addr)); + } + } /* We could use inet_ntoa() but that is broken * in HP-UX + GCC + 64bitint (returns "0.0.0.0"), * so let's use this sprintf() workaround everywhere. */ @@ -396,6 +406,7 @@ unpack_sockaddr_in(sin_sv) struct sockaddr_in addr; unsigned short port; struct in_addr ip_address; + char saddr[PERL_IN_ADDR_S_ADDR_SIZE]; char * sin = SvPV(sin_sv,sockaddrlen); if (sockaddrlen != sizeof(addr)) { croak("Bad arg length for %s, length is %d, should be %d", @@ -411,8 +422,12 @@ unpack_sockaddr_in(sin_sv) } port = ntohs(addr.sin_port); ip_address = addr.sin_addr; + saddr[0] = (ip_address.s_addr >> 24) & 0xFF; + saddr[1] = (ip_address.s_addr >> 16) & 0xFF; + saddr[2] = (ip_address.s_addr >> 8) & 0xFF; + saddr[3] = (ip_address.s_addr ) & 0xFF; EXTEND(SP, 2); PUSHs(sv_2mortal(newSViv((IV) port))); - PUSHs(sv_2mortal(newSVpvn((char *)&ip_address,sizeof ip_address))); + PUSHs(sv_2mortal(newSVpvn(saddr, PERL_IN_ADDR_S_ADDR_SIZE))); }