| /* 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) |