Correctly emulate linux_connect. For nonblocking sockets, the behavior
is to return EINPROGRESS, EALREADY, (so_error ONCE), EISCONN. Certain linux applications rely on the so_error (normally 0) being returned in order to operate properly. Tested by: Thomas Moestl <tmoestl@gmx.net>
This commit is contained in:
parent
6e8fd9ef89
commit
0bdef26329
@ -40,7 +40,9 @@
|
|||||||
#include <sys/systm.h>
|
#include <sys/systm.h>
|
||||||
#include <sys/sysproto.h>
|
#include <sys/sysproto.h>
|
||||||
#include <sys/fcntl.h>
|
#include <sys/fcntl.h>
|
||||||
|
#include <sys/file.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/socketvar.h>
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
@ -392,6 +394,8 @@ linux_connect(struct proc *p, struct linux_connect_args *args)
|
|||||||
caddr_t name;
|
caddr_t name;
|
||||||
int namelen;
|
int namelen;
|
||||||
} */ bsd_args;
|
} */ bsd_args;
|
||||||
|
struct socket *so;
|
||||||
|
struct file *fp;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
#ifdef __alpha__
|
#ifdef __alpha__
|
||||||
@ -405,59 +409,25 @@ linux_connect(struct proc *p, struct linux_connect_args *args)
|
|||||||
bsd_args.name = (caddr_t)linux_args.name;
|
bsd_args.name = (caddr_t)linux_args.name;
|
||||||
bsd_args.namelen = linux_args.namelen;
|
bsd_args.namelen = linux_args.namelen;
|
||||||
error = connect(p, &bsd_args);
|
error = connect(p, &bsd_args);
|
||||||
if (error == EISCONN) {
|
if (error != EISCONN)
|
||||||
/*
|
return (error);
|
||||||
* Linux doesn't return EISCONN the first time it occurs,
|
|
||||||
* when on a non-blocking socket. Instead it returns the
|
|
||||||
* error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD.
|
|
||||||
*/
|
|
||||||
struct fcntl_args /* {
|
|
||||||
int fd;
|
|
||||||
int cmd;
|
|
||||||
int arg;
|
|
||||||
} */ bsd_fcntl_args;
|
|
||||||
struct getsockopt_args /* {
|
|
||||||
int s;
|
|
||||||
int level;
|
|
||||||
int name;
|
|
||||||
caddr_t val;
|
|
||||||
int *avalsize;
|
|
||||||
} */ bsd_getsockopt_args;
|
|
||||||
void *status, *statusl;
|
|
||||||
int stat, statl = sizeof stat;
|
|
||||||
caddr_t sg;
|
|
||||||
|
|
||||||
/* Check for non-blocking */
|
/*
|
||||||
bsd_fcntl_args.fd = linux_args.s;
|
* Linux doesn't return EISCONN the first time it occurs,
|
||||||
bsd_fcntl_args.cmd = F_GETFL;
|
* when on a non-blocking socket. Instead it returns the
|
||||||
bsd_fcntl_args.arg = 0;
|
* error getsockopt(SOL_SOCKET, SO_ERROR) would return on BSD.
|
||||||
error = fcntl(p, &bsd_fcntl_args);
|
*/
|
||||||
if (error == 0 && (p->p_retval[0] & O_NONBLOCK)) {
|
error = holdsock(p->p_fd, linux_args.s, &fp);
|
||||||
sg = stackgap_init();
|
if (error)
|
||||||
status = stackgap_alloc(&sg, sizeof stat);
|
return (error);
|
||||||
statusl = stackgap_alloc(&sg, sizeof statusl);
|
error = EISCONN;
|
||||||
|
if (fp->f_flag & FNONBLOCK) {
|
||||||
if ((error = copyout(&statl, statusl, sizeof statl)))
|
so = (struct socket *)fp->f_data;
|
||||||
return (error);
|
if ((u_int)so->so_emuldata != 0)
|
||||||
|
error = so->so_error;
|
||||||
bsd_getsockopt_args.s = linux_args.s;
|
so->so_emuldata = (void *)1;
|
||||||
bsd_getsockopt_args.level = SOL_SOCKET;
|
|
||||||
bsd_getsockopt_args.name = SO_ERROR;
|
|
||||||
bsd_getsockopt_args.val = status;
|
|
||||||
bsd_getsockopt_args.avalsize = statusl;
|
|
||||||
|
|
||||||
error = getsockopt(p, &bsd_getsockopt_args);
|
|
||||||
if (error)
|
|
||||||
return (error);
|
|
||||||
|
|
||||||
if ((error = copyin(status, &stat, sizeof stat)))
|
|
||||||
return (error);
|
|
||||||
|
|
||||||
p->p_retval[0] = stat;
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
fdrop(fp, p);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user