1 #include <sys/cdefs.h> 2 #include "namespace.h" 3 #include <lib.h> 4 5 #include <assert.h> 6 #include <errno.h> 7 #include <stdio.h> 8 #include <string.h> 9 #include <sys/ioctl.h> 10 #include <sys/socket.h> 11 #include <sys/types.h> 12 #include <sys/ucred.h> 13 #include <netinet/tcp.h> 14 15 #include <net/gen/in.h> 16 #include <net/gen/tcp.h> 17 #include <net/gen/tcp_io.h> 18 #include <net/gen/udp.h> 19 #include <net/gen/udp_io.h> 20 21 #include <minix/type.h> 22 23 #define DEBUG 0 24 25 static int _tcp_getsockopt(int sock, int level, int option_name, 26 void *__restrict option_value, socklen_t *__restrict option_len); 27 static int _udp_getsockopt(int sock, int level, int option_name, 28 void *__restrict option_value, socklen_t *__restrict option_len); 29 static int _uds_getsockopt(int sock, int level, int option_name, 30 void *__restrict option_value, socklen_t *__restrict option_len); 31 static void getsockopt_copy(void *return_value, size_t return_len, 32 void *__restrict option_value, socklen_t *__restrict option_len); 33 34 /* 35 * Get socket options. 36 */ 37 static int 38 __getsockopt(int fd, int level, int option_name, 39 void * __restrict option_value, socklen_t * __restrict option_len) 40 { 41 message m; 42 43 if (option_len == NULL) { 44 errno = EFAULT; 45 return -1; 46 } 47 48 memset(&m, 0, sizeof(m)); 49 m.m_lc_vfs_sockopt.fd = fd; 50 m.m_lc_vfs_sockopt.level = level; 51 m.m_lc_vfs_sockopt.name = option_name; 52 m.m_lc_vfs_sockopt.buf = (vir_bytes)option_value; 53 m.m_lc_vfs_sockopt.len = *option_len; 54 55 if (_syscall(VFS_PROC_NR, VFS_GETSOCKOPT, &m) < 0) 56 return -1; 57 58 *option_len = m.m_vfs_lc_socklen.len; 59 return 0; 60 } 61 62 int getsockopt(int sock, int level, int option_name, 63 void *__restrict option_value, socklen_t *__restrict option_len) 64 { 65 int r; 66 nwio_tcpopt_t tcpopt; 67 nwio_udpopt_t udpopt; 68 struct sockaddr_un uds_addr; 69 70 r = __getsockopt(sock, level, option_name, option_value, option_len); 71 if (r != -1 || (errno != ENOTSOCK && errno != ENOSYS)) 72 return r; 73 74 r= ioctl(sock, NWIOGTCPOPT, &tcpopt); 75 if (r != -1 || errno != ENOTTY) 76 { 77 if (r == -1) 78 { 79 /* Bad file descriptor */ 80 return -1; 81 } 82 return _tcp_getsockopt(sock, level, option_name, 83 option_value, option_len); 84 } 85 86 r= ioctl(sock, NWIOGUDPOPT, &udpopt); 87 if (r != -1 || errno != ENOTTY) 88 { 89 if (r == -1) 90 { 91 /* Bad file descriptor */ 92 return -1; 93 } 94 return _udp_getsockopt(sock, level, option_name, 95 option_value, option_len); 96 } 97 98 r= ioctl(sock, NWIOGUDSADDR, &uds_addr); 99 if (r != -1 || errno != ENOTTY) 100 { 101 if (r == -1) 102 { 103 /* Bad file descriptor */ 104 return -1; 105 } 106 return _uds_getsockopt(sock, level, option_name, 107 option_value, option_len); 108 } 109 110 errno = ENOTSOCK; 111 return -1; 112 } 113 114 static void getsockopt_copy(void *return_value, size_t return_len, 115 void *__restrict option_value, socklen_t *__restrict option_len) 116 { 117 /* copy as much data as possible */ 118 if (*option_len < return_len) 119 memcpy(option_value, return_value, *option_len); 120 else 121 memcpy(option_value, return_value, return_len); 122 123 /* return length */ 124 *option_len = return_len; 125 } 126 127 static int _tcp_getsockopt(int sock, int level, int option_name, 128 void *__restrict option_value, socklen_t *__restrict option_len) 129 { 130 int i, r, err; 131 132 if (level == SOL_SOCKET && option_name == SO_REUSEADDR) 133 { 134 i = 1; /* Binds to TIME_WAIT sockets never cause errors */ 135 getsockopt_copy(&i, sizeof(i), option_value, option_len); 136 return 0; 137 } 138 if (level == SOL_SOCKET && option_name == SO_KEEPALIVE) 139 { 140 i = 1; /* Keepalive is always on */ 141 getsockopt_copy(&i, sizeof(i), option_value, option_len); 142 return 0; 143 } 144 if (level == SOL_SOCKET && option_name == SO_ERROR) 145 { 146 r = ioctl(sock, NWIOTCPGERROR, &err); 147 if (r != 0) 148 return r; 149 150 getsockopt_copy(&err, sizeof(err), option_value, option_len); 151 return 0; 152 } 153 if (level == SOL_SOCKET && option_name == SO_RCVBUF) 154 { 155 i = 32 * 1024; /* Receive buffer in the current 156 * implementation 157 */ 158 getsockopt_copy(&i, sizeof(i), option_value, option_len); 159 return 0; 160 } 161 if (level == SOL_SOCKET && option_name == SO_SNDBUF) 162 { 163 i = 32 * 1024; /* Send buffer in the current implementation */ 164 getsockopt_copy(&i, sizeof(i), option_value, option_len); 165 return 0; 166 } 167 if (level == SOL_SOCKET && option_name == SO_TYPE) 168 { 169 i = SOCK_STREAM; /* this is a TCP socket */ 170 getsockopt_copy(&i, sizeof(i), option_value, option_len); 171 return 0; 172 } 173 if (level == IPPROTO_TCP && option_name == TCP_NODELAY) 174 { 175 i = 0; /* nodelay is always off */ 176 getsockopt_copy(&i, sizeof(i), option_value, option_len); 177 return 0; 178 } 179 #if DEBUG 180 fprintf(stderr, "_tcp_getsocketopt: level %d, name %d\n", 181 level, option_name); 182 #endif 183 184 errno= ENOPROTOOPT; 185 return -1; 186 } 187 188 static int _udp_getsockopt(int sock, int level, int option_name, 189 void *__restrict option_value, socklen_t *__restrict option_len) 190 { 191 int i; 192 193 if (level == SOL_SOCKET && option_name == SO_TYPE) 194 { 195 i = SOCK_DGRAM; /* this is a UDP socket */ 196 getsockopt_copy(&i, sizeof(i), option_value, option_len); 197 return 0; 198 } 199 #if DEBUG 200 fprintf(stderr, "_udp_getsocketopt: level %d, name %d\n", 201 level, option_name); 202 #endif 203 204 errno= ENOSYS; 205 return -1; 206 } 207 208 static int _uds_getsockopt(int sock, int level, int option_name, 209 void *__restrict option_value, socklen_t *__restrict option_len) 210 { 211 int i, r; 212 size_t size; 213 214 if (level == SOL_SOCKET && option_name == SO_RCVBUF) 215 { 216 r= ioctl(sock, NWIOGUDSRCVBUF, &size); 217 if (r == -1) { 218 return r; 219 } 220 221 getsockopt_copy(&size, sizeof(size), option_value, option_len); 222 return 0; 223 } 224 225 if (level == SOL_SOCKET && option_name == SO_SNDBUF) 226 { 227 r= ioctl(sock, NWIOGUDSSNDBUF, &size); 228 if (r == -1) { 229 return r; 230 } 231 232 getsockopt_copy(&size, sizeof(size), option_value, option_len); 233 return 0; 234 } 235 236 if (level == SOL_SOCKET && option_name == SO_TYPE) 237 { 238 r= ioctl(sock, NWIOGUDSSOTYPE, &i); 239 if (r == -1) { 240 return r; 241 } 242 243 getsockopt_copy(&i, sizeof(i), option_value, option_len); 244 return 0; 245 } 246 247 if (level == SOL_SOCKET && option_name == SO_PEERCRED) 248 { 249 struct uucred cred; 250 251 r= ioctl(sock, NWIOGUDSPEERCRED, &cred); 252 if (r == -1) { 253 return -1; 254 } 255 256 getsockopt_copy(&cred, sizeof(struct uucred), option_value, 257 option_len); 258 return 0; 259 } 260 261 262 if (level == SOL_SOCKET && option_name == SO_REUSEADDR) 263 { 264 i = 1; /* as long as nobody is listen()ing on the address, 265 * it can be reused without waiting for a 266 * timeout to expire. 267 */ 268 getsockopt_copy(&i, sizeof(i), option_value, option_len); 269 return 0; 270 } 271 272 if (level == SOL_SOCKET && option_name == SO_PASSCRED) 273 { 274 i = 1; /* option is always 'on' */ 275 getsockopt_copy(&i, sizeof(i), option_value, option_len); 276 return 0; 277 } 278 279 #if DEBUG 280 fprintf(stderr, "_uds_getsocketopt: level %d, name %d\n", 281 level, option_name); 282 #endif 283 284 errno= ENOSYS; 285 return -1; 286 } 287