diff options
Diffstat (limited to 'generic/tclIOSock.c')
| -rw-r--r-- | generic/tclIOSock.c | 182 | 
1 files changed, 173 insertions, 9 deletions
| diff --git a/generic/tclIOSock.c b/generic/tclIOSock.c index 6887b0c..694501f 100644 --- a/generic/tclIOSock.c +++ b/generic/tclIOSock.c @@ -7,11 +7,15 @@   *   * See the file "license.terms" for information on usage and redistribution of   * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclIOSock.c,v 1.11 2007/02/20 23:24:04 nijtmans Exp $   */  #include "tclInt.h" + +#if defined(_WIN32) && defined(UNICODE) +/* On Windows, we always need the ASCII version. */ +#   undef gai_strerror +#   define gai_strerror gai_strerrorA +#endif  /*   *--------------------------------------------------------------------------- @@ -60,8 +64,8 @@ TclSockGetPort(  	return TCL_ERROR;      }      if (*portPtr > 0xFFFF) { -	Tcl_AppendResult(interp, "couldn't open socket: port number too high", -		NULL); +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"couldn't open socket: port number too high", -1));  	return TCL_ERROR;      }      return TCL_OK; @@ -83,30 +87,190 @@ TclSockGetPort(   *----------------------------------------------------------------------   */ +#if !defined(_WIN32) && !defined(__CYGWIN__) +#   define SOCKET int +#endif +  int  TclSockMinimumBuffers( -    int sock,			/* Socket file descriptor */ +    void *sock,			/* Socket file descriptor */      int size)			/* Minimum buffer size */  {      int current;      socklen_t len;      len = sizeof(int); -    getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)¤t, &len); +    getsockopt((SOCKET)(size_t) sock, SOL_SOCKET, SO_SNDBUF, +	    (char *) ¤t, &len);      if (current < size) {  	len = sizeof(int); -	setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *)&size, len); +	setsockopt((SOCKET)(size_t) sock, SOL_SOCKET, SO_SNDBUF, +		(char *) &size, len);      }      len = sizeof(int); -    getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)¤t, &len); +    getsockopt((SOCKET)(size_t) sock, SOL_SOCKET, SO_RCVBUF, +		(char *) ¤t, &len);      if (current < size) {  	len = sizeof(int); -	setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *)&size, len); +	setsockopt((SOCKET)(size_t) sock, SOL_SOCKET, SO_RCVBUF, +		(char *) &size, len);      }      return TCL_OK;  }  /* + *---------------------------------------------------------------------- + * + * TclCreateSocketAddress -- + * + *	This function initializes a sockaddr structure for a host and port. + * + * Results: + *	1 if the host was valid, 0 if the host could not be converted to an IP + *	address. + * + * Side effects: + *	Fills in the *sockaddrPtr structure. + * + *---------------------------------------------------------------------- + */ + +int +TclCreateSocketAddress( +    Tcl_Interp *interp,                 /* Interpreter for querying +					 * the desired socket family */ +    struct addrinfo **addrlist,		/* Socket address list */ +    const char *host,			/* Host. NULL implies INADDR_ANY */ +    int port,				/* Port number */ +    int willBind,			/* Is this an address to bind() to or +					 * to connect() to? */ +    const char **errorMsgPtr)		/* Place to store the error message +					 * detail, if available. */ +{ +    struct addrinfo hints; +    struct addrinfo *p; +    struct addrinfo *v4head = NULL, *v4ptr = NULL; +    struct addrinfo *v6head = NULL, *v6ptr = NULL; +    char *native = NULL, portbuf[TCL_INTEGER_SPACE], *portstring; +    const char *family = NULL; +    Tcl_DString ds; +    int result, i; + +    if (host != NULL) { +	native = Tcl_UtfToExternalDString(NULL, host, -1, &ds); +    } + +    /* +     * Workaround for OSX's apparent inability to resolve "localhost", "0" +     * when the loopback device is the only available network interface. +     */ +    if (host != NULL && port == 0) { +        portstring = NULL; +    } else { +        TclFormatInt(portbuf, port); +        portstring = portbuf; +    } +     +    (void) memset(&hints, 0, sizeof(hints)); +    hints.ai_family = AF_UNSPEC; + +    /*  +     * Magic variable to enforce a certain address family - to be superseded +     * by a TIP that adds explicit switches to [socket] +     */ + +    if (interp != NULL) { +        family = Tcl_GetVar(interp, "::tcl::unsupported::socketAF", 0); +        if (family != NULL) { +            if (strcmp(family, "inet") == 0) { +                hints.ai_family = AF_INET; +            } else if (strcmp(family, "inet6") == 0) { +                hints.ai_family = AF_INET6; +            } +        } +    } + +    hints.ai_socktype = SOCK_STREAM; + +#if 0 +    /* +     * We found some problems when using AI_ADDRCONFIG, e.g. on systems that +     * have no networking besides the loopback interface and want to resolve +     * localhost. See [Bugs 3385024, 3382419, 3382431]. As the advantage of +     * using AI_ADDRCONFIG in situations where it works, is probably low, +     * we'll leave it out for now. After all, it is just an optimisation. +     * +     * Missing on: OpenBSD, NetBSD. +     * Causes failure when used on AIX 5.1 and HP-UX +     */ + +#if defined(AI_ADDRCONFIG) && !defined(_AIX) && !defined(__hpux) +    hints.ai_flags |= AI_ADDRCONFIG; +#endif /* AI_ADDRCONFIG && !_AIX && !__hpux */ +#endif /* 0 */ + +    if (willBind) { +	hints.ai_flags |= AI_PASSIVE; +    }  + +    result = getaddrinfo(native, portstring, &hints, addrlist); + +    if (host != NULL) { +	Tcl_DStringFree(&ds); +    } + +    if (result != 0) { +	*errorMsgPtr = +#ifdef EAI_SYSTEM	/* Doesn't exist on Windows */ +		(result == EAI_SYSTEM) ? Tcl_PosixError(interp) : +#endif /* EAI_SYSTEM */ +		gai_strerror(result); +        return 0; +    } + +    /* +     * Put IPv4 addresses before IPv6 addresses to maximize backwards +     * compatibility of [fconfigure -sockname] output. +     * +     * There might be more elegant/efficient ways to do this. +     */ +    if (willBind) { +	for (p = *addrlist; p != NULL; p = p->ai_next) { +	    if (p->ai_family == AF_INET) { +		if (v4head == NULL) { +		    v4head = p; +		} else { +		    v4ptr->ai_next = p; +		} +		v4ptr = p; +	    } else { +		if (v6head == NULL) { +		    v6head = p; +		} else { +		    v6ptr->ai_next = p; +		} +		v6ptr = p; +	    } +	} +	*addrlist = NULL; +	if (v6head != NULL) { +	    *addrlist = v6head; +	    v6ptr->ai_next = NULL; +	} +	if (v4head != NULL) { +	    v4ptr->ai_next = *addrlist; +	    *addrlist = v4head; +	} +    } +    i = 0; +    for (p = *addrlist; p != NULL; p = p->ai_next) { +	i++; +    } +     +    return 1; +} + +/*   * Local Variables:   * mode: c   * c-basic-offset: 4 | 
