damonp@daemon.UUCP (Damon Permezel) (09/14/84)
Index: /sys/sys/uipc_usrreq.c 4.2BSD Description: Out of band data transmissions are not supported in the UN*X domain, contrary to documentation. In fact, attempted use of the facility will crash 4.2 as distributed. There were some fixes distributed to prevent the crash (panic mfree freeing free, if I remember correctly), in which case the 'not supported' error is returned for send oob and recieve oob. Repeat-By: Test programme courtesy of Ron Lunde (teklds!ronl). I have modified it somewhat for testing the fixes. #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <signal.h> #include <sys/time.h> #include <fcntl.h> #include <errno.h> int newsock; FILE *pout, *cout; char oob_data[] = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 67890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234 678901234567890123456789012345678901234567890123456789"; main () /* This will demonstrate the out-of-band socket bug */ { static struct sockaddr sock; static struct sockaddr sock2; char * mytime(); int s; int socklen; int amount, i; int rfds, nfound, xfds; char buffer[256]; int size = 256; int pid; struct sigvec vec; extern int get_urg(); static char msg1[] = "An apple a day will keep the PC away"; static char msg2[] = "X"; int res, fd, cmd, arg; extern int errno; sigsetmask(0); vec.sv_handler = get_urg; sigvec(SIGURG,&vec,0); if ((pid = fork()) > 0) { /* The parent */ pout = fopen("parentout","w"); /* sleep long enough to let child create the socket */ sleep(2); if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { fprintf(stderr, "Parent: could not create a socket!\n"); fflush(stderr); exit(1); } strcpy(sock.sa_data, "testsock"); socklen=sizeof(sock); if (connect(s, &sock, socklen) < 0) { fprintf(stderr, "Parent: could not make a connection!\n"); fflush(stderr); exit(1); } printf("Parent: Connected to the socket!\n"); fflush(stdout); fprintf(pout,"Parent: Sending \"%s\" (normally) at %s\n",msg1,mytime()); fflush(pout); amount = send (s, msg1, strlen(msg1), 0); printf("Going to sleep for 3 seconds...\n"); fflush(stdout); sleep(3); for (i=1; i<512; ++i) { amount = send (s, oob_data, i, MSG_OOB); if (amount < 0) { perror("send"); } sleep(3); } kill(pid, 9); /* knock off child */ exit(0); } else { /* The child */ cout = fopen("childout","w"); if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { fprintf(stderr, "Child: could not create a socket!\n"); fflush(stderr); exit(1); } unlink("testsock"); /* in case it's there from the last time this thing was run */ strcpy(sock.sa_data, "testsock"); socklen=sizeof(sock); if (bind(s, &sock, socklen) < 0) { fprintf(stderr, "Child: could not bind a name to a socket!\n"); fflush(stderr); exit(1); } if (listen(s,5) < 0) { fprintf(stderr, "Child: could not listen to a socket!\n"); fflush(stderr); exit(1); } socklen = sizeof(sock2); if ((newsock = accept(s, &sock2, &socklen)) < 0) { fprintf(stderr, "Child: could not accept a connection!\n"); fflush(stderr); exit(1); } printf("Child: Accepted a connection on the socket!\n"); fflush(stdout); /* set the child as the receiver of SIGURG signals */ arg = getpid(); res = fcntl (newsock, F_SETOWN, arg); res = fcntl (newsock, F_GETOWN, 0); if (res != arg) { fprintf(stderr, "Child is not set to receive SIGURG\n"); fflush(stderr); } for (i=0;i<2;i++){ amount = recv (newsock, buffer, size, 0); if (amount < 0) { if (errno == EINTR) { fprintf(cout, "Child: recv was interrupted\n"); fflush(cout); } i--; } else if (amount == 0) { fprintf(cout, "Child: EOF on the socket\n"); fflush(cout); } else { buffer[amount] = '\0'; fprintf(cout, "Child: Received \"%s\" at %s\n", buffer, mytime()); fflush(cout); } } for (;;) pause(); exit(0); } } get_urg() { char *mytime(); char buffer[512]; extern char *sys_errlist[]; int amount; fprintf(cout,"Child: Got SIGURG signal at %s\n", mytime()); amount = recv (newsock, buffer, 256, MSG_OOB); if (amount > 0) { buffer[amount] = '\0'; fprintf(cout, "Child: Received \"%s\" (urg) at %s\n", buffer, mytime()); } else { fprintf(cout, "get_urg: %s\n", sys_errlist[errno]); } fflush(cout); return; } char * mytime() { struct timeval t; struct timezone tz; static char hms[9]; gettimeofday(&t, &tz); strncpy(hms, ctime(&(t.tv_sec))+11, 9); hms[8]='\0'; return(hms); } Fix: Code has been added to uipc_usrreq.c to provide up to MLEN bytes of OOB data. Diffs follow. The line numbers wont make too much sense. ===========/sys/sys/uipc_usrreq.c================ *** /tmp/,RCSt1020162 Fri Sep 14 13:49:37 1984 --- uipc_usrreq.c Wed Aug 22 20:02:21 1984 *************** *** 1,5 /* ! * $Header: uipc_usrreq.c,v 1.2 84/03/30 19:33:53 damonp Exp $ * * $Log: uipc_usrreq.c,v $ * Revision 1.2 84/03/30 19:33:53 damonp --- 1,5 ----- /* ! * $Header: uipc_usrreq.c,v 1.3 84/08/22 20:00:09 damonp Exp $ * * $Log: uipc_usrreq.c,v $ * Revision 1.3 84/08/22 20:00:09 damonp *************** *** 2,7 * $Header: uipc_usrreq.c,v 1.2 84/03/30 19:33:53 damonp Exp $ * * $Log: uipc_usrreq.c,v $ * Revision 1.2 84/03/30 19:33:53 damonp * Fixed a bug that was causing the kernel to loop at network interrupt * level. If a unix domain socket that was listen()ing for connections was --- 2,11 ----- * $Header: uipc_usrreq.c,v 1.3 84/08/22 20:00:09 damonp Exp $ * * $Log: uipc_usrreq.c,v $ + * Revision 1.3 84/08/22 20:00:09 damonp + * Add enhancements to allow OOB data on UNIX domain stream sockets. + * -dap@tek + * * Revision 1.2 84/03/30 19:33:53 damonp * Fixed a bug that was causing the kernel to loop at network interrupt * level. If a unix domain socket that was listen()ing for connections was *************** *** 42,48 /*ARGSUSED*/ uipc_usrreq(so, req, m, nam, rights) ! struct socket *so; int req; struct mbuf *m, *nam, *rights; { --- 46,52 ----- /*ARGSUSED*/ uipc_usrreq(so, req, m, nam, rights) ! register struct socket *so; int req; struct mbuf *m, *nam, *rights; { *************** *** 46,52 int req; struct mbuf *m, *nam, *rights; { ! struct unpcb *unp = sotounpcb(so); register struct socket *so2; int error = 0; --- 50,56 ----- int req; struct mbuf *m, *nam, *rights; { ! struct unpcb *unp = sotounpcb(so), *unp2; register struct socket *so2; int error = 0; *************** *** 221,227 /* END UNIMPLEMENTED HOOKS */ case PRU_RCVOOB: ! break; case PRU_SENDOOB: break; --- 225,251 ----- /* END UNIMPLEMENTED HOOKS */ case PRU_RCVOOB: ! /* ! * recieve out of band data. -dap@tek ! */ ! switch (so->so_type) { ! case SOCK_STREAM: ! if (so->so_oobmark == 0 ! && (so->so_state & SS_RCVATMARK) == 0) { ! error = EINVAL; /* no OOB data to recv */ ! break; ! } ! if (unp->unp_oob == 0) { ! /* ! * no data to recv. must have just ! * recv''d it. Personally, I think both ! * this error and the above should be the ! * same, but I am being compatable with ! * the TCP stuff. -dap@tek ! */ ! error = EWOULDBLOCK; ! break; ! } /* * link the OOB data mbuf to the mbuf we were *************** *** 223,228 case PRU_RCVOOB: break; case PRU_SENDOOB: break; --- 247,268 ----- break; } + /* + * link the OOB data mbuf to the mbuf we were + * passed. the caller will free both these mbufs. + * -dap@tek + */ + m->m_len = 0; + m->m_next = unp->unp_oob; + unp->unp_oob = 0; + break; + + default: + error = EOPNOTSUPP; + break; + } + return (error); + case PRU_SENDOOB: /* * send OOB data. At most MLEN bytes will be sent. *************** *** 224,229 break; case PRU_SENDOOB: break; case PRU_SOCKADDR: --- 264,323 ----- return (error); case PRU_SENDOOB: + /* + * send OOB data. At most MLEN bytes will be sent. + * At most one outstanding request will be allowed. + * -dap@tek + */ + switch (so->so_type) { + case SOCK_STREAM: + if (unp->unp_conn == 0) + panic("uipc 5"); + + so2 = unp->unp_conn->unp_socket; + unp2 = sotounpcb(so2); + + if (unp2->unp_oob) { + /* + * must wait for the preceeding OOB data + * to be handled. -dap@tek + */ + error = EWOULDBLOCK; + break; + } + + /* + * make sure there is at most MLEN bytes of data. + * (this is to place a bound on the amt of mbufs that + * may be wasted if the recv()er never asks for + * OOB data) -dap@tek + */ + { + register int size = 0; + register struct mbuf *n; + + for (n = m; n; n = n->m_next) { + size += n->m_len; + if (size > MLEN) { + error = EMSGSIZE; + break; + } + } + } + if (error) + break; + + unp2->unp_oob = m; + + if ((so2->so_oobmark = so2->so_rcv.sb_cc) == 0) + so2->so_state |= SS_RCVATMARK; + + sohasoutofband(so2); + return (0); /* don't release mbuf chain */ + + default: + error = EOPNOTSUPP; + } break; case PRU_SOCKADDR: *************** *** 282,287 soisdisconnected(unp->unp_socket); unp->unp_socket->so_pcb = 0; m_freem(unp->unp_remaddr); (void) m_free(dtom(unp)); } --- 376,386 ----- soisdisconnected(unp->unp_socket); unp->unp_socket->so_pcb = 0; m_freem(unp->unp_remaddr); + /* + * free any unrecv''d OOB data mbufs. -dap@tek + */ + if (unp->unp_oob) + m_freem(unp->unp_oob); (void) m_free(dtom(unp)); } ===========/sys/h/unpcb.h================ *** /tmp/,RCSt1020230 Fri Sep 14 13:50:51 1984 --- unpcb.h Wed Aug 22 22:41:35 1984 *************** *** 1,5 /* ! * $Header: unpcb.h,v 1.1 83/12/09 13:00:03 avatar Exp $ * * $Log: unpcb.h,v $ * Revision 1.1 83/12/09 13:00:03 avatar --- 1,5 ----- /* ! * $Header: unpcb.h,v 1.2 84/08/22 20:03:16 damonp Exp $ * * $Log: unpcb.h,v $ * Revision 1.2 84/08/22 20:03:16 damonp *************** *** 2,7 * $Header: unpcb.h,v 1.1 83/12/09 13:00:03 avatar Exp $ * * $Log: unpcb.h,v $ * Revision 1.1 83/12/09 13:00:03 avatar * Initial revision * --- 2,11 ----- * $Header: unpcb.h,v 1.2 84/08/22 20:03:16 damonp Exp $ * * $Log: unpcb.h,v $ + * Revision 1.2 84/08/22 20:03:16 damonp + * Add field so that protocol can keep track of OOB data. + * -dap@tek + * * Revision 1.1 83/12/09 13:00:03 avatar * Initial revision * *************** *** 28,33 * by a number of other sockets and may also reference a socket (not * necessarily one which is referencing it). This generates * the need for unp_refs and unp_nextref to be separate fields. */ struct unpcb { struct socket *unp_socket; /* pointer back to socket */ --- 32,39 ----- * by a number of other sockets and may also reference a socket (not * necessarily one which is referencing it). This generates * the need for unp_refs and unp_nextref to be separate fields. + * + * An mbuf ^ has been added to allow OOB data. -dap@tek */ struct unpcb { struct socket *unp_socket; /* pointer back to socket */ *************** *** 36,41 struct unpcb *unp_refs; /* referencing socket linked list */ struct unpcb *unp_nextref; /* link in unp_refs list */ struct mbuf *unp_remaddr; /* address of connected socket */ }; #define sotounpcb(so) ((struct unpcb *)((so)->so_pcb)) --- 42,48 ----- struct unpcb *unp_refs; /* referencing socket linked list */ struct unpcb *unp_nextref; /* link in unp_refs list */ struct mbuf *unp_remaddr; /* address of connected socket */ + struct mbuf *unp_oob; /* out of band data */ }; #define sotounpcb(so) ((struct unpcb *)((so)->so_pcb))