int do_getsockname(message *dev_m_in, message *dev_m_out) { int minor; int rc; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_getsockname() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); /* Unconditionally send the address we have assigned to this socket. * The POSIX standard doesn't say what to do if the address * hasn't been set. If the address isn't currently set, then * the user will get NULL bytes. Note: libc depends on this * behavior. */ rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &(uds_fd_table[minor].addr), sizeof(struct sockaddr_un)); return rc ? EIO : OK; }
int do_shutdown(message *dev_m_in, message *dev_m_out) { int minor; int rc, how; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_shutdown() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); if (uds_fd_table[minor].type != SOCK_STREAM && uds_fd_table[minor].type != SOCK_SEQPACKET) { /* socket must be a connection oriented socket */ return EINVAL; } if (uds_fd_table[minor].peer == -1) { /* shutdown(2) is only valid for connected sockets */ if (uds_fd_table[minor].err == ECONNRESET) { return ECONNRESET; } else { return ENOTCONN; } } /* get the 'how' parameter from the process */ rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &how, sizeof(int)); if (rc != OK) { return EIO; } switch (how) { case SHUT_RD: /* take away read permission */ uds_fd_table[minor].mode &= ~S_IRUSR; break; case SHUT_WR: /* take away write permission */ uds_fd_table[minor].mode &= ~S_IWUSR; break; case SHUT_RDWR: /* completely shutdown */ uds_fd_table[minor].mode = 0; break; default: /* the 'how' parameter is invalid */ return EINVAL; } return OK; }
int do_setsockopt_rcvbuf(message *dev_m_in, message *dev_m_out) { int minor; int rc; size_t rcvbuf; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_setsockopt_rcvbuf() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &rcvbuf, sizeof(size_t), D); if (rc != OK) { return EIO; } if (rcvbuf > PIPE_BUF) { /* The send buffer is limited to 32K at the moment. */ return ENOSYS; } /* There is no way to reduce the send buffer, do we have to * let this call fail for smaller buffers? */ return OK; }
int do_getsockopt_sotype(message *dev_m_in, message *dev_m_out) { int minor; int rc; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_getsockopt_sotype() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); if (uds_fd_table[minor].type == -1) { /* the type hasn't been set yet. instead of returning an * invalid type, we fail with EINVAL */ return EINVAL; } rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &(uds_fd_table[minor].type), sizeof(int)); return rc ? EIO : OK; }
int do_socket(message *dev_m_in, message *dev_m_out) { int rc; int minor; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_socket() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); /* see if this socket already has a type */ if (uds_fd_table[minor].type != -1) { /* socket type can only be set once */ return EINVAL; } /* get the requested type */ rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &(uds_fd_table[minor].type), sizeof(int)); if (rc != OK) { /* something went wrong and we couldn't get the type */ return EIO; } /* validate the type */ switch (uds_fd_table[minor].type) { case SOCK_STREAM: case SOCK_DGRAM: case SOCK_SEQPACKET: /* the type is one of the 3 valid socket types */ return OK; default: /* if the type isn't one of the 3 valid socket * types, then it must be invalid. */ /* set the type back to '-1' (no type set) */ uds_fd_table[minor].type = -1; return EINVAL; } }
PUBLIC int uds_write(message *dev_m_in, message *dev_m_out) { int bytes; int minor; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] uds_write() call_count=%d\n", uds_minor(dev_m_in), ++call_count); printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->USER_ENDPT, dev_m_in->POSITION); #endif minor = uds_minor(dev_m_in); if (uds_fd_table[minor].state != UDS_INUSE) { /* attempted to close a socket that hasn't been opened -- * something is very wrong :( */ uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->USER_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL); return EINVAL; } /* track the system call we are performing in case it gets cancelled */ uds_fd_table[minor].call_nr = dev_m_in->m_type; uds_fd_table[minor].ioctl = 0; uds_fd_table[minor].syscall_done = 0; /* Update the process endpoint. */ uds_fd_table[minor].endpoint = dev_m_in->USER_ENDPT; /* setup select(2) framework */ uds_fd_table[minor].selecting = 0; /* save I/O Grant info */ uds_fd_table[minor].io_gr = (cp_grant_id_t) dev_m_in->IO_GRANT; uds_fd_table[minor].io_gr_size = dev_m_in->COUNT; bytes = uds_perform_write(minor, dev_m_in->m_source, uds_fd_table[minor].io_gr_size, 0); uds_set_reply(dev_m_out, TASK_REPLY, uds_fd_table[minor].endpoint, uds_fd_table[minor].io_gr, bytes); return bytes; }
int perform_connection(message *dev_m_in, message *dev_m_out, struct sockaddr_un *addr, int minorx, int minory) { /* there are several places were a connection is established. */ /* accept(2), connect(2), uds_status(2), socketpair(2) */ /* This is a helper function to make sure it is done in the */ /* same way in each place with the same validation checks. */ #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] perform_connection() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif /* only connection oriented types are acceptable and only like * types can connect to each other */ if ((uds_fd_table[minorx].type != SOCK_SEQPACKET && uds_fd_table[minorx].type != SOCK_STREAM) || uds_fd_table[minorx].type != uds_fd_table[minory].type) { /* sockets are not in a valid state */ return EINVAL; } /* connect the pair of sockets */ uds_fd_table[minorx].peer = minory; uds_fd_table[minory].peer = minorx; /* Set the address of both sockets */ memcpy(&(uds_fd_table[minorx].addr), addr, sizeof(struct sockaddr_un)); memcpy(&(uds_fd_table[minory].addr), addr, sizeof(struct sockaddr_un)); return OK; }
int do_getsockopt_peercred_old(message *dev_m_in, message *dev_m_out) { int minor; int peer_minor; int rc; struct ucred cred; struct ucred_old cred_old; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_getsockopt_peercred() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); if (uds_fd_table[minor].peer == -1) { if (uds_fd_table[minor].err == ECONNRESET) { uds_fd_table[minor].err = 0; return ECONNRESET; } else { return ENOTCONN; } } peer_minor = uds_fd_table[minor].peer; /* obtain the peer's credentials */ rc = getnucred(uds_fd_table[peer_minor].owner, &cred); if (rc == -1) { /* likely error: invalid endpoint / proc doesn't exist */ return errno; } /* copy to old structure */ cred_old.pid = cred.pid; cred_old.uid = (short) cred.uid; cred_old.gid = (char) cred.gid; rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &cred_old, sizeof(struct ucred_old), D); return rc ? EIO : OK; }
int do_socketpair(message *dev_m_in, message *dev_m_out) { int rc; dev_t minorin; int minorx, minory; struct sockaddr_un addr; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_socketpair() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif /* first ioctl param is the first socket */ minorx = uds_minor(dev_m_in); /* third ioctl param is the minor number of the second socket */ rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &minorin, sizeof(dev_t)); if (rc != OK) { return EIO; } minory = minor(minorin); #if DEBUG == 1 printf("socketpair() %d - %d\n", minorx, minory); #endif /* security check - both sockets must have the same endpoint (owner) */ if (uds_fd_table[minorx].owner != uds_fd_table[minory].owner) { /* we won't allow you to magically connect your socket to * someone elses socket */ return EPERM; } addr.sun_family = AF_UNIX; addr.sun_path[0] = 'X'; addr.sun_path[1] = '\0'; uds_fd_table[minorx].syscall_done = 1; return perform_connection(dev_m_in, dev_m_out, &addr, minorx, minory); }
int do_sendto(message *dev_m_in, message *dev_m_out) { int minor; int rc; struct sockaddr_un addr; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_sendto() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); if (uds_fd_table[minor].type != SOCK_DGRAM) { /* This IOCTL is only for SOCK_DGRAM sockets */ return EINVAL; } rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &addr, sizeof(struct sockaddr_un), D); if (rc != OK) { return EIO; } /* do some basic sanity checks on the address */ if (addr.sun_family != AF_UNIX || addr.sun_path[0] == '\0') { /* bad address */ return EINVAL; } rc = check_perms(minor, &addr); if (rc != OK) { return rc; } memcpy(&(uds_fd_table[minor].target), &addr, sizeof(struct sockaddr_un)); return OK; }
int do_recvfrom(message *dev_m_in, message *dev_m_out) { int minor; int rc; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_recvfrom() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &(uds_fd_table[minor].source), sizeof(struct sockaddr_un)); return rc ? EIO : OK; }
int do_getsockopt_rcvbuf(message *dev_m_in, message *dev_m_out) { int minor; int rc; size_t rcvbuf = PIPE_BUF; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_getsockopt_rcvbuf() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &(rcvbuf), sizeof(size_t), D); return rc ? EIO : OK; }
int do_getpeername(message *dev_m_in, message *dev_m_out) { int minor; int rc; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_getpeername() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); /* check that the socket is connected with a valid peer */ if (uds_fd_table[minor].peer != -1) { int peer_minor; peer_minor = uds_fd_table[minor].peer; /* copy the address from the peer */ rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &(uds_fd_table[peer_minor].addr), sizeof(struct sockaddr_un)); return rc ? EIO : OK; } else { if (uds_fd_table[minor].err == ECONNRESET) { uds_fd_table[minor].err = 0; return ECONNRESET; } else { return ENOTCONN; } } }
PUBLIC int uds_cancel(message *dev_m_in, message *dev_m_out) { int i, j; int minor; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] uds_cancel() call_count=%d\n", uds_minor(dev_m_in), ++call_count); printf("Endpoint: 0x%x\n", dev_m_in->IO_ENDPT); #endif minor = uds_minor(dev_m_in); if (uds_fd_table[minor].state != UDS_INUSE) { /* attempted to close a socket that hasn't been opened -- * something is very wrong :( */ uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL); return EINVAL; } /* Update the process endpoint. */ uds_fd_table[minor].endpoint = dev_m_in->IO_ENDPT; /* setup select(2) framework */ uds_fd_table[minor].selecting = 0; /* the system call was cancelled, so if the socket was suspended * (which is likely the case), then it is not suspended anymore. */ uds_fd_table[minor].suspended = UDS_NOT_SUSPENDED; /* If there is a system call and it isn't complete, roll back */ if (uds_fd_table[minor].call_nr && !uds_fd_table[minor].syscall_done) { if (uds_fd_table[minor].call_nr == DEV_IOCTL_S) { switch (uds_fd_table[minor].ioctl) { case NWIOSUDSACCEPT: /* accept() */ /* partial accept() only changes * uds_fd_table[minorparent].child */ for (i = 0; i < NR_FDS; i++) { if (uds_fd_table[i].child == minor) { uds_fd_table[i].child = -1; } } break; case NWIOSUDSCONN: /* connect() */ /* partial connect() sets addr * and adds minor to server backlog */ for (i = 0; i < NR_FDS; i++) { /* find a socket that is in * use. */ if (uds_fd_table[i].state == UDS_INUSE) { /* see if minor is in * the backlog */ for (j = 0; j < uds_fd_table[i].backlog_size; j++) { if (uds_fd_table[i].backlog[j] == minor) { /* remove from backlog */ uds_fd_table[i].backlog[j] = -1; } } } } /* clear the address */ memset(&(uds_fd_table[minor].addr), '\0', sizeof(struct sockaddr_un)); break; case NWIOSUDSTADDR: /* sendto() */ case NWIOSUDSADDR: /* bind() */ case NWIOGUDSADDR: /* getsockname() */ case NWIOGUDSPADDR: /* getpeername() */ case NWIOSUDSTYPE: /* socket() */ case NWIOSUDSBLOG: /* listen() */ case NWIOSUDSSHUT: /* shutdown() */ case NWIOSUDSPAIR: /* socketpair() */ case NWIOGUDSSOTYPE: /* SO_TYPE */ case NWIOGUDSPEERCRED: /* SO_PEERCRED */ default: /* these are atomic, never suspend, * and can't be cancelled once called */ break; } } /* DEV_READ_S or DEV_WRITE_S don't need to do anything * when cancelled. DEV_OPEN, DEV_REOPEN, DEV_SELECT, * DEV_CLOSE are atomic, never suspend, and can't * be cancelled once called. */ uds_fd_table[minor].syscall_done = 1; } uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, EINTR); return EINTR; }
int do_bind(message *dev_m_in, message *dev_m_out) { int minor; struct sockaddr_un addr; int rc, i; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_bind() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); if ((uds_fd_table[minor].type == -1) || (uds_fd_table[minor].addr.sun_family == AF_UNIX && uds_fd_table[minor].type != SOCK_DGRAM)) { /* the type hasn't been set by do_socket() yet OR attempting * to re-bind() a non-SOCK_DGRAM socket */ return EINVAL; } rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &addr, sizeof(struct sockaddr_un)); if (rc != OK) { return EIO; } /* do some basic sanity checks on the address */ if (addr.sun_family != AF_UNIX) { /* bad family */ return EAFNOSUPPORT; } if (addr.sun_path[0] == '\0') { /* bad address */ return ENOENT; } rc = check_perms(minor, &addr); if (rc != OK) { /* permission denied, socket file doesn't exist, etc. */ return rc; } /* make sure the address isn't already in use by another socket. */ for (i = 0; i < NR_FDS; i++) { if ((uds_fd_table[i].addr.sun_family == AF_UNIX) && !strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path, UNIX_PATH_MAX)) { /* another socket is bound to this sun_path */ return EADDRINUSE; } } /* looks good, perform the bind() */ memcpy(&(uds_fd_table[minor].addr), &addr, sizeof(struct sockaddr_un)); return OK; }
PUBLIC int uds_close(message *dev_m_in, message *dev_m_out) { int minor; message fs_m_in, fs_m_out; int rc; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] uds_close() call_count=%d\n", uds_minor(dev_m_in), ++call_count); printf("Endpoint: 0x%x\n", dev_m_in->IO_ENDPT); #endif minor = uds_minor(dev_m_in); if (uds_fd_table[minor].state != UDS_INUSE) { /* attempted to close a socket that hasn't been opened -- * something is very wrong :( */ uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL); return EINVAL; } /* no need to track the syscall in case of cancellation. close() is * atomic and can't be cancelled. no need to update the endpoint here, * we won't be needing it to kill the socket */ /* if the socket is connected, disconnect it */ if (uds_fd_table[minor].peer != -1) { /* set peer of this peer to -1 */ uds_fd_table[uds_fd_table[minor].peer].peer = -1; /* error to pass to peer */ uds_fd_table[uds_fd_table[minor].peer].err = ECONNRESET; /* if peer was blocked on I/O revive peer */ if (uds_fd_table[uds_fd_table[minor].peer].suspended) { int peer = uds_fd_table[minor].peer; uds_fd_table[peer].ready_to_revive = 1; notify(dev_m_in->m_source); } } if (uds_fd_table[minor].ancillary_data.nfiledes > 0) { clear_fds(minor, &(uds_fd_table[minor].ancillary_data)); } /* Prepare Request to the FS side of PFS */ fs_m_in.m_type = REQ_PUTNODE; fs_m_in.REQ_INODE_NR = uds_fd_table[minor].inode_nr; fs_m_in.REQ_COUNT = 1; /* set the socket back to its original UDS_FREE state */ memset(&(uds_fd_table[minor]), '\0', sizeof(uds_fd_t)); /* Request the removal of the inode from the pipe file system */ rc = fs_putnode(&fs_m_in, &fs_m_out); if (rc != OK) { perror("fs_putnode"); /* likely error: get_block() failed */ return rc; } uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, OK); return OK; }
int do_listen(message *dev_m_in, message *dev_m_out) { int minor; int rc; int backlog_size; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_listen() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); /* ensure the socket has a type and is bound */ if (uds_fd_table[minor].type == -1 || uds_fd_table[minor].addr.sun_family != AF_UNIX) { /* probably trying to call listen() before bind() */ return EINVAL; } /* the two supported types for listen(2) are SOCK_STREAM and * SOCK_SEQPACKET */ if (uds_fd_table[minor].type != SOCK_STREAM && uds_fd_table[minor].type != SOCK_SEQPACKET) { /* probably trying to call listen() with a SOCK_DGRAM */ return EOPNOTSUPP; } /* The POSIX standard doesn't say what to do if listen() has * already been called. Well, there isn't an errno. we silently * let it happen, but if listen() has already been called, we * don't allow the backlog to shrink */ rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &backlog_size, sizeof(int)); if (rc != OK) { return EIO; } if (uds_fd_table[minor].listening == 0) { /* See if backlog_size is between 0 and UDS_SOMAXCONN */ if (backlog_size >= 0 && backlog_size < UDS_SOMAXCONN) { /* use the user provided backlog_size */ uds_fd_table[minor].backlog_size = backlog_size; } else { /* the user gave an invalid size, use * UDS_SOMAXCONN instead */ uds_fd_table[minor].backlog_size = UDS_SOMAXCONN; } } else { /* See if the user is trying to expand the backlog_size */ if (backlog_size > uds_fd_table[minor].backlog_size && backlog_size < UDS_SOMAXCONN) { /* expand backlog_size */ uds_fd_table[minor].backlog_size = backlog_size; } /* Don't let the user shrink the backlog_size (we might * have clients waiting in those slots */ } /* perform listen(2) */ uds_fd_table[minor].listening = 1; return OK; }
PUBLIC int uds_ioctl(message *dev_m_in, message *dev_m_out) { int minor; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] uds_ioctl() call_count=%d\n", uds_minor(dev_m_in), ++call_count); printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->IO_ENDPT, dev_m_in->POSITION); #endif minor = uds_minor(dev_m_in); if (uds_fd_table[minor].state != UDS_INUSE) { /* attempted to close a socket that hasn't been opened -- * something is very wrong :( */ uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL); return EINVAL; } /* track the system call we are performing in case it gets cancelled */ uds_fd_table[minor].call_nr = dev_m_in->m_type; uds_fd_table[minor].ioctl = dev_m_in->COUNT; uds_fd_table[minor].syscall_done = 0; /* setup select(2) framework */ uds_fd_table[minor].selecting = 0; /* update the owner endpoint - yes it's really stored in POSITION */ uds_fd_table[minor].owner = dev_m_in->POSITION; switch (dev_m_in->COUNT) { /* Handle the ioctl(2) command */ case NWIOSUDSCONN: /* connect to a listening socket -- connect() */ return do_connect(dev_m_in, dev_m_out); case NWIOSUDSACCEPT: /* accept an incoming connection -- accept() */ return do_accept(dev_m_in, dev_m_out); case NWIOSUDSBLOG: /* set the backlog_size and put the socket into the * listening state -- listen() */ return do_listen(dev_m_in, dev_m_out); case NWIOSUDSTYPE: /* set the type for this socket (i.e. * SOCK_STREAM, SOCK_DGRAM, etc) -- socket() */ return do_socket(dev_m_in, dev_m_out); case NWIOSUDSADDR: /* set the address for this socket -- bind() */ return do_bind(dev_m_in, dev_m_out); case NWIOGUDSADDR: /* get the address for this socket -- getsockname() */ return do_getsockname(dev_m_in, dev_m_out); case NWIOGUDSPADDR: /* get the address for the peer -- getpeername() */ return do_getpeername(dev_m_in, dev_m_out); case NWIOSUDSSHUT: /* shutdown a socket for reading, writing, or * both -- shutdown() */ return do_shutdown(dev_m_in, dev_m_out); case NWIOSUDSPAIR: /* connect two sockets -- socketpair() */ return do_socketpair(dev_m_in, dev_m_out); case NWIOGUDSSOTYPE: /* get socket type -- getsockopt(SO_TYPE) */ return do_getsockopt_sotype(dev_m_in, dev_m_out); case NWIOGUDSPEERCRED: /* get peer endpoint -- getsockopt(SO_PEERCRED) */ return do_getsockopt_peercred(dev_m_in, dev_m_out); case NWIOSUDSTADDR: /* set target address -- sendto() */ return do_sendto(dev_m_in, dev_m_out); case NWIOGUDSFADDR: /* get from address -- recvfrom() */ return do_recvfrom(dev_m_in, dev_m_out); case NWIOGUDSSNDBUF: /* get the send buffer size -- getsockopt(SO_SNDBUF) */ return do_getsockopt_sndbuf(dev_m_in, dev_m_out); case NWIOSUDSSNDBUF: /* set the send buffer size -- setsockopt(SO_SNDBUF) */ return do_setsockopt_sndbuf(dev_m_in, dev_m_out); case NWIOGUDSRCVBUF: /* get the send buffer size -- getsockopt(SO_SNDBUF) */ return do_getsockopt_rcvbuf(dev_m_in, dev_m_out); case NWIOSUDSRCVBUF: /* set the send buffer size -- setsockopt(SO_SNDBUF) */ return do_setsockopt_rcvbuf(dev_m_in, dev_m_out); case NWIOSUDSCTRL: /* set the control data -- sendmsg() */ return do_sendmsg(dev_m_in, dev_m_out); case NWIOGUDSCTRL: /* set the control data -- recvmsg() */ return do_recvmsg(dev_m_in, dev_m_out); default: /* the IOCTL command is not valid for /dev/uds -- * this happens a lot and is normal. a lot of * libc functions determine the socket type with * IOCTLs. Any not for us simply get a EBADIOCTL * response. */ uds_fd_table[minor].syscall_done = 1; uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, EBADIOCTL); return EBADIOCTL; } }
int do_accept(message *dev_m_in, message *dev_m_out) { int minor; int minorparent; /* minor number of parent (server) */ int minorpeer; int rc, i; struct sockaddr_un addr; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_accept() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif /* Somewhat weird logic is used in this function, so here's an * overview... The minor number is the server's client socket * (the socket to be returned by accept()). The data waiting * for us in the IO Grant is the address that the server is * listening on. This function uses the address to find the * server's descriptor. From there we can perform the * connection or suspend and wait for a connect(). */ minor = uds_minor(dev_m_in); if (uds_fd_table[minor].type != -1) { /* this IOCTL must be called on a 'fresh' socket */ return EINVAL; } /* Get the server's address */ rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &addr, sizeof(struct sockaddr_un)); if (rc != OK) { return EIO; } /* locate server socket */ rc = -1; /* to trap error */ for (i = 0; i < NR_FDS; i++) { if (uds_fd_table[i].addr.sun_family == AF_UNIX && !strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path, UNIX_PATH_MAX) && uds_fd_table[i].listening == 1) { rc = 0; break; } } if (rc == -1) { /* there is no server listening on addr. Maybe someone * screwed up the ioctl()? */ return EINVAL; } minorparent = i; /* parent */ /* we are the parent's child */ uds_fd_table[minorparent].child = minor; /* the peer has the same type as the parent. we need to be that * type too. */ uds_fd_table[minor].type = uds_fd_table[minorparent].type; /* locate peer to accept in the parent's backlog */ minorpeer = -1; /* to trap error */ for (i = 0; i < uds_fd_table[minorparent].backlog_size; i++) { if (uds_fd_table[minorparent].backlog[i] != -1) { minorpeer = uds_fd_table[minorparent].backlog[i]; uds_fd_table[minorparent].backlog[i] = -1; rc = 0; break; } } if (minorpeer == -1) { #if DEBUG == 1 printf("(uds) [%d] {do_accept} suspend\n", minor); #endif /* there are no peers in the backlog, suspend and wait * for some to show up */ uds_fd_table[minor].suspended = UDS_SUSPENDED_ACCEPT; return SUSPEND; } #if DEBUG == 1 printf("(uds) [%d] connecting to %d -- parent is %d\n", minor, minorpeer, minorparent); #endif rc = perform_connection(dev_m_in, dev_m_out, &addr, minor, minorpeer); if (rc != OK) { #if DEBUG == 1 printf("(uds) [%d] {do_accept} connection not performed\n", minor); #endif return rc; } uds_fd_table[minorparent].child = -1; /* if peer is blocked on connect() revive peer */ if (uds_fd_table[minorpeer].suspended) { #if DEBUG == 1 printf("(uds) [%d] {do_accept} revive %d\n", minor, minorpeer); #endif uds_fd_table[minorpeer].ready_to_revive = 1; uds_unsuspend(dev_m_in->m_source, minorpeer); } return OK; }
PUBLIC int uds_select(message *dev_m_in, message *dev_m_out) { int i, bytes; int minor; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] uds_select() call_count=%d\n", uds_minor(dev_m_in), ++call_count); printf("Endpoint: 0x%x\n", dev_m_in->IO_ENDPT); #endif minor = uds_minor(dev_m_in); if (uds_fd_table[minor].state != UDS_INUSE) { /* attempted to close a socket that hasn't been opened -- * something is very wrong :( */ uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, EINVAL); return EINVAL; } /* setup select(2) framework */ uds_fd_table[minor].selecting = 1; uds_fd_table[minor].select_proc = dev_m_in->m_source; /* track the system call we are performing in case it gets cancelled */ uds_fd_table[minor].call_nr = dev_m_in->m_type; uds_fd_table[minor].ioctl = 0; uds_fd_table[minor].syscall_done = 0; /* Can't update the process endpoint here, no info. */ uds_fd_table[minor].sel_ops_in = dev_m_in->IO_ENDPT; uds_fd_table[minor].sel_ops_out = 0; /* check if there is data available to read */ bytes = uds_perform_read(minor, dev_m_in->m_source, 1, 1); if (bytes > 0) { /* there is data in the pipe for us to read */ uds_fd_table[minor].sel_ops_out |= SEL_RD; } else if (uds_fd_table[minor].listening == 1) { /* check for pending connections */ for (i = 0; i < uds_fd_table[minor].backlog_size; i++) { if (uds_fd_table[minor].backlog[i] != -1) { uds_fd_table[minor].sel_ops_out |= SEL_RD; break; } } } /* check if we can write without blocking */ bytes = uds_perform_write(minor, dev_m_in->m_source, PIPE_BUF, 1); if (bytes > 0) { uds_fd_table[minor].sel_ops_out |= SEL_WR; } uds_fd_table[minor].syscall_done = 1; uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, uds_fd_table[minor].sel_ops_out); return uds_fd_table[minor].sel_ops_out; }
int do_recvmsg(message *dev_m_in, message *dev_m_out) { int minor; int rc; struct msg_control msg_ctrl; socklen_t controllen_avail = 0; socklen_t controllen_needed = 0; socklen_t controllen_desired = 0; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_sendmsg() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); #if DEBUG == 1 printf("(uds) [%d] CREDENTIALS {pid:%d,uid:%d,gid:%d}\n", minor, uds_fd_table[minor].ancillary_data.cred.pid, uds_fd_table[minor].ancillary_data.cred.uid, uds_fd_table[minor].ancillary_data.cred.gid); #endif memset(&msg_ctrl, '\0', sizeof(struct msg_control)); /* get the msg_control from the user, it will include the * amount of space the user has allocated for control data. */ rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &msg_ctrl, sizeof(struct msg_control)); if (rc != OK) { return EIO; } controllen_avail = MIN(msg_ctrl.msg_controllen, MSG_CONTROL_MAX); if (uds_fd_table[minor].ancillary_data.nfiledes > 0) { controllen_needed = CMSG_LEN(sizeof(int) * (uds_fd_table[minor].ancillary_data.nfiledes)); } /* if there is room we also include credentials */ controllen_desired = controllen_needed + CMSG_LEN(sizeof(struct ucred)); if (controllen_needed > controllen_avail) { return EOVERFLOW; } rc = recv_fds(minor, &uds_fd_table[minor].ancillary_data, &msg_ctrl); if (rc != OK) { return rc; } if (controllen_desired <= controllen_avail) { rc = recv_cred(minor, &uds_fd_table[minor].ancillary_data, &msg_ctrl); if (rc != OK) { return rc; } } /* send the user the control data */ rc = sys_safecopyto(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &msg_ctrl, sizeof(struct msg_control)); return rc ? EIO : OK; }
PUBLIC int uds_status(message *dev_m_in, message *dev_m_out) { int i, bytes; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] uds_status() call_count=%d\n", uds_minor(dev_m_in), ++call_count); printf("Endpoint: 0x%x | Position 0x%x\n", dev_m_in->IO_ENDPT, dev_m_in->POSITION); #endif for (i = 0; i < NR_FDS; i++) { if (uds_fd_table[i].status_updated == 1) { /* clear the status_updated flag */ uds_fd_table[i].status_updated = 0; uds_fd_table[i].selecting = 0; /* prepare the response */ dev_m_out->m_type = DEV_IO_READY; dev_m_out->DEV_MINOR = i; dev_m_out->DEV_SEL_OPS = uds_fd_table[i].sel_ops_out; return uds_fd_table[i].sel_ops_out; } if (uds_fd_table[i].ready_to_revive == 1) { /* clear the ready to revive flag */ uds_fd_table[i].ready_to_revive = 0; switch (uds_fd_table[i].suspended) { case UDS_SUSPENDED_READ: bytes = uds_perform_read(i, dev_m_in->m_source, uds_fd_table[i].io_gr_size, 0); if (bytes == -1) { uds_set_reply(dev_m_out, DEV_REVIVE, uds_fd_table[i].endpoint, uds_fd_table[i].io_gr, errno); return errno; } else if (bytes == SUSPEND) { dev_m_out->m_type = DEV_NO_STATUS; return OK; } else { uds_fd_table[i].suspended = UDS_NOT_SUSPENDED; uds_set_reply(dev_m_out, DEV_REVIVE, uds_fd_table[i].endpoint, uds_fd_table[i].io_gr, bytes); return bytes; } case UDS_SUSPENDED_WRITE: bytes = uds_perform_write(i, dev_m_in->m_source, uds_fd_table[i].io_gr_size, 0); if (bytes == -1) { uds_set_reply(dev_m_out, DEV_REVIVE, uds_fd_table[i].endpoint, uds_fd_table[i].io_gr, errno); return errno; } else if (bytes == SUSPEND) { dev_m_out->m_type = DEV_NO_STATUS; return OK; } else { uds_fd_table[i].suspended = UDS_NOT_SUSPENDED; uds_set_reply(dev_m_out, DEV_REVIVE, uds_fd_table[i].endpoint, uds_fd_table[i].io_gr, bytes); return bytes; } case UDS_SUSPENDED_CONNECT: case UDS_SUSPENDED_ACCEPT: /* In both cases, the process * that send the notify() * already performed the connection. * The only thing to do here is * unblock. */ uds_fd_table[i].suspended = UDS_NOT_SUSPENDED; uds_set_reply(dev_m_out, DEV_REVIVE, uds_fd_table[i].endpoint, uds_fd_table[i].io_gr, OK); return OK; default: continue; } } } dev_m_out->m_type = DEV_NO_STATUS; return OK; }
int do_connect(message *dev_m_in, message *dev_m_out) { int minor, child; struct sockaddr_un addr; int rc, i, j; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_connect() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); /* only connection oriented sockets can connect */ if (uds_fd_table[minor].type != SOCK_STREAM && uds_fd_table[minor].type != SOCK_SEQPACKET) { return EINVAL; } if (uds_fd_table[minor].peer != -1) { /* socket is already connected */ return EISCONN; } rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &addr, sizeof(struct sockaddr_un)); if (rc != OK) { return EIO; } rc = check_perms(minor, &addr); if (rc != OK) { /* permission denied, socket file doesn't exist, etc. */ return rc; } /* look for a socket of the same type that is listening on the * address we want to connect to */ for (i = 0; i < NR_FDS; i++) { if (uds_fd_table[minor].type == uds_fd_table[i].type && uds_fd_table[i].listening && uds_fd_table[i].addr.sun_family == AF_UNIX && !strncmp(addr.sun_path, uds_fd_table[i].addr.sun_path, UNIX_PATH_MAX)) { if ((child = uds_fd_table[i].child) != -1) { /* the server is blocked on accept(2) -- * perform connection to the child */ rc = perform_connection(dev_m_in, dev_m_out, &addr, minor, child); if (rc == OK) { uds_fd_table[i].child = -1; #if DEBUG == 1 printf("(uds) [%d] {do_connect} revive %d\n", minor, child); #endif /* wake the parent (server) */ uds_fd_table[child].ready_to_revive = 1; uds_unsuspend(dev_m_in->m_source, child); } return rc; } else { #if DEBUG == 1 printf("(uds) [%d] adding to %d's backlog\n", minor, i); #endif /* tell the server were waiting to be served */ /* look for a free slot in the backlog */ rc = -1; /* to trap error */ for (j = 0; j < uds_fd_table[i].backlog_size; j++) { if (uds_fd_table[i].backlog[j] == -1) { uds_fd_table[i].backlog[j] = minor; rc = 0; break; } } if (rc == -1) { /* backlog is full */ break; } /* see if the server is blocked on select() */ if (uds_fd_table[i].selecting == 1) { /* if the server wants to know * about data ready to read and * it doesn't know about it * already, then let the server * know we have data for it. */ if ((uds_fd_table[i].sel_ops_in & SEL_RD) && !(uds_fd_table[i].sel_ops_out & SEL_RD)) { uds_fd_table[i].sel_ops_out |= SEL_RD; uds_fd_table[i].status_updated = 1; uds_unsuspend( dev_m_in->m_source, i); } } /* we found our server */ uds_fd_table[minor].peer = i; /* set the address */ memcpy(&(uds_fd_table[minor].addr), &addr, sizeof(struct sockaddr_un)); break; } } } if (uds_fd_table[minor].peer == -1) { /* could not find another open socket listening on the * specified address with room in the backlog */ return ECONNREFUSED; } #if DEBUG == 1 printf("(uds) [%d] {do_connect} suspend\n", minor); #endif /* suspend until the server side completes the connection with accept() */ uds_fd_table[minor].suspended = UDS_SUSPENDED_CONNECT; return SUSPEND; }
int do_sendmsg(message *dev_m_in, message *dev_m_out) { int minor, peer, rc, i; struct msg_control msg_ctrl; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] do_sendmsg() call_count=%d\n", uds_minor(dev_m_in), ++call_count); #endif minor = uds_minor(dev_m_in); memset(&msg_ctrl, '\0', sizeof(struct msg_control)); rc = sys_safecopyfrom(VFS_PROC_NR, (cp_grant_id_t) dev_m_in->IO_GRANT, (vir_bytes) 0, (vir_bytes) &msg_ctrl, sizeof(struct msg_control)); if (rc != OK) { return EIO; } /* locate peer */ peer = -1; if (uds_fd_table[minor].type == SOCK_DGRAM) { if (uds_fd_table[minor].target.sun_path[0] == '\0' || uds_fd_table[minor].target.sun_family != AF_UNIX) { return EDESTADDRREQ; } for (i = 0; i < NR_FDS; i++) { /* look for a SOCK_DGRAM socket that is bound on * the target address */ if (uds_fd_table[i].type == SOCK_DGRAM && uds_fd_table[i].addr.sun_family == AF_UNIX && !strncmp(uds_fd_table[minor].target.sun_path, uds_fd_table[i].addr.sun_path, UNIX_PATH_MAX)){ peer = i; break; } } if (peer == -1) { return ENOENT; } } else { peer = uds_fd_table[minor].peer; if (peer == -1) { return ENOTCONN; } } #if DEBUG == 1 printf("(uds) [%d] sendmsg() -- peer=%d\n", minor, peer); #endif /* note: it's possible that there is already some file * descriptors in ancillary_data if the peer didn't call * recvmsg() yet. That's okay. The receiver will * get the current file descriptors plus the new ones. */ rc = msg_control_read(&msg_ctrl, &uds_fd_table[peer].ancillary_data, minor); if (rc != OK) { return rc; } return send_fds(minor, &uds_fd_table[peer].ancillary_data); }
PUBLIC int uds_open(message *dev_m_in, message *dev_m_out) { message fs_m_in, fs_m_out; struct ucred ucred; int rc, i; int minor; #if DEBUG == 1 static int call_count = 0; printf("(uds) [%d] uds_open() call_count=%d\n", uds_minor(dev_m_in), ++call_count); printf("Endpoint: 0x%x\n", dev_m_in->IO_ENDPT); #endif /* * Find a slot in the descriptor table for the new descriptor. * The index of the descriptor in the table will be returned. * Subsequent calls to read/write/close/ioctl/etc will use this * minor number. The minor number must be different from the * the /dev/uds device's minor number (currently 0). */ minor = -1; /* to trap error */ for (i = 1; i < NR_FDS; i++) { if (uds_fd_table[i].state == UDS_FREE) { minor = i; break; } } if (minor == -1) { /* descriptor table full */ uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, ENFILE); return ENFILE; } /* * We found a slot in uds_fd_table, now initialize the descriptor */ /* mark this one as 'in use' so that it doesn't get assigned to * another socket */ uds_fd_table[minor].state = UDS_INUSE; /* track the system call we are performing in case it gets cancelled */ uds_fd_table[minor].call_nr = dev_m_in->m_type; uds_fd_table[minor].ioctl = 0; uds_fd_table[minor].syscall_done = 0; /* set the socket owner */ uds_fd_table[minor].owner = dev_m_in->IO_ENDPT; uds_fd_table[minor].endpoint = dev_m_in->IO_ENDPT; /* setup select(2) framework */ uds_fd_table[minor].selecting = 0; uds_fd_table[minor].select_proc = 0; uds_fd_table[minor].sel_ops_in = 0; uds_fd_table[minor].sel_ops_out = 0; uds_fd_table[minor].status_updated = 0; /* initialize the data pointer (pos) to the start of the PIPE */ uds_fd_table[minor].pos = 0; /* the PIPE is initially empty */ uds_fd_table[minor].size = 0; /* the default for a new socket is to allow reading and writing. * shutdown(2) will remove one or both flags. */ uds_fd_table[minor].mode = S_IRUSR|S_IWUSR; /* In libc socket(2) sets this to the actual value later with the * NWIOSUDSTYPE ioctl(). */ uds_fd_table[minor].type = -1; /* Clear the backlog by setting each entry to -1 */ for (i = 0; i < UDS_SOMAXCONN; i++) { /* initially no connections are pending */ uds_fd_table[minor].backlog[i] = -1; } memset(&uds_fd_table[minor].ancillary_data, '\0', sizeof(struct ancillary)); for (i = 0; i < OPEN_MAX; i++) { uds_fd_table[minor].ancillary_data.fds[i] = -1; } /* default the size to UDS_SOMAXCONN */ uds_fd_table[minor].backlog_size = UDS_SOMAXCONN; /* the socket isn't listening for incoming connections until * listen(2) is called */ uds_fd_table[minor].listening = 0; /* initially the socket is not connected to a peer */ uds_fd_table[minor].peer = -1; /* there isn't a child waiting to be accept(2)'d */ uds_fd_table[minor].child = -1; /* initially the socket is not bound or listening on an address */ memset(&(uds_fd_table[minor].addr), '\0', sizeof(struct sockaddr_un)); memset(&(uds_fd_table[minor].source), '\0', sizeof(struct sockaddr_un)); memset(&(uds_fd_table[minor].target), '\0', sizeof(struct sockaddr_un)); /* Initially the socket isn't suspended. */ uds_fd_table[minor].suspended = UDS_NOT_SUSPENDED; /* and the socket doesn't have an I/O grant initially */ uds_fd_table[minor].io_gr = (cp_grant_id_t) 0; /* since there is no I/O grant it effectively has no size either */ uds_fd_table[minor].io_gr_size = 0; /* The process isn't suspended so we don't flag it as revivable */ uds_fd_table[minor].ready_to_revive = 0; /* get the effective user id and effective group id from the endpoint */ /* this is needed in the REQ_NEWNODE request to PFS. */ rc = getnucred(uds_fd_table[minor].endpoint, &ucred); if (rc == -1) { /* roll back the changes we made to the descriptor */ memset(&(uds_fd_table[minor]), '\0', sizeof(uds_fd_t)); /* likely error: invalid endpoint / proc doesn't exist */ uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, errno); return errno; } /* Prepare Request to the FS side of PFS */ fs_m_in.m_type = REQ_NEWNODE; fs_m_in.REQ_MODE = I_NAMED_PIPE; fs_m_in.REQ_DEV = NO_DEV; fs_m_in.REQ_UID = ucred.uid; fs_m_in.REQ_GID = ucred.gid; /* Request a new inode on the pipe file system */ rc = fs_newnode(&fs_m_in, &fs_m_out); if (rc != OK) { /* roll back the changes we made to the descriptor */ memset(&(uds_fd_table[minor]), '\0', sizeof(uds_fd_t)); /* likely error: get_block() failed */ uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, errno); return errno; } /* Process the response */ uds_fd_table[minor].inode_nr = fs_m_out.RES_INODE_NR; /* prepare the reply */ uds_fd_table[minor].syscall_done = 1; uds_set_reply(dev_m_out, TASK_REPLY, dev_m_in->IO_ENDPT, (cp_grant_id_t) dev_m_in->IO_GRANT, minor); return minor; }