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>
11 #include <linux/smp_lock.h>
13 #include <linux/slab.h>
14 #include <linux/socket.h>
15 #include <linux/file.h>
16 #include <linux/net.h>
17 #include <linux/compat.h>
18 #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 iov
[UIO_FASTIOV
];
301 unsigned char ctl
[sizeof(struct cmsghdr
) + 20];
302 unsigned char *ctl_buf
= ctl
;
303 struct msghdr kern_msg
;
306 if(msghdr_from_user32_to_kern(&kern_msg
, user_msg
))
308 if(kern_msg
.msg_iovlen
> UIO_MAXIOV
)
310 err
= verify_compat_iovec(&kern_msg
, iov
, address
, VERIFY_READ
);
315 if(kern_msg
.msg_controllen
) {
316 struct sol_cmsghdr __user
*ucmsg
= kern_msg
.msg_control
;
317 unsigned long *kcmsg
;
320 if(kern_msg
.msg_controllen
> sizeof(ctl
) &&
321 kern_msg
.msg_controllen
<= 256) {
323 ctl_buf
= kmalloc(kern_msg
.msg_controllen
, GFP_KERNEL
);
327 __get_user(cmlen
, &ucmsg
->cmsg_len
);
328 kcmsg
= (unsigned long *) ctl_buf
;
329 *kcmsg
++ = (unsigned long)cmlen
;
331 if(copy_from_user(kcmsg
, &ucmsg
->cmsg_level
,
332 kern_msg
.msg_controllen
- sizeof(compat_size_t
)))
334 kern_msg
.msg_control
= ctl_buf
;
336 kern_msg
.msg_flags
= solaris_to_linux_msgflags(user_flags
);
339 sock
= sockfd_lookup(fd
, &err
);
341 if (sock
->file
->f_flags
& O_NONBLOCK
)
342 kern_msg
.msg_flags
|= MSG_DONTWAIT
;
343 err
= sock_sendmsg(sock
, &kern_msg
, total_len
);
349 /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */
353 if(kern_msg
.msg_iov
!= iov
)
354 kfree(kern_msg
.msg_iov
);
359 asmlinkage
int solaris_recvmsg(int fd
, struct sol_nmsghdr __user
*user_msg
, unsigned int user_flags
)
361 struct iovec iovstack
[UIO_FASTIOV
];
362 struct msghdr kern_msg
;
363 char addr
[MAX_SOCK_ADDR
];
365 struct iovec
*iov
= iovstack
;
366 struct sockaddr __user
*uaddr
;
367 int __user
*uaddr_len
;
368 unsigned long cmsg_ptr
;
369 int err
, total_len
, len
= 0;
371 if(msghdr_from_user32_to_kern(&kern_msg
, user_msg
))
373 if(kern_msg
.msg_iovlen
> UIO_MAXIOV
)
376 uaddr
= kern_msg
.msg_name
;
377 uaddr_len
= &user_msg
->msg_namelen
;
378 err
= verify_compat_iovec(&kern_msg
, iov
, addr
, VERIFY_WRITE
);
383 cmsg_ptr
= (unsigned long) kern_msg
.msg_control
;
384 kern_msg
.msg_flags
= 0;
387 sock
= sockfd_lookup(fd
, &err
);
389 if (sock
->file
->f_flags
& O_NONBLOCK
)
390 user_flags
|= MSG_DONTWAIT
;
391 err
= sock_recvmsg(sock
, &kern_msg
, total_len
, user_flags
);
398 if(uaddr
!= NULL
&& err
>= 0)
399 err
= move_addr_to_user(addr
, kern_msg
.msg_namelen
, uaddr
, uaddr_len
);
401 err
= __put_user(linux_to_solaris_msgflags(kern_msg
.msg_flags
), &user_msg
->msg_flags
);
403 /* XXX Convert cmsg back into userspace 32-bit format... */
404 err
= __put_user((unsigned long)kern_msg
.msg_control
- cmsg_ptr
,
405 &user_msg
->msg_controllen
);
409 if(kern_msg
.msg_iov
!= iov
)
410 kfree(kern_msg
.msg_iov
);