⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 getaddrinfo.c

📁 unix网络编程卷1:套接口API的全书源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Copyright (c) 1996 W. Richard Stevens.  All rights reserved.  * * Permission to use or modify this software for educational or * for commercial purposes, and without fee, is hereby granted, * provided that the above copyright notice appears in connection * with any and all uses, with clear indication as to any * modifications made.  The author RESERVES the sole rights of * reproduction, publication and distribution and hence permission * to print this source code in any book, reference manual, * magazine, or other type of publication, including any digital * medium, must be granted in writing by W. Richard Stevens. * * The author makes no representations about the suitability of this  * software for any purpose.  It is provided "as is" without express * or implied warranty.  *//* tabs set for 4 spaces, not 8 */#ifdef	__osf__#define	_SOCKADDR_LEN		/* required for this implementation */#define	INET6				/* required for this implementation */#endif#include	<sys/types.h>#include	<sys/socket.h>#include	<netinet/in.h>#include	<ctype.h>		/* isdigit() */#include	<netdb.h>		/* hostent{}, servent{}, etc. */#include	<arpa/inet.h>	/* inet_pton() */#include	<arpa/nameser.h>#include	<resolv.h>		/* DNS resolver */#include	<stdlib.h>		/* malloc() and calloc() */#include	<string.h>		/* strdup() and strncpy() */#include	<unistd.h>		/* unlink() */#include	<sys/un.h>		/* for Unix domain socket stuff */#ifndef	__osf__#include	"addrinfo.h"	/* defines in here really belong in <netdb.h> */#endif/* NOTE: this code assumes you have the inet_pton() function as defined * in the BIND-4.9.4 release or later (ftp://ftp.vix.com/pub/bind). * If you don't have this in your resolver library, in this tar file is * a copy of it, along with inet_ntop(). *//* We need a way to determine if the compiling host supports IPv4/IPv6. * Cannot test for AF_INET6, as some vendors #define it, even though * they don't support it. */#ifdef	AF_INET#define	IPV4	/* this is tested throughout the code that follows */#endif			/* no one constant that all implementations define, sigh */#if		defined(IPV6ADDR_ANY_INIT)#define	IPV6	/* this is tested throughout the code that follows */#elif	defined(IPV6_FLOWINFO_FLOWLABEL)#define	IPV6	/* this is tested throughout the code that follows */#endif/* There is no requirement that getaddrinfo() support Unix domain sockets, * but what the heck, it doesn't take too much code, and it's a real good * test whether an application is *really* protocol-independent. */#ifdef	AF_UNIX#define	LOCAL	/* this is tested throughout the code that follows */#ifndef	AF_LOCAL#define	AF_LOCAL	AF_UNIX		/* the Posix.1g correct term */#endif#ifndef	PF_LOCAL#define	PF_LOCAL	PF_UNIX		/* the Posix.1g correct term */#endif#endif	/* AF_UNIX *//* Define REENTRANT if the reentrant versions of get{host|serv}byname * are to be used. * Note that getaddrinfo{} *must* be reentrant if the underlying * get{host|serv}byname_r functions are provided. * This program has only been tested with the Solaris 2.x functions. * (I have no idea if other vendors have the same functions or not.) * * Long diatribe: Don't define REENTRANT.  At least not until you know * what your vendor's gethostbyname_r() function does with regard to * IPv4/IPv6 addresses.  If you really need a reentrant version of this * function, because you call it from different threads, then use a * mutex lock to protect the calls. * The problem at the time of this writing is the handling of IPv4/IPv6 * addresses.  BIND-4.9.4 does it the "right" way :-), but this won't * be documented until a later revision of the IPv6 BSD API spec.  Also * BIND-4.9.4 doesn't provide the reentrant _r() functions, and I have * no idea what the vendors like Sun have done with these functions. * The code far below that calls gethostbyname() sets the resolver * RES_USE_INET6 option if the caller specifies an ai_family of AF_INET6. * This causes 16-byte addresses to be returned, regardless, either * "true" IPv6 address from AAAA records, or IPv4-mapped IPv6 addresses * from A records.  If the caller specifies an ai_family of AF_INET6, * then we should return 16-byte addresses. * With BIND-4.9.4 the caller can also force the return of 16-byte addresses * by setting the environment variable RES_OPTIONS, as in *    % RES_OPTIONS=inet6 ./a.out arguments... * This way the caller need not pollute the code with code like * ai_family = AF_INET6, making the code protocol-dependent. *//* #define	REENTRANT *//* Define following function prototype if your headers don't define it */int	 inet_pton(int, const char *, void *);#define	HENTBUFSIZ	8*1024#define	HENTMAXADDR	    32	/* max binary address: 16 for IPv4, 24 for IPv6 */		/* following internal flags cannot overlap with other AI_xxx flags */#define	AI_CLONE	     4	/* clone this entry for other socket types */		/* function prototypes for our own internal functions */static int	getaddrinfo_host(const char *, struct hostent *,							 struct hostent **, char *, int, int);static int	getaddrinfo_serv(struct addrinfo *,							 const struct addrinfo *, const char *,							 struct servent *, char *, int);static int	getaddrinfo_port(struct addrinfo *, int , int);static int	addrinfo_local(const char *, struct addrinfo *,						   struct addrinfo **);static struct addrinfo	*getaddrinfo_clone(struct addrinfo *);		/* globals for all functions in this file; these *must* be		   read-only if this function is to be reentrant */static struct addrinfo	hints_default;intgetaddrinfo(const char *host, const char *serv,			const struct addrinfo *hintsptr, struct addrinfo **result){	int					rc, error;	struct hostent		*hptr, hent;	struct servent		sent;	char				hentbuf[HENTBUFSIZ], hent_addr[HENTMAXADDR];	char				*hent_aliases[1], *hent_addr_list[2];	char				**ap;	struct addrinfo		hints, *ai, *aihead, **aipnext;#ifdef	IPV4	struct sockaddr_in	*sinptr;#endif#ifdef	IPV6	struct sockaddr_in6	*sin6ptr;#endif/* If we encounter an error we want to free() any dynamic memory   that we've allocated.  This is our hack to simplify the code. */#define	error(e) { error = (e); goto bad; }	/*	 * We must make a copy of the caller's hints structure, so we can	 * modify ai_family.  If the caller doesn't provide a hints structure,	 * use a default one.  This simplifies all the following code.	 * In the default one, ai_flags, ai_socktype, and ai_protocol are all 0,	 * but we have to set ai_family to AF_UNSPEC, which isn't guaranteed to	 * be 0.	 */ 	if (hintsptr == NULL) {		hints_default.ai_family = AF_UNSPEC;		hints = hints_default;	/* struct copy */	} else		hints = *hintsptr;		/* struct copy */	/*	 * First some error checking.	 */	if (hints.ai_flags & ~(AI_PASSIVE | AI_CANONNAME))		error(EAI_BADFLAGS);	/* unknown flag bits */	/*	 * Check that the family is valid, and if a socket type is also	 * specified, check that it's valid for the family.	 */	if (hints.ai_family != 0) {		switch(hints.ai_family) {			case AF_UNSPEC:	break;				/* Actually, AF_UNSPEC is normally defined as 0,				   but Posix.1g does not require this. */#ifdef	IPV4			case AF_INET:				if (hints.ai_socktype != 0 &&					(hints.ai_socktype != SOCK_STREAM &&					 hints.ai_socktype != SOCK_DGRAM &&					 hints.ai_socktype != SOCK_RAW))					error(EAI_SOCKTYPE);	/* invalid socket type */				break;#endif#ifdef	IPV6			case AF_INET6:				if (hints.ai_socktype != 0 &&					(hints.ai_socktype != SOCK_STREAM &&					 hints.ai_socktype != SOCK_DGRAM &&					 hints.ai_socktype != SOCK_RAW))					error(EAI_SOCKTYPE);	/* invalid socket type */				break;#endif#ifdef	LOCAL			case AF_LOCAL:				if (hints.ai_socktype != 0 &&					(hints.ai_socktype != SOCK_STREAM &&					 hints.ai_socktype != SOCK_DGRAM))					error(EAI_SOCKTYPE);	/* invalid socket type */				break;#endif		default:			error(EAI_FAMILY);		/* unknown protocol family */		}	}	if (host == NULL || host[0] == '\0') {		if (serv == NULL || serv[0] == '\0')			error(EAI_NONAME);	/* either host or serv must be specified */		if (hints.ai_flags & AI_PASSIVE) {			/*			 * No "host" and AI_PASSIVE: the returned address must be			 * ready for bind(): 0.0.0.0 for IPv4 or 0::0 for IPv6.			 */			switch (hints.ai_family) {#ifdef	IPV4			case AF_INET:	host = "0.0.0.0"; break;#endif#ifdef	IPV6			case AF_INET6:	host = "0::0"; break;#endif#ifdef	LOCAL			case AF_LOCAL:				if (serv[0] != '/')		/* allow service to specify path */					error(EAI_ADDRFAMILY);				break;#endif			case 0:			error(EAI_ADDRFAMILY);					/* How can we initialize a socket address structure					   for a passive open if we don't even know the family? */			}		} else {			/*			 * No host and not AI_PASSIVE: caller implies connect() to			 * local host.			 */			host = "localhost";		}	} else if (hints.ai_family == 0) {		/*		 * Caller specifies a host but no address family.		 * If the host string is really a valid IPv4 dotted-decimal address,		 * set family to IPv4.  Similarly for IPv6 strings.		 * This allows server applications to be protocol independent		 * (not having to hard code a protocol family), allowing the		 * user who starts the program to specify either 0.0.0.0 or 0::0.		 *		 * Assumed below is that inet_pton() allows only "valid" strings,		 * which Paul Vixie put into the BIND-4.9.4 version of this function.		 */		char	temp[16];#ifdef	IPV4		if (inet_pton(AF_INET, host, temp) == 1)			hints.ai_family = AF_INET;#endif#ifdef	IPV6		if (inet_pton(AF_INET6, host, temp) == 1)			hints.ai_family = AF_INET6;#endif		/*		 * Note that we could bypass some of the testing done in		 * getaddrinfo_host(), but it doesn't seem worth complicating		 * this (already long) function.		 */	}#ifdef	LOCAL	/*	 * For a Unix domain socket only one string can be provided and we	 * require it to be an absolute pathname.  (Using relative pathnames	 * is asking for trouble.)  We allow this string to be specified as	 * either the hostname or the service name, in which case we ignore	 * the other string.  Notice that a slash is not allowed in a DNS	 * hostname (see RFC 1912) and a slash does not appear in any of the	 * service names in /etc/services either.  Hence no conflict.	 * For example, often a protocol-independent server will allow an	 * argument to specify the service (e.g., port number) and let the	 * hostname be wildcarded.  Similarly, a protocol-independent client	 * often allows only the hostname as a command-line argument, hardcoding	 * a service name in the program (which we ignore).	 */	if ((host != NULL && host[0] == '/'))		return(addrinfo_local(host, &hints, result));	if ((serv != NULL && serv[0] == '/'))		return(addrinfo_local(serv, &hints, result));#endif	/*	 * Look up the host.  The code above guarantees that "host"	 * is a nonnull pointer to a nonull string.	 *	 * We first initialize "hent" assuming "host" is an IPv4/IPv6 address	 * (instead of a name).  This saves passing lots of additional	 * arguments to getaddrinfo_host().	 */	hent.h_name = hentbuf;		/* char string specifying address goes here */	hent.h_aliases = hent_aliases;	hent_aliases[0] = NULL;			/* no aliases */	hent.h_addr_list = hent_addr_list;	hent_addr_list[0] = hent_addr;	/* one binary address in [0] */	hent_addr_list[1] = NULL;	hptr = &hent;	if ( (rc = getaddrinfo_host(host, &hent, &hptr, hentbuf, HENTBUFSIZ,								hints.ai_family)) != 0)		error(rc);	/*	 * "hptr" now points to a filled in hostent{}.	 * If "host" was an IPv4/IPv6 address, instead of a name, then	 * "hptr" points to our own "hent" structure.	 * If gethostbyname_r() was called, then "hptr" points to our own	 * "hent" structure, which was passed as as an argument to the	 * reentrant function.	 * If gethostbyname() was called, then "hptr" points to the static	 * hostent{} that it returned.	 *	 * Check for address family mismatch if the caller specified one.	 * Note that Posix.1g assumes that AF_foo == PF_foo.	 */	if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype)		error(EAI_ADDRFAMILY);	/*	 * Go through the list of returned addresses and create one	 * addrinfo{} for each one, linking all the structures together.	 * We still have not looked at the service--that comes after this.	 */	aihead = NULL;	aipnext = &aihead;	for (ap = hptr->h_addr_list; *ap != NULL; ap++) {		if ( (ai = calloc(1, sizeof(struct addrinfo))) == NULL)			error(EAI_MEMORY);		*aipnext = ai;			/* prev points to this new one */		aipnext = &ai->ai_next;	/* pointer to next one goes here */			/* initialize from hints; could be 0 */		if ( (ai->ai_socktype = hints.ai_socktype) == 0)			ai->ai_flags |= AI_CLONE;		ai->ai_protocol = hints.ai_protocol;		ai->ai_family = hptr->h_addrtype;		switch (ai->ai_family) {			/*			 * Allocate a socket address structure and fill it in.			 * The port number will be filled in later, when the service			 * is processed.			 */#ifdef	IPV4			case AF_INET:				if ( (sinptr = calloc(1, sizeof(struct sockaddr_in))) == NULL)					error(EAI_MEMORY);#ifdef	SIN_LEN				sinptr->sin_len = sizeof(struct sockaddr_in);#endif				sinptr->sin_family = AF_INET;				memcpy(&sinptr->sin_addr, *ap, sizeof(struct in_addr));				ai->ai_addr = (struct sockaddr *) sinptr;				ai->ai_addrlen = sizeof(struct sockaddr_in);				break;#endif	/* IPV4 */#ifdef	IPV6			case AF_INET6:

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -