1/* $Id: socket.c,v 1.1.1.1 2008/10/15 03:26:19 james26_jang Exp $ 2 * socket.c: Socket syscall emulation for Solaris 2.6+ 3 * 4 * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) 5 * 6 * 1999-08-19 Fixed socketpair code 7 * Jason Rappleye (rappleye@ccr.buffalo.edu) 8 */ 9 10#include <linux/types.h> 11#include <linux/smp_lock.h> 12#include <linux/mm.h> 13#include <linux/slab.h> 14#include <linux/socket.h> 15#include <linux/file.h> 16 17#include <asm/uaccess.h> 18#include <asm/string.h> 19#include <asm/oplib.h> 20#include <asm/idprom.h> 21 22#include "conv.h" 23 24#define SOCK_SOL_STREAM 2 25#define SOCK_SOL_DGRAM 1 26#define SOCK_SOL_RAW 4 27#define SOCK_SOL_RDM 5 28#define SOCK_SOL_SEQPACKET 6 29 30#define SOL_SO_SNDLOWAT 0x1003 31#define SOL_SO_RCVLOWAT 0x1004 32#define SOL_SO_SNDTIMEO 0x1005 33#define SOL_SO_RCVTIMEO 0x1006 34#define SOL_SO_STATE 0x2000 35 36#define SOL_SS_NDELAY 0x040 37#define SOL_SS_NONBLOCK 0x080 38#define SOL_SS_ASYNC 0x100 39 40#define SO_STATE 0x000e 41 42static int socket_check(int family, int type) 43{ 44 if (family != PF_UNIX && family != PF_INET) 45 return -ESOCKTNOSUPPORT; 46 switch (type) { 47 case SOCK_SOL_STREAM: type = SOCK_STREAM; break; 48 case SOCK_SOL_DGRAM: type = SOCK_DGRAM; break; 49 case SOCK_SOL_RAW: type = SOCK_RAW; break; 50 case SOCK_SOL_RDM: type = SOCK_RDM; break; 51 case SOCK_SOL_SEQPACKET: type = SOCK_SEQPACKET; break; 52 default: return -EINVAL; 53 } 54 return type; 55} 56 57static int solaris_to_linux_sockopt(int optname) 58{ 59 switch (optname) { 60 case SOL_SO_SNDLOWAT: optname = SO_SNDLOWAT; break; 61 case SOL_SO_RCVLOWAT: optname = SO_RCVLOWAT; break; 62 case SOL_SO_SNDTIMEO: optname = SO_SNDTIMEO; break; 63 case SOL_SO_RCVTIMEO: optname = SO_RCVTIMEO; break; 64 case SOL_SO_STATE: optname = SO_STATE; break; 65 }; 66 67 return optname; 68} 69 70asmlinkage int solaris_socket(int family, int type, int protocol) 71{ 72 int (*sys_socket)(int, int, int) = 73 (int (*)(int, int, int))SYS(socket); 74 75 type = socket_check (family, type); 76 if (type < 0) return type; 77 return sys_socket(family, type, protocol); 78} 79 80asmlinkage int solaris_socketpair(int *usockvec) 81{ 82 int (*sys_socketpair)(int, int, int, int *) = 83 (int (*)(int, int, int, int *))SYS(socketpair); 84 85 /* solaris socketpair really only takes one arg at the syscall 86 * level, int * usockvec. The libs apparently take care of 87 * making sure that family==AF_UNIX and type==SOCK_STREAM. The 88 * pointer we really want ends up residing in the first (and 89 * supposedly only) argument. 90 */ 91 92 return sys_socketpair(AF_UNIX, SOCK_STREAM, 0, (int *)usockvec); 93} 94 95asmlinkage int solaris_bind(int fd, struct sockaddr *addr, int addrlen) 96{ 97 int (*sys_bind)(int, struct sockaddr *, int) = 98 (int (*)(int, struct sockaddr *, int))SUNOS(104); 99 100 return sys_bind(fd, addr, addrlen); 101} 102 103asmlinkage int solaris_setsockopt(int fd, int level, int optname, u32 optval, int optlen) 104{ 105 int (*sunos_setsockopt)(int, int, int, u32, int) = 106 (int (*)(int, int, int, u32, int))SUNOS(105); 107 108 optname = solaris_to_linux_sockopt(optname); 109 if (optname < 0) 110 return optname; 111 if (optname == SO_STATE) 112 return 0; 113 114 return sunos_setsockopt(fd, level, optname, optval, optlen); 115} 116 117asmlinkage int solaris_getsockopt(int fd, int level, int optname, u32 optval, u32 optlen) 118{ 119 int (*sunos_getsockopt)(int, int, int, u32, u32) = 120 (int (*)(int, int, int, u32, u32))SUNOS(118); 121 122 optname = solaris_to_linux_sockopt(optname); 123 if (optname < 0) 124 return optname; 125 126 if (optname == SO_STATE) 127 optname = SOL_SO_STATE; 128 129 return sunos_getsockopt(fd, level, optname, optval, optlen); 130} 131 132asmlinkage int solaris_connect(int fd, struct sockaddr *addr, int addrlen) 133{ 134 int (*sys_connect)(int, struct sockaddr *, int) = 135 (int (*)(int, struct sockaddr *, int))SYS(connect); 136 137 return sys_connect(fd, addr, addrlen); 138} 139 140asmlinkage int solaris_accept(int fd, struct sockaddr *addr, int *addrlen) 141{ 142 int (*sys_accept)(int, struct sockaddr *, int *) = 143 (int (*)(int, struct sockaddr *, int *))SYS(accept); 144 145 return sys_accept(fd, addr, addrlen); 146} 147 148asmlinkage int solaris_listen(int fd, int backlog) 149{ 150 int (*sys_listen)(int, int) = 151 (int (*)(int, int))SUNOS(106); 152 153 return sys_listen(fd, backlog); 154} 155 156asmlinkage int solaris_shutdown(int fd, int how) 157{ 158 int (*sys_shutdown)(int, int) = 159 (int (*)(int, int))SYS(shutdown); 160 161 return sys_shutdown(fd, how); 162} 163 164#define MSG_SOL_OOB 0x1 165#define MSG_SOL_PEEK 0x2 166#define MSG_SOL_DONTROUTE 0x4 167#define MSG_SOL_EOR 0x8 168#define MSG_SOL_CTRUNC 0x10 169#define MSG_SOL_TRUNC 0x20 170#define MSG_SOL_WAITALL 0x40 171#define MSG_SOL_DONTWAIT 0x80 172 173static int solaris_to_linux_msgflags(int flags) 174{ 175 int fl = flags & (MSG_OOB|MSG_PEEK|MSG_DONTROUTE); 176 177 if (flags & MSG_SOL_EOR) fl |= MSG_EOR; 178 if (flags & MSG_SOL_CTRUNC) fl |= MSG_CTRUNC; 179 if (flags & MSG_SOL_TRUNC) fl |= MSG_TRUNC; 180 if (flags & MSG_SOL_WAITALL) fl |= MSG_WAITALL; 181 if (flags & MSG_SOL_DONTWAIT) fl |= MSG_DONTWAIT; 182 return fl; 183} 184 185static int linux_to_solaris_msgflags(int flags) 186{ 187 int fl = flags & (MSG_OOB|MSG_PEEK|MSG_DONTROUTE); 188 189 if (flags & MSG_EOR) fl |= MSG_SOL_EOR; 190 if (flags & MSG_CTRUNC) fl |= MSG_SOL_CTRUNC; 191 if (flags & MSG_TRUNC) fl |= MSG_SOL_TRUNC; 192 if (flags & MSG_WAITALL) fl |= MSG_SOL_WAITALL; 193 if (flags & MSG_DONTWAIT) fl |= MSG_SOL_DONTWAIT; 194 return fl; 195} 196 197asmlinkage int solaris_recvfrom(int s, char *buf, int len, int flags, u32 from, u32 fromlen) 198{ 199 int (*sys_recvfrom)(int, void *, size_t, unsigned, struct sockaddr *, int *) = 200 (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int *))SYS(recvfrom); 201 202 return sys_recvfrom(s, buf, len, solaris_to_linux_msgflags(flags), (struct sockaddr *)A(from), (int *)A(fromlen)); 203} 204 205asmlinkage int solaris_recv(int s, char *buf, int len, int flags) 206{ 207 int (*sys_recvfrom)(int, void *, size_t, unsigned, struct sockaddr *, int *) = 208 (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int *))SYS(recvfrom); 209 210 return sys_recvfrom(s, buf, len, solaris_to_linux_msgflags(flags), NULL, NULL); 211} 212 213asmlinkage int solaris_sendto(int s, char *buf, int len, int flags, u32 to, u32 tolen) 214{ 215 int (*sys_sendto)(int, void *, size_t, unsigned, struct sockaddr *, int *) = 216 (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int *))SYS(sendto); 217 218 return sys_sendto(s, buf, len, solaris_to_linux_msgflags(flags), (struct sockaddr *)A(to), (int *)A(tolen)); 219} 220 221asmlinkage int solaris_send(int s, char *buf, int len, int flags) 222{ 223 int (*sys_sendto)(int, void *, size_t, unsigned, struct sockaddr *, int *) = 224 (int (*)(int, void *, size_t, unsigned, struct sockaddr *, int *))SYS(sendto); 225 226 return sys_sendto(s, buf, len, solaris_to_linux_msgflags(flags), NULL, NULL); 227} 228 229asmlinkage int solaris_getpeername(int fd, struct sockaddr *addr, int *addrlen) 230{ 231 int (*sys_getpeername)(int, struct sockaddr *, int *) = 232 (int (*)(int, struct sockaddr *, int *))SYS(getpeername); 233 234 return sys_getpeername(fd, addr, addrlen); 235} 236 237asmlinkage int solaris_getsockname(int fd, struct sockaddr *addr, int *addrlen) 238{ 239 int (*sys_getsockname)(int, struct sockaddr *, int *) = 240 (int (*)(int, struct sockaddr *, int *))SYS(getsockname); 241 242 return sys_getsockname(fd, addr, addrlen); 243} 244 245#define MAX_SOCK_ADDR 128 /* 108 for Unix domain - 246 16 for IP, 16 for IPX, 247 24 for IPv6, 248 about 80 for AX.25 */ 249 250extern __inline__ struct socket *socki_lookup(struct inode *inode) 251{ 252 return &inode->u.socket_i; 253} 254 255extern __inline__ struct socket *sockfd_lookup(int fd, int *err) 256{ 257 struct file *file; 258 struct inode *inode; 259 260 if (!(file = fget(fd))) { 261 *err = -EBADF; 262 return NULL; 263 } 264 265 inode = file->f_dentry->d_inode; 266 if (!inode->i_sock || !socki_lookup(inode)) { 267 *err = -ENOTSOCK; 268 fput(file); 269 return NULL; 270 } 271 272 return socki_lookup(inode); 273} 274 275extern __inline__ void sockfd_put(struct socket *sock) 276{ 277 fput(sock->file); 278} 279 280struct sol_nmsghdr { 281 u32 msg_name; 282 int msg_namelen; 283 u32 msg_iov; 284 u32 msg_iovlen; 285 u32 msg_control; 286 u32 msg_controllen; 287 u32 msg_flags; 288}; 289 290struct sol_cmsghdr { 291 u32 cmsg_len; 292 int cmsg_level; 293 int cmsg_type; 294 unsigned char cmsg_data[0]; 295}; 296 297struct iovec32 { 298 u32 iov_base; 299 u32 iov_len; 300}; 301 302static inline int iov_from_user32_to_kern(struct iovec *kiov, 303 struct iovec32 *uiov32, 304 int niov) 305{ 306 int tot_len = 0; 307 308 while(niov > 0) { 309 u32 len, buf; 310 311 if(get_user(len, &uiov32->iov_len) || 312 get_user(buf, &uiov32->iov_base)) { 313 tot_len = -EFAULT; 314 break; 315 } 316 tot_len += len; 317 kiov->iov_base = (void *)A(buf); 318 kiov->iov_len = (__kernel_size_t) len; 319 uiov32++; 320 kiov++; 321 niov--; 322 } 323 return tot_len; 324} 325 326static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg, 327 struct sol_nmsghdr *umsg) 328{ 329 u32 tmp1, tmp2, tmp3; 330 int err; 331 332 err = get_user(tmp1, &umsg->msg_name); 333 err |= __get_user(tmp2, &umsg->msg_iov); 334 err |= __get_user(tmp3, &umsg->msg_control); 335 if (err) 336 return -EFAULT; 337 338 kmsg->msg_name = (void *)A(tmp1); 339 kmsg->msg_iov = (struct iovec *)A(tmp2); 340 kmsg->msg_control = (void *)A(tmp3); 341 342 err = get_user(kmsg->msg_namelen, &umsg->msg_namelen); 343 err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen); 344 err |= get_user(kmsg->msg_flags, &umsg->msg_flags); 345 346 kmsg->msg_flags = solaris_to_linux_msgflags(kmsg->msg_flags); 347 348 return err; 349} 350 351/* I've named the args so it is easy to tell whose space the pointers are in. */ 352static int verify_iovec32(struct msghdr *kern_msg, struct iovec *kern_iov, 353 char *kern_address, int mode) 354{ 355 int tot_len; 356 357 if(kern_msg->msg_namelen) { 358 if(mode==VERIFY_READ) { 359 int err = move_addr_to_kernel(kern_msg->msg_name, 360 kern_msg->msg_namelen, 361 kern_address); 362 if(err < 0) 363 return err; 364 } 365 kern_msg->msg_name = kern_address; 366 } else 367 kern_msg->msg_name = NULL; 368 369 if(kern_msg->msg_iovlen > UIO_FASTIOV) { 370 kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), 371 GFP_KERNEL); 372 if(!kern_iov) 373 return -ENOMEM; 374 } 375 376 tot_len = iov_from_user32_to_kern(kern_iov, 377 (struct iovec32 *)kern_msg->msg_iov, 378 kern_msg->msg_iovlen); 379 if(tot_len >= 0) 380 kern_msg->msg_iov = kern_iov; 381 else if(kern_msg->msg_iovlen > UIO_FASTIOV) 382 kfree(kern_iov); 383 384 return tot_len; 385} 386 387asmlinkage int solaris_sendmsg(int fd, struct sol_nmsghdr *user_msg, unsigned user_flags) 388{ 389 struct socket *sock; 390 char address[MAX_SOCK_ADDR]; 391 struct iovec iov[UIO_FASTIOV]; 392 unsigned char ctl[sizeof(struct cmsghdr) + 20]; 393 unsigned char *ctl_buf = ctl; 394 struct msghdr kern_msg; 395 int err, total_len; 396 397 if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) 398 return -EFAULT; 399 if(kern_msg.msg_iovlen > UIO_MAXIOV) 400 return -EINVAL; 401 err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); 402 if (err < 0) 403 goto out; 404 total_len = err; 405 406 if(kern_msg.msg_controllen) { 407 struct sol_cmsghdr *ucmsg = (struct sol_cmsghdr *)kern_msg.msg_control; 408 unsigned long *kcmsg; 409 __kernel_size_t32 cmlen; 410 411 if(kern_msg.msg_controllen > sizeof(ctl) && 412 kern_msg.msg_controllen <= 256) { 413 err = -ENOBUFS; 414 ctl_buf = kmalloc(kern_msg.msg_controllen, GFP_KERNEL); 415 if(!ctl_buf) 416 goto out_freeiov; 417 } 418 __get_user(cmlen, &ucmsg->cmsg_len); 419 kcmsg = (unsigned long *) ctl_buf; 420 *kcmsg++ = (unsigned long)cmlen; 421 err = -EFAULT; 422 if(copy_from_user(kcmsg, &ucmsg->cmsg_level, 423 kern_msg.msg_controllen - sizeof(__kernel_size_t32))) 424 goto out_freectl; 425 kern_msg.msg_control = ctl_buf; 426 } 427 kern_msg.msg_flags = solaris_to_linux_msgflags(user_flags); 428 429 lock_kernel(); 430 sock = sockfd_lookup(fd, &err); 431 if (sock != NULL) { 432 if (sock->file->f_flags & O_NONBLOCK) 433 kern_msg.msg_flags |= MSG_DONTWAIT; 434 err = sock_sendmsg(sock, &kern_msg, total_len); 435 sockfd_put(sock); 436 } 437 unlock_kernel(); 438 439out_freectl: 440 /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ 441 if(ctl_buf != ctl) 442 kfree(ctl_buf); 443out_freeiov: 444 if(kern_msg.msg_iov != iov) 445 kfree(kern_msg.msg_iov); 446out: 447 return err; 448} 449 450asmlinkage int solaris_recvmsg(int fd, struct sol_nmsghdr *user_msg, unsigned int user_flags) 451{ 452 struct iovec iovstack[UIO_FASTIOV]; 453 struct msghdr kern_msg; 454 char addr[MAX_SOCK_ADDR]; 455 struct socket *sock; 456 struct iovec *iov = iovstack; 457 struct sockaddr *uaddr; 458 int *uaddr_len; 459 unsigned long cmsg_ptr; 460 int err, total_len, len = 0; 461 462 if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) 463 return -EFAULT; 464 if(kern_msg.msg_iovlen > UIO_MAXIOV) 465 return -EINVAL; 466 467 uaddr = kern_msg.msg_name; 468 uaddr_len = &user_msg->msg_namelen; 469 err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE); 470 if (err < 0) 471 goto out; 472 total_len = err; 473 474 cmsg_ptr = (unsigned long) kern_msg.msg_control; 475 kern_msg.msg_flags = 0; 476 477 lock_kernel(); 478 sock = sockfd_lookup(fd, &err); 479 if (sock != NULL) { 480 if (sock->file->f_flags & O_NONBLOCK) 481 user_flags |= MSG_DONTWAIT; 482 err = sock_recvmsg(sock, &kern_msg, total_len, user_flags); 483 if(err >= 0) 484 len = err; 485 sockfd_put(sock); 486 } 487 unlock_kernel(); 488 489 if(uaddr != NULL && err >= 0) 490 err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); 491 if(err >= 0) { 492 err = __put_user(linux_to_solaris_msgflags(kern_msg.msg_flags), &user_msg->msg_flags); 493 if(!err) { 494 err = __put_user((unsigned long)kern_msg.msg_control - cmsg_ptr, 495 &user_msg->msg_controllen); 496 } 497 } 498 499 if(kern_msg.msg_iov != iov) 500 kfree(kern_msg.msg_iov); 501out: 502 if(err < 0) 503 return err; 504 return len; 505} 506