| /* Copyright (c) 2015 Google, Inc. | 
 |  * Barret Rhoden <brho@cs.berkeley.edu> | 
 |  * See LICENSE for details. */ | 
 |  | 
 | #include <errno.h> | 
 | #include <sys/socket.h> | 
 | #include <sys/types.h> | 
 | #include <sys/stat.h> | 
 | #include <fcntl.h> | 
 |  | 
 | #include <sys/plan9_helpers.h> | 
 |  | 
 | static int sol_socket_error_gso(Rock *r, void *optval, socklen_t *optlen) | 
 | { | 
 | 	char buf[Ctlsize]; | 
 | 	int fd, ret; | 
 | 	char *p; | 
 |  | 
 | 	_sock_get_conv_filename(r, "status", buf); | 
 | 	fd = open(buf, O_RDONLY); | 
 | 	if (fd < 0) | 
 | 		return -1; | 
 | 	ret = read(fd, buf, sizeof(buf)); | 
 | 	close(fd); | 
 | 	if (ret < 0) | 
 | 		return -1; | 
 | 	p = strchr(buf, ' '); | 
 | 	if (!p) | 
 | 		return -1; | 
 | 	*p = 0; | 
 | 	/* The first word in a connected TCP conv status file is 'Established'. | 
 | 	 * For UDP it is 'Open'. | 
 | 	 * | 
 | 	 * For now, we'll default to no socket error, and only set the error if | 
 | 	 * we know we aren't Established/Open.  If we want, we can parse the | 
 | 	 * different string values, like Established, Syn_sent, and return | 
 | 	 * custom error messages.  But just ECONNREFUSED is fine for now. */ | 
 | 	ret = 0; | 
 | 	switch (r->stype) { | 
 | 	case SOCK_DGRAM: | 
 | 		if (strcmp(buf, "Open")) | 
 | 			ret = ECONNREFUSED; | 
 | 		break; | 
 | 	case SOCK_STREAM: | 
 | 		if (strcmp(buf, "Established")) | 
 | 			ret = ECONNREFUSED; | 
 | 		break; | 
 | 	} | 
 | 	*(int*)optval = ret; | 
 | 	*optlen = 4; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int sol_socket_gso(Rock *r, int optname, void *optval, socklen_t *optlen) | 
 | { | 
 | 	switch (optname) { | 
 | 	case (SO_TYPE): | 
 | 		if (*optlen < 4) { | 
 | 			__set_errno(EINVAL); | 
 | 			return -1; | 
 | 		} | 
 | 		*(int*)optval = r->stype; | 
 | 		*optlen = 4; | 
 | 		break; | 
 | 	case (SO_ERROR): | 
 | 		return sol_socket_error_gso(r, optval, optlen); | 
 | 	default: | 
 | 		__set_errno(ENOPROTOOPT); | 
 | 		return -1; | 
 | 	}; | 
 | 	return 0; | 
 | } | 
 |  | 
 | int __getsockopt(int sockfd, int level, int optname, void *optval, | 
 |                  socklen_t *optlen) | 
 | { | 
 | 	Rock *r = _sock_findrock(sockfd, 0); | 
 |  | 
 | 	if (!r) { | 
 | 		/* could be EBADF too, we can't tell */ | 
 | 		__set_errno(ENOTSOCK); | 
 | 		return -1; | 
 | 	} | 
 | 	switch (level) { | 
 | 	case (SOL_SOCKET): | 
 | 		return sol_socket_gso(r, optname, optval, optlen); | 
 | 	default: | 
 | 		__set_errno(ENOPROTOOPT); | 
 | 		return -1; | 
 | 	}; | 
 | } | 
 | weak_alias(__getsockopt, getsockopt) |