[comp.unix.questions] sockets vs. STREAMS, also intermachine networking on SV

daveb@gonzo.UUCP (Dave Brower) (02/10/89)

In <11191@ulysses.homer.nj.att.com> ekrell@hector.UUCP (Eduardo Krell) writes:
>In article <438@laic.UUCP> scott@nova.laic.uucp (Scott Weitzenkamp) writes:
>
>>How does X11 (or X10 for that matter) use STREAMS?
>
>I think they use STREAMS pipes, which work like unix domain sockets.

While in <11196@ulysses.homer.nj.att.com> smb@ulysses.homer.nj.att.com 
(Steven M. Bellovin) quotes my question:
>
>In article <501@gonzo.UUCP>, daveb@gonzo.UUCP (Dave Brower) writes:
>> (Actually, a /dev/spx "stream pipe" sometimes exists, but it is
>> undocumented and unsupported, I think.  It also isn't bidirectional,
>> which is annoying).
>
>Yes it is bidirectional; however, it's only a half-pipe.  More
>precisely, to create a stream pipe one has to open /dev/spx twice, and
>issue the I_FDINSERT on one file descriptor passing down the value of
>the second file descriptor.  (Don't blame me, folks; I didn't invent
>/dev/spx or the ioctl.)
>
>In the distributed system, /dev/spx is mode 644, I believe; it's
>certainly not 666.  I cannot vouch for the security of a system in which
>/dev/spx is made generally available; its primary (sole?) purpose is to
>support parts of RFS, and it's quite possible that the security of RFS
>could be compromised by such a change.  On the other hand, I don't know
>that such a scenario is possible; I've never felt the urge to dig that
>deeply into RFS internals.

I take Steve's questions about permissions on /dev/spx seriously.  
(Some vendors deliver it mode 600 to root, I think.) This makes me
wonder about Eduardo's comments about X.  The main reason you want a
stream pipe is so you can use poll() in your server, which is supposed
to pay attention to a number of connected clients. Now SVR3 has no
generically supported method for doing this as far as I can tell. 
/dev/spx can probably be coerced into working, but there seem to be
security implications.  Is there a reasonable answer, or are we really
supposed to wait for SVR4?

I stand corrected by Steve and Doug Gwyn who also corrected my
understanding of /dev/spx bidirectionality.  I will claim that lacking
any documentation (or a source license for the stuff) it is kind of hard
to tell!

Lacking documentation and source, can someone answer these questions
about /dev/spx? Imagine a server has opened /dev/spx, and arranged for
the creation of  a device file for the returned minor device.  Client
programs want to talk to the server by opening that device file.

1. What happens if two client programs try to open the same minor device?
   Would they get a multiplexed channel with a server on the other end,
   or would the second fail?  (By comparison FIFO files multiplex, but
   psuedo-tty's fail, I think).

2. If the server is sitting in a poll() on a stream pipe, and the client
   on the other end dies, does the poll return or does it hang?  If it
   hangs, what can you do to recover?

3. What _does_ RFS use /dev/spx for?  How does it arrange 
   rendevouz addresses?

Thanks,

-dB
-- 
If life was like the movies, the music would match the picture.

{sun,mtxinu,hoptoad}!rtech!gonzo!daveb		daveb@gonzo.uucp

ekrell@hector.UUCP (Eduardo Krell) (02/11/89)

In article <506@gonzo.UUCP> daveb@gonzo.UUCP (Dave Brower) writes:

>The main reason you want a
>stream pipe is so you can use poll() in your server, which is supposed
>to pay attention to a number of connected clients. Now SVR3 has no
>generically supported method for doing this as far as I can tell. 

I don't understand your question. The poll() system call in SVR3 works
like BSD's select() except that you can only apply it to STREAMS
file descriptors. A file descriptor obtained by opening a (full
duplex) connection through /dev/spx IS a STREAMS file descriptor.
    
Eduardo Krell                   AT&T Bell Laboratories, Murray Hill, NJ

UUCP: {att,decvax,ucbvax}!ulysses!ekrell  Internet: ekrell@ulysses.att.com

smb@ulysses.homer.nj.att.com (Steven M. Bellovin) (02/11/89)

In article <506@gonzo.UUCP>, daveb@gonzo.UUCP (Dave Brower) writes:
> The main reason you want a
> stream pipe is so you can use poll() in your server, which is supposed
> to pay attention to a number of connected clients.

Also to use I_SENDFD, to hand off file descriptors.  That's not a trivial
point; see below.

> [about security concerns]  Is there a reasonable answer, or are we really
> supposed to wait for SVR4?

I'm afraid I just don't know; when I was using /dev/spx, I was doing research
and prototype development; it was enough for me to wave my hands and say
``assume a stream pipe''...  I wouldn't release production code with that
sort of attitude towards security, but I wasn't working on anything releasable.

> I will claim that lacking
> any documentation (or a source license for the stuff) it is kind of hard
> to tell!

Yes.  /dev/spx was never intended as a standard, supported feature; in
fact, in some sense you're not supposed to use it, any more than you're
supposed to call _doprnt() directly.  It's considered an internal interface
in SVR3 that may go away without notice at any time -- like if they introduce
real stream pipes.

> Lacking documentation and source, can someone answer these questions
> about /dev/spx? Imagine a server has opened /dev/spx, and arranged for
> the creation of  a device file for the returned minor device.  Client
> programs want to talk to the server by opening that device file.
> 
> 1. What happens if two client programs try to open the same minor device?
>    Would they get a multiplexed channel with a server on the other end,
>    or would the second fail?  (By comparison FIFO files multiplex, but
>    psuedo-tty's fail, I think).

It can't happen if the proper protocol is used.  You're supposed to open
/dev/spx only, and it's a ``clone device'' -- a driver that returns a
minor device that isn't in use.  In fact, as distributed I don't believe
there are any named minor devices.

FIFOs don't multiplex in the sense of getting a distinguishable connection
on the server end; anyone who opens a FIFO can read data written by the
server.  There's no way to direct it to just the proper consumer.  Nor
can the server distinguish between data written by multiple clients.

> 2. If the server is sitting in a poll() on a stream pipe, and the client
>    on the other end dies, does the poll return or does it hang?  If it
>    hangs, what can you do to recover?

An M_HANGUP message is sent upstream.

> 3. What _does_ RFS use /dev/spx for?  How does it arrange 
>    rendevouz addresses?

Let me warn you here, this is going to be a bit weird....

The server creates a stream pipe by doing two opens of /dev/spx, and then
an I_FDINSERT.  It then uses fstat() to determine the major and minor device
numbers of one end of the pipe, and uses mknod() to create another name for
one end of the pipe.  The server can then sit there poll()ing the other end
(i.e., the anonymous end).

Have your eyes popped back in yet?  It gets worse.

To contact the server, a client also creates a stream pipe, via /dev/spx and
I_FDINSERT.  It then opens the named portion of the server's pipe, and
uses I_SENDFD to hand off its file descriptor for one end of its pipe to
the server.  It then closes the fd handed off, and also the fd to the
named pipe; it retains only one fd for its anonymous stream pipe.  The
named pipe is not used for any data transfer; it's not a multiplexing
connection, there's no way to differentiate between different opens, one
can't even associate text with a transmitted file descriptor, etc.

The above description is a bit sketchy; I gave the same explanation, in
somewhat more detail, in my ``Session Tty Manager'' paper in the S.F.
Usenix proceedings.  I refer you to it.

It's obvious, I think, why this interface was never documented.  It's
ugly, cumbersome, usable only by root, limited in the number of stream
pipes and servers available (only 256 minor devices, after all), etc.
SVR4 will have much nicer ways to do the same thing, but I don't know if
I'm allowed to discuss just what they are.  I have no idea if /dev/spx
will still be present; it will be obsolete, and was never guaranteed, but
of course it was used, and compatibility is a major concern.

The ability to pass file descriptors around is very powerful.  4.2bsd has
a similar feature -- the ``access rights'' on UNIX domain sockets are in
fact file descriptors.  It was rather buggy in 4.2, but I believe it works
well in 4.3; however, I don't know of any Berkeley code that actually uses
it.

You should also check out the Presotto and Ritche paper ``Interprocess
Communication in the 8th Edition UNIX System'', in the proceedings of the
Portland (Summer '85) Usenix conference; they describe lots of other things
to do when you can pass around file descriptors.  For example, you can
have a ``connection server''; it makes calls for you, using whatever weird
conventions your network or autodialers want, and pass back the file
descriptor.  If you get a new network, or a new type of dialer, only
one process need be changed.

		--Steve Bellovin

daveb@gonzo.UUCP (Dave Brower) (02/12/89)

In <11199@ulysses.homer.nj.att.com> ekrell@hector.UUCP 
(Eduardo Krell) wonders:
>In article <506@gonzo.UUCP> daveb@gonzo.UUCP (Dave Brower) writes:
>
>>The main reason you want a
>>stream pipe is so you can use poll() in your server, which is supposed
>>to pay attention to a number of connected clients. Now SVR3 has no
>>generically supported method for doing this as far as I can tell. 
>
>I don't understand your question. The poll() system call in SVR3 works
>like BSD's select() except that you can only apply it to STREAMS
>file descriptors. A file descriptor obtained by opening a (full
>duplex) connection through /dev/spx IS a STREAMS file descriptor.

I understand that fine, but it is not the issue.

The point is that generic SVR3 does not, by default, contain a usable
local IPC mechanism for use by a server supporting multiple simultaneous
connections.  

You only have a workable system for _local_ IPC if the system happens to
have TLI and a _networking_ device.  Both are optional, and the latter
often comes from a third party vendor.  And you probably pay a real
performance penalty going through all the networking code that you
really don't need.  There are no documented local stream devices,
despite the /dev/tivc so prominantly displayed in the "Network
Programmer's Guide."

/dev/spx isn't a documented, supported device you can count on having
around, and it has some severe limitations if it is available.  You need
to run your server as root and can only get 255 channels open at the
same time.  

On a stripped system the only choice is /dev/spx, which is a kludge. And
if it's really stripped (no/dev/spx), then you seem to be SOL.  And it's
reasonable to drop spx becuase it is only supposed to be used by RFS,
and without networking, why carry it around?

All of which is a rude awakening when you start looking at the reality
of the available SVR3 environments.  Despite the license flap, you can't
count on being able to get there from here.

Heck, even AIX has had usable sockets for a year or so.

Frustratedly,
-dB
-- 
"Godot will have SVR4 when he comes."	- Estragon
{sun,mtxinu,amdahl,hoptoad}!rtech!gonzo!daveb	daveb@gonzo.uucp

ka@june.cs.washington.edu (Kenneth Almquist) (02/15/89)

> The point is that generic SVR3 does not, by default, contain a usable
> local IPC mechanism for use by a server supporting multiple simultaneous
> connections.

Local IPC was added to AT&T UNIX before the first release of System V.
See msgget(2), msgop(2), and msgctl(2).  There is nothing along the
lines of "select" for message queues, but you don't need it.  The scheme
works like this:

- When the server starts up, it calls msgget to create a message queue
  with a known key.  It then reads and processes requests from this queue.
  Each request includes a message queue identifier indicating where the
  response to the request should be sent.

- When a client wants to start a conversation, it calls msgget to obtain
  the message queue identifier of the server's message queue.  It then
  calls msgget again with a key of IPC_PRIVATE to create a queue to accept
  responses.  The message queue identifier of this latter queue is included
  in all requests to the server.

- When the client is done talking with the server, it should call msgctl
  with the IPC_RMID command to remove the message queue.

- Clients which terminate abnormally may leave message queues around, so
  you want to remove message queues with a key of IPC_PRIVATE which have
  not been used for a while.  You can use the ipcs command to identify
  these, and either remove them manually or have a daemon remove them
  periodically.  You can determine when a message queue was last used
  using the -t option of ipcs(2) or the IPC_STAT command of msgctl(2).

If you later decide to allow the server to be on a different machine than
the client, you can write a pair of stub processes to forward requests
across the network in a manner that's transparent to the client and the
server.
				Kenneth Almquist