1 /* $Id: socket.c,v 1.6 2002/02/08 03:57:14 davem Exp $
2 * socket.c: Socket syscall emulation for Solaris 2.6+
4 * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz)
6 * 1999-08-19 Fixed socketpair code
7 * Jason Rappleye (rappleye@ccr.buffalo.edu)
10 #include <linux/types.h>
12 #include <linux/slab.h>
13 #include <linux/socket.h>
14 #include <linux/file.h>
15 #include <linux/net.h>
16 #include <linux/compat.h>
17 #include <net/compat.h>
20 #include <asm/uaccess.h>
21 #include <asm/string.h>
22 #include <asm/oplib.h>
23 #include <asm/idprom.h>
27 #define SOCK_SOL_STREAM 2
28 #define SOCK_SOL_DGRAM 1
29 #define SOCK_SOL_RAW 4
30 #define SOCK_SOL_RDM 5
31 #define SOCK_SOL_SEQPACKET 6
33 #define SOL_SO_SNDLOWAT 0x1003
34 #define SOL_SO_RCVLOWAT 0x1004
35 #define SOL_SO_SNDTIMEO 0x1005
36 #define SOL_SO_RCVTIMEO 0x1006
37 #define SOL_SO_STATE 0x2000
39 #define SOL_SS_NDELAY 0x040
40 #define SOL_SS_NONBLOCK 0x080
41 #define SOL_SS_ASYNC 0x100
43 #define SO_STATE 0x000e
45 static int socket_check(int family
, int type
)
47 if (family
!= PF_UNIX
&& family
!= PF_INET
)
48 return -ESOCKTNOSUPPORT
;
50 case SOCK_SOL_STREAM
: type
= SOCK_STREAM
; break;
51 case SOCK_SOL_DGRAM
: type
= SOCK_DGRAM
; break;
52 case SOCK_SOL_RAW
: type
= SOCK_RAW
; break;
53 case SOCK_SOL_RDM
: type
= SOCK_RDM
; break;
54 case SOCK_SOL_SEQPACKET
: type
= SOCK_SEQPACKET
; break;
55 default: return -EINVAL
;
60 static int solaris_to_linux_sockopt(int optname
)
63 case SOL_SO_SNDLOWAT
: optname
= SO_SNDLOWAT
; break;
64 case SOL_SO_RCVLOWAT
: optname
= SO_RCVLOWAT
; break;
65 case SOL_SO_SNDTIMEO
: optname
= SO_SNDTIMEO
; break;
66 case SOL_SO_RCVTIMEO
: optname
= SO_RCVTIMEO
; break;
67 case SOL_SO_STATE
: optname
= SO_STATE
; break;
73 asmlinkage
int solaris_socket(int family
, int type
, int protocol
)
75 int (*sys_socket
)(int, int, int) =
76 (int (*)(int, int, int))SYS(socket
);
78 type
= socket_check (family
, type
);
79 if (type
< 0) return type
;
80 return sys_socket(family
, type
, protocol
);
83 asmlinkage
int solaris_socketpair(int *usockvec
)
85 int (*sys_socketpair
)(int, int, int, int *) =
86 (int (*)(int, int, int, int *))SYS(socketpair
);
88 /* solaris socketpair really only takes one arg at the syscall
89 * level, int * usockvec. The libs apparently take care of
90 * making sure that family==AF_UNIX and type==SOCK_STREAM. The
91 * pointer we really want ends up residing in the first (and
92 * supposedly only) argument.
95 return sys_socketpair(AF_UNIX
, SOCK_STREAM
, 0, (int *)usockvec
);
98 asmlinkage
int solaris_bind(int fd
, struct sockaddr
*addr
, int addrlen
)
100 int (*sys_bind
)(int, struct sockaddr
*, int) =
101 (int (*)(int, struct sockaddr
*, int))SUNOS(104);
103 return sys_bind(fd
, addr
, addrlen
);
106 asmlinkage
int solaris_setsockopt(int fd
, int level
, int optname
, u32 optval
, int optlen
)
108 int (*sunos_setsockopt
)(int, int, int, u32
, int) =
109 (int (*)(int, int, int, u32
, int))SUNOS(105);
111 optname
= solaris_to_linux_sockopt(optname
);
114 if (optname
== SO_STATE
)
117 return sunos_setsockopt(fd
, level
, optname
, optval
, optlen
);
120 asmlinkage
int solaris_getsockopt(int fd
, int level
, int optname
, u32 optval
, u32 optlen
)
122 int (*sunos_getsockopt
)(int, int, int, u32
, u32
) =
123 (int (*)(int, int, int, u32
, u32
))SUNOS(118);
125 optname
= solaris_to_linux_sockopt(optname
);
129 if (optname
== SO_STATE
)
130 optname
= SOL_SO_STATE
;
132 return sunos_getsockopt(fd
, level
, optname
, optval
, optlen
);
135 asmlinkage
int solaris_connect(int fd
, struct sockaddr __user
*addr
, int addrlen
)
137 int (*sys_connect
)(int, struct sockaddr __user
*, int) =
138 (int (*)(int, struct sockaddr __user
*, int))SYS(connect
);
140 return sys_connect(fd
, addr
, addrlen
);
143 asmlinkage
int solaris_accept(int fd
, struct sockaddr __user
*addr
, int __user
*addrlen
)
145 int (*sys_accept
)(int, struct sockaddr __user
*, int __user
*) =
146 (int (*)(int, struct sockaddr __user
*, int __user
*))SYS(accept
);
148 return sys_accept(fd
, addr
, addrlen
);
151 asmlinkage
int solaris_listen(int fd
, int backlog
)
153 int (*sys_listen
)(int, int) =
154 (int (*)(int, int))SUNOS(106);
156 return sys_listen(fd
, backlog
);
159 asmlinkage
int solaris_shutdown(int fd
, int how
)
161 int (*sys_shutdown
)(int, int) =
162 (int (*)(int, int))SYS(shutdown
);
164 return sys_shutdown(fd
, how
);
167 #define MSG_SOL_OOB 0x1
168 #define MSG_SOL_PEEK 0x2
169 #define MSG_SOL_DONTROUTE 0x4
170 #define MSG_SOL_EOR 0x8
171 #define MSG_SOL_CTRUNC 0x10
172 #define MSG_SOL_TRUNC 0x20
173 #define MSG_SOL_WAITALL 0x40
174 #define MSG_SOL_DONTWAIT 0x80
176 static int solaris_to_linux_msgflags(int flags
)
178 int fl
= flags
& (MSG_OOB
|MSG_PEEK
|MSG_DONTROUTE
);
180 if (flags
& MSG_SOL_EOR
) fl
|= MSG_EOR
;
181 if (flags
& MSG_SOL_CTRUNC
) fl
|= MSG_CTRUNC
;
182 if (flags
& MSG_SOL_TRUNC
) fl
|= MSG_TRUNC
;
183 if (flags
& MSG_SOL_WAITALL
) fl
|= MSG_WAITALL
;
184 if (flags
& MSG_SOL_DONTWAIT
) fl
|= MSG_DONTWAIT
;
188 static int linux_to_solaris_msgflags(int flags
)
190 int fl
= flags
& (MSG_OOB
|MSG_PEEK
|MSG_DONTROUTE
);
192 if (flags
& MSG_EOR
) fl
|= MSG_SOL_EOR
;
193 if (flags
& MSG_CTRUNC
) fl
|= MSG_SOL_CTRUNC
;
194 if (flags
& MSG_TRUNC
) fl
|= MSG_SOL_TRUNC
;
195 if (flags
& MSG_WAITALL
) fl
|= MSG_SOL_WAITALL
;
196 if (flags
& MSG_DONTWAIT
) fl
|= MSG_SOL_DONTWAIT
;
200 asmlinkage
int solaris_recvfrom(int s
, char __user
*buf
, int len
, int flags
, u32 from
, u32 fromlen
)
202 int (*sys_recvfrom
)(int, void __user
*, size_t, unsigned, struct sockaddr __user
*, int __user
*) =
203 (int (*)(int, void __user
*, size_t, unsigned, struct sockaddr __user
*, int __user
*))SYS(recvfrom
);
205 return sys_recvfrom(s
, buf
, len
, solaris_to_linux_msgflags(flags
), A(from
), A(fromlen
));
208 asmlinkage
int solaris_recv(int s
, char __user
*buf
, int len
, int flags
)
210 int (*sys_recvfrom
)(int, void __user
*, size_t, unsigned, struct sockaddr __user
*, int __user
*) =
211 (int (*)(int, void __user
*, size_t, unsigned, struct sockaddr __user
*, int __user
*))SYS(recvfrom
);
213 return sys_recvfrom(s
, buf
, len
, solaris_to_linux_msgflags(flags
), NULL
, NULL
);
216 asmlinkage
int solaris_sendto(int s
, char __user
*buf
, int len
, int flags
, u32 to
, u32 tolen
)
218 int (*sys_sendto
)(int, void __user
*, size_t, unsigned, struct sockaddr __user
*, int __user
*) =
219 (int (*)(int, void __user
*, size_t, unsigned, struct sockaddr __user
*, int __user
*))SYS(sendto
);
221 return sys_sendto(s
, buf
, len
, solaris_to_linux_msgflags(flags
), A(to
), A(tolen
));
224 asmlinkage
int solaris_send(int s
, char *buf
, int len
, int flags
)
226 int (*sys_sendto
)(int, void *, size_t, unsigned, struct sockaddr
*, int *) =
227 (int (*)(int, void *, size_t, unsigned, struct sockaddr
*, int *))SYS(sendto
);
229 return sys_sendto(s
, buf
, len
, solaris_to_linux_msgflags(flags
), NULL
, NULL
);
232 asmlinkage
int solaris_getpeername(int fd
, struct sockaddr
*addr
, int *addrlen
)
234 int (*sys_getpeername
)(int, struct sockaddr
*, int *) =
235 (int (*)(int, struct sockaddr
*, int *))SYS(getpeername
);
237 return sys_getpeername(fd
, addr
, addrlen
);
240 asmlinkage
int solaris_getsockname(int fd
, struct sockaddr
*addr
, int *addrlen
)
242 int (*sys_getsockname
)(int, struct sockaddr
*, int *) =
243 (int (*)(int, struct sockaddr
*, int *))SYS(getsockname
);
245 return sys_getsockname(fd
, addr
, addrlen
);
248 /* XXX This really belongs in some header file... -DaveM */
249 #define MAX_SOCK_ADDR 128 /* 108 for Unix domain -
250 16 for IP, 16 for IPX,
252 about 80 for AX.25 */
268 unsigned char cmsg_data
[0];
271 static inline int msghdr_from_user32_to_kern(struct msghdr
*kmsg
,
272 struct sol_nmsghdr __user
*umsg
)
274 u32 tmp1
, tmp2
, tmp3
;
277 err
= get_user(tmp1
, &umsg
->msg_name
);
278 err
|= __get_user(tmp2
, &umsg
->msg_iov
);
279 err
|= __get_user(tmp3
, &umsg
->msg_control
);
283 kmsg
->msg_name
= A(tmp1
);
284 kmsg
->msg_iov
= A(tmp2
);
285 kmsg
->msg_control
= A(tmp3
);
287 err
= get_user(kmsg
->msg_namelen
, &umsg
->msg_namelen
);
288 err
|= get_user(kmsg
->msg_controllen
, &umsg
->msg_controllen
);
289 err
|= get_user(kmsg
->msg_flags
, &umsg
->msg_flags
);
291 kmsg
->msg_flags
= solaris_to_linux_msgflags(kmsg
->msg_flags
);
296 asmlinkage
int solaris_sendmsg(int fd
, struct sol_nmsghdr __user
*user_msg
, unsigned user_flags
)
299 char address
[MAX_SOCK_ADDR
];
300 struct iovec iovstack
[UIO_FASTIOV
], *iov
= iovstack
;
301 unsigned char ctl
[sizeof(struct cmsghdr
) + 20];
302 unsigned char *ctl_buf
= ctl
;
303 struct msghdr msg_sys
;
304 int err
, ctl_len
, iov_size
, total_len
;
307 if (msghdr_from_user32_to_kern(&msg_sys
, user_msg
))
310 sock
= sockfd_lookup(fd
, &err
);
314 /* do not move before msg_sys is valid */
316 if (msg_sys
.msg_iovlen
> UIO_MAXIOV
)
319 /* Check whether to allocate the iovec area*/
321 iov_size
= msg_sys
.msg_iovlen
* sizeof(struct iovec
);
322 if (msg_sys
.msg_iovlen
> UIO_FASTIOV
) {
323 iov
= sock_kmalloc(sock
->sk
, iov_size
, GFP_KERNEL
);
328 err
= verify_compat_iovec(&msg_sys
, iov
, address
, VERIFY_READ
);
334 if (msg_sys
.msg_controllen
> INT_MAX
)
337 ctl_len
= msg_sys
.msg_controllen
;
339 struct sol_cmsghdr __user
*ucmsg
= msg_sys
.msg_control
;
340 unsigned long *kcmsg
;
344 if (ctl_len
<= sizeof(compat_size_t
))
347 if (ctl_len
> sizeof(ctl
)) {
349 ctl_buf
= kmalloc(ctl_len
, GFP_KERNEL
);
353 __get_user(cmlen
, &ucmsg
->cmsg_len
);
354 kcmsg
= (unsigned long *) ctl_buf
;
355 *kcmsg
++ = (unsigned long)cmlen
;
357 if (copy_from_user(kcmsg
, &ucmsg
->cmsg_level
,
358 ctl_len
- sizeof(compat_size_t
)))
360 msg_sys
.msg_control
= ctl_buf
;
362 msg_sys
.msg_flags
= solaris_to_linux_msgflags(user_flags
);
364 if (sock
->file
->f_flags
& O_NONBLOCK
)
365 msg_sys
.msg_flags
|= MSG_DONTWAIT
;
366 err
= sock_sendmsg(sock
, &msg_sys
, total_len
);
370 sock_kfree_s(sock
->sk
, ctl_buf
, ctl_len
);
373 sock_kfree_s(sock
->sk
, iov
, iov_size
);
380 asmlinkage
int solaris_recvmsg(int fd
, struct sol_nmsghdr __user
*user_msg
, unsigned int user_flags
)
383 struct iovec iovstack
[UIO_FASTIOV
];
384 struct iovec
*iov
= iovstack
;
385 struct msghdr msg_sys
;
386 unsigned long cmsg_ptr
;
387 int err
, iov_size
, total_len
, len
;
389 /* kernel mode address */
390 char addr
[MAX_SOCK_ADDR
];
392 /* user mode address pointers */
393 struct sockaddr __user
*uaddr
;
394 int __user
*uaddr_len
;
396 if (msghdr_from_user32_to_kern(&msg_sys
, user_msg
))
399 sock
= sockfd_lookup(fd
, &err
);
404 if (msg_sys
.msg_iovlen
> UIO_MAXIOV
)
407 /* Check whether to allocate the iovec area*/
409 iov_size
= msg_sys
.msg_iovlen
* sizeof(struct iovec
);
410 if (msg_sys
.msg_iovlen
> UIO_FASTIOV
) {
411 iov
= sock_kmalloc(sock
->sk
, iov_size
, GFP_KERNEL
);
417 * Save the user-mode address (verify_iovec will change the
418 * kernel msghdr to use the kernel address space)
421 uaddr
= (void __user
*) msg_sys
.msg_name
;
422 uaddr_len
= &user_msg
->msg_namelen
;
423 err
= verify_compat_iovec(&msg_sys
, iov
, addr
, VERIFY_WRITE
);
428 cmsg_ptr
= (unsigned long) msg_sys
.msg_control
;
429 msg_sys
.msg_flags
= MSG_CMSG_COMPAT
;
431 if (sock
->file
->f_flags
& O_NONBLOCK
)
432 user_flags
|= MSG_DONTWAIT
;
434 err
= sock_recvmsg(sock
, &msg_sys
, total_len
, user_flags
);
441 err
= move_addr_to_user(addr
, msg_sys
.msg_namelen
, uaddr
, uaddr_len
);
445 err
= __put_user(linux_to_solaris_msgflags(msg_sys
.msg_flags
), &user_msg
->msg_flags
);
448 err
= __put_user((unsigned long)msg_sys
.msg_control
- cmsg_ptr
,
449 &user_msg
->msg_controllen
);
456 sock_kfree_s(sock
->sk
, iov
, iov_size
);