| #include <u.h> | 
 | #define NOPLAN9DEFINES | 
 | #include <libc.h> | 
 |  | 
 | #include <sys/types.h> | 
 | #include <sys/socket.h> | 
 | #include <netdb.h> | 
 | #include <sys/un.h> | 
 | #include <netinet/in.h> | 
 |  | 
 | static char *nets[] = { "tcp", "udp", nil }; | 
 | #define CLASS(p) ((*(uchar*)(p))>>6) | 
 |  | 
 | static struct { | 
 | 	char *net; | 
 | 	char *service; | 
 | 	int port; | 
 | } porttbl[] = { | 
 | 	"tcp",	"9fs",	564, | 
 | 	"tcp",	"whoami",	565, | 
 | 	"tcp",	"guard",	566, | 
 | 	"tcp",	"ticket",	567, | 
 | 	"tcp",	"exportfs",	17007, | 
 | 	"tcp",	"rexexec",	17009, | 
 | 	"tcp",	"ncpu",	17010, | 
 | 	"tcp",	"cpu",	17013, | 
 | 	"tcp",	"venti",	17034, | 
 | 	"tcp",	"wiki",	17035, | 
 | 	"tcp",	"secstore",	5356, | 
 | 	"udp",	"dns",	53, | 
 | 	"tcp",	"dns",	53, | 
 | }; | 
 |  | 
 | static int | 
 | setport(struct sockaddr_storage *ss, int port) | 
 | { | 
 | 	switch(ss->ss_family){ | 
 | 	case AF_INET: | 
 | 		((struct sockaddr_in*)ss)->sin_port = htons(port); | 
 | 		break; | 
 | 	case AF_INET6: | 
 | 		((struct sockaddr_in6*)ss)->sin6_port = htons(port); | 
 | 		break; | 
 | 	default: | 
 | 		errstr("unknown protocol family %d", ss->ss_family); | 
 | 		return -1; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | int | 
 | p9dialparse(char *addr, char **pnet, char **punix, void *phost, int *pport) | 
 | { | 
 | 	char *net, *host, *port, *e; | 
 | 	int i; | 
 | 	struct servent *se; | 
 | 	struct hostent *he; | 
 | 	struct sockaddr_storage *ss; | 
 | 	struct addrinfo *result; | 
 |  | 
 | 	ss = phost; | 
 |  | 
 | 	memset(ss, 0, sizeof *ss); | 
 |  | 
 | 	*punix = nil; | 
 | 	net = addr; | 
 | 	if((host = strchr(net, '!')) == nil){ | 
 | 		werrstr("malformed address"); | 
 | 		return -1; | 
 | 	} | 
 | 	*host++ = 0; | 
 | 	if((port = strchr(host, '!')) == nil){ | 
 | 		if(strcmp(net, "unix")==0 || strcmp(net, "net")==0){ | 
 | 		Unix: | 
 | 			if(strlen(host)+1 > sizeof ((struct sockaddr_un*)&ss)->sun_path){ | 
 | 				werrstr("unix socket name too long"); | 
 | 				return -1; | 
 | 			} | 
 | 			*punix = host; | 
 | 			*pnet = "unix"; | 
 | 			ss->ss_family = AF_UNIX; | 
 | 			strcpy(((struct sockaddr_un*)ss)->sun_path, host); | 
 | 			*pport = 0; | 
 | 			return 0; | 
 | 		} | 
 | 		werrstr("malformed address"); | 
 | 		return -1; | 
 | 	} | 
 | 	*port++ = 0; | 
 |  | 
 | 	if(*host == 0){ | 
 | 		werrstr("malformed address (empty host)"); | 
 | 		return -1; | 
 | 	} | 
 | 	if(*port == 0){ | 
 | 		werrstr("malformed address (empty port)"); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	if(strcmp(net, "unix") == 0) | 
 | 		goto Unix; | 
 |  | 
 | 	if(strcmp(net, "tcp")!=0 && strcmp(net, "udp")!=0 && strcmp(net, "net") != 0){ | 
 | 		werrstr("bad network %s!%s!%s", net, host, port); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	/* translate host */ | 
 | 	if(strcmp(host, "*") == 0){ | 
 | 		ss->ss_family = AF_INET6; | 
 | 		((struct sockaddr_in6*)ss)->sin6_addr = in6addr_any; | 
 | 	}else if((he = gethostbyname(host)) != nil && he->h_addr_list[0] != nil){ | 
 | 		ss->ss_family = he->h_addrtype; | 
 | 		switch(ss->ss_family){ | 
 | 		case AF_INET: | 
 | 			((struct sockaddr_in*)ss)->sin_addr = *(struct in_addr*) *(he->h_addr_list); | 
 | 			break; | 
 | 		case AF_INET6: | 
 | 			((struct sockaddr_in6*)ss)->sin6_addr = *(struct in6_addr*) *(he->h_addr_list); | 
 | 			break; | 
 | 		default: | 
 | 			errstr("unknown protocol family %d", ss->ss_family); | 
 | 			return -1; | 
 | 		} | 
 | 	}else if(getaddrinfo(host, NULL, NULL, &result) == 0) { | 
 | 		switch (result->ai_family) { | 
 | 		case AF_INET: | 
 | 			memmove((struct sockaddr_in*)ss, result->ai_addr, result->ai_addrlen); | 
 | 			break; | 
 | 		case AF_INET6: | 
 | 			memmove((struct sockaddr_in6*)ss, result->ai_addr, result->ai_addrlen); | 
 | 			break; | 
 | 		default: | 
 | 			errstr("unknown protocol family %d", ss->ss_family); | 
 | 			return -1; | 
 | 		} | 
 | 	}else{ | 
 | 		werrstr("unknown host %s", host); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	/* translate network and port; should return list rather than first */ | 
 | 	if(strcmp(net, "net") == 0){ | 
 | 		for(i=0; nets[i]; i++){ | 
 | 			if((se = getservbyname(port, nets[i])) != nil){ | 
 | 				*pnet = nets[i]; | 
 | 				*pport = ntohs(se->s_port); | 
 | 				return setport(ss, *pport); | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	for(i=0; i<nelem(porttbl); i++){ | 
 | 		if(strcmp(net, "net") == 0 || strcmp(porttbl[i].net, net) == 0) | 
 | 		if(strcmp(porttbl[i].service, port) == 0){ | 
 | 			*pnet = porttbl[i].net; | 
 | 			*pport = porttbl[i].port; | 
 | 			return setport(ss, *pport); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if(strcmp(net, "net") == 0){ | 
 | 		werrstr("unknown service net!*!%s", port); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	if(strcmp(net, "tcp") != 0 && strcmp(net, "udp") != 0){ | 
 | 		werrstr("unknown network %s", net); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	*pnet = net; | 
 | 	i = strtol(port, &e, 0); | 
 | 	if(*e == 0){ | 
 | 		*pport = i; | 
 | 		return setport(ss, *pport); | 
 | 	} | 
 |  | 
 | 	if((se = getservbyname(port, net)) != nil){ | 
 | 		*pport = ntohs(se->s_port); | 
 | 		return setport(ss, *pport); | 
 | 	} | 
 | 	werrstr("unknown service %s!*!%s", net, port); | 
 | 	return -1; | 
 | } |