SUMMARY: How to run a program through a telnet port? (long)

From: Tamas Badics (badics@rutcor.rutgers.edu)
Date: Tue Nov 30 1993 - 11:30:56 CST


Here is a summary of the many useful advices I got to the following
questions:

> I'd like to assign a program to a port number on our Sun Sparcserver.
> (Under SunOs 4.1.3) That is if somebody tries telnetting to our
> machine on that port, that program should start right away.
> I tried doing something after reading manuals, but it just doesn't
> want to work.
> Can you tell me exactly what is the right procedure for this task?
> (I think it is quite a simple task and anybody who has done it once
> could answer my question.)
>
> Another thing is how to set the maximum number of users allowed to use
> the same port at the same time.
>
> Also if it is working, it would be nice to find out the person's username
> and address who is logged in through that port. Is it also trivial?
>
> Tamas Badics (sysadm)
> badics@rutcor.rutgers.edu

First of all I'd like to say thanks to everybody who answered. I am
really impressed by the efficiency of the Usenet. I got about 50 answers in
two days with many useful suggestions.

There appears to be two kinds of solutions to this problem.:

(1) The easier - what I finally chose -
is to simply create a user which has a login shell that is the
application I want to run. This could open security holes. (I'd be
interesting in discussing how insecure it is?)

(2a) The more involved - which actually assigns my program to a port -
is done by modifying /etc/services, /etc/inetd.conf. (See the first
reply by Ian MacPhedran.)

(2b) Also there is another way to handle requests coming to ports. One can
write a server program which listens to incoming requests. Some of the
answers below contain perl scripts or C-code fragments for doing this.

My second requirement was to know the user's terminal type. This is
done autmatically with approach (1), and some further programming
is required for (2ab).

Thank you all for the help.

                Tamas Badics

===============================================================
From: Ian MacPhedran <Ian_MacPhedran@engr.usask.ca>

1) edit /etc/services and include a line to define the service you
   want to provide. You need the name you want to use for it, the
   port number, and (in this case) that it's using tcp.
1a) if you're using NIS, make the services map to propagate this change.
2) edit /etc/inetd.conf and include a line for the service you just
   added:
servicename stream tcp nowait user /path/to/program arguments
3) restart inetd - this can be done by "kill -HUP pid" (where pid is the
   process id of inetd)

> Another thing is how to set the maximum number of users allowed to use
> the same port at the same time.

Have your program keep count of how many times it has been invoked and
store this number in a shared manner (e.g. shared memory, status file,
...). Remember to decrement this count when the program exits.

> Also if it is working, it would be nice to find out the person's username
> and address who is logged in through that port. Is it also trivial?

The address should be fairly easy to get with getpeername. The username is
more difficult to get, and the calling host has to cooperate on this (i.e.
the remote host has to be running something like authd).For the most part,
you won't be able to determine the username.

I would suggest that you get the latest copy of Wietse Venema's log_tcp.
It is available through anonymous ftp from
ftp.cert.org:/pub/tools/tcp_wrappers

It's really a security tool, but it has code to get the information you're
looking for (remote host/username). It would be a good place to start if you
want some real code to look at.

Ian.
----------------------------------------------------------------------------
Ian MacPhedran, Engineering Computer Centre, University of Saskatchewan.
2B13 Engineering Building, U. of S. Campus, Saskatoon, Sask., CANADA S7N 0W0
macphed@dvinci.USask.CA (306) 966-4832 Ian_MacPhedran@engr.USask.CA

==================================================================
From: Ian MacPhedran <Ian_MacPhedran@engr.usask.ca>

What most people do to set up services available through
telnet is create a user which has a login shell that is the application you
want to run. This could open security holes. (For that matter, using the
other approach could as well, but this is somewhat less secure.)

     ++ no password, else you will have to publish the password
     V
myapp::10000:10000:The Application:/tmp/myapp:/usr/local/bin/app
        ^ ^ ^
        | | the actual application +++++++++++++++++
        | |
       +++++++++++ uid and gid not used by anyone else in your NISdomain

Without a password (or with a global known password) this can open some
holes in your security.

Then you login as that user and the application starts right away.

Place a .hushlogin file in myapp's home directory to get rid of the login
messages.
As to how big the hole is depends on a lot of things - for the most part,
it should be safe enough, however, having a username without a password is
something I tend to shy away from - perhaps I'm just a little too paranoid
:-)

Ian.

===============================================================
From: tcasey@mv.us.adobe.com (Tim Casey)

I will tell you the rough outline of the code I use to set up the
port on 6969. The limit for this port, because of the design of the
server is 254 connections. This is with stderr, stdin, stdout all closed:

if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        error and exit
}
bzero((char *) & serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port); /* port is 6969 in case of IGS */
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,(char *) & opt, sizeof(opt));

if(bind(fd,(struct sockaddr *) & serv_addr, sizeof(serv_addr)) < 0) {
    error and exit
}
fcntl(fd, F_SETFL, FNDELAY);
listen(fd, 5);

return fd;

===============================================================
From: winger@stone.sewp.nasa.gov (Dan Winger -- SEWP)

1) Make the entry into your "/etc/services" file.

<Process Name> <Port #>/tcp #<Comment If needed>

2) Make the entry into your "/etc/inetd.conf" file.

<service_name> tli <proto> <flags> <user> <server_pathname> <args>

3) kill -1 <PID of inetd>

4) remote user should then type:

        telnet <Remote Hostname> <Port #>

        a) The remote user may have to execute: xhost + <Remote Hostname>
           before the "telnet" command.

I hope this is of some use to you!

Dan Winger
winger@sewp.nasa.gov
===============================================================
From: adap@andrews.edu

Write a program that binds to a TCP port and socket. Add this program
files.

The book "Unix Network Programming" by Stevens outlines how to create
such daemon programs.

 Edsel Adap Computer Science / Mathematics Major
 adap@andrews.edu Andrews University, Berrien Springs, MI

===============================================================
From: davec@cs.ust.hk (Dave Curado)

Probably the easiest way to do this is with Perl.
If you have the Perl (Camel) book, check out the section on IPC.

If not, ftp to metronet.com, cd into /pub/perl/script/netstuff
and grab new.server (or server.new?) ... anyway, that's a wicked
easy way to do it.

There's also a tutorial in the Sun manuals in the network programming guide.

Dave C.

===============================================================
From: winger@stone.sewp.nasa.gov (Dan Winger -- SEWP)

> Now a new problem: how to find out the terminal type of the remote
> user?

The terminal type is unknown through using "telnet". If you are stuck
with having to use "telnet", the only real way to find out there terminal
type is to ask them. "rlogin" has the capability to send a terminal type.

> Also, how many users can use it at the same time?

This is all based on the size of the program, swap space in the system and
the subject matter of the program.

If all of the above is trivial, look in your directory "/dev" for a listing
of all the files that match "tty*". This listing excluding "ttya", "ttyb" and
so on, can be used via "telnet"

Dan Winger
===============================================================
From: stpeters@spare-parts.crd.ge.com (Dick St.Peters)

> Except :-), I also would like to know the remote user's terminal type,
> because my service program uses curses to print on the screen.
> I think if you log in to a remote place by telnet (or rlogin) the
> system will know your terminal type. So I suppose it is not that hard
> either.

rlogin has it's own protocol - rlogin passed a bunch of info to the
remote rlogind (in.rlogind, actually), including terminal userid,
groups, etc., and also including terminal type.

telnet does not do this; it passes nothing at all except what you
tell it to - what you type in. It just connects your terminal to
the remote daemon, which is why you can telnet to a sendmail port
of a gopher port or a WWW port, for examples, and talk to the daemons.
When you telnet to a remote system, it does not know your terminal
type until/unless you tell it.

There are some terminals that respond to an escape sequence and say
what they are, but not many do, and the inquiry sequence can screw
up those that don't know what it's about.

There's currently no good solution to your problem that I know of.
It has bugged people since the dawn of time (well, time *sharing*,
at least :-).

Dick St.Peters
GE Corporate R&D, Schenectady, NY stpeters@dawn.crd.ge.com

===============================================================
From: "Jim Davis" <jdavis@cs.arizona.edu>

You need to have a server listening on the chosen port. Once it gets
a connection, it then exec's your program.

Writing such a server isn't terribly complicated, but there are a fair
number of details you have to get right. Two good references I've
read are _UNIX Network Programming_, by Richard Stevens, and
_Internetworking with TCP/IP, vol.3_, by Comer and Stevens. The
latter has good examples of building servers, with lots of code
included, in chapters 8-12. Both are geared towards C programming,
though you probably could take the concepts from those books and write
a Perl script that would work too.

-- 
Jim Davis               | "*You* painted the esophaguses.  *I* painted the
jdavis@cs.arizona.edu   |   spleen!" -- Ellen

=============================================================== From: sanford@lsil.com (Sanford Whitehouse)

The simplest solution for part of your problem may be tcpd. You can find it through archie.

Sanford Whitehouse Phone : (408) 433-6378 _____ LSI Logic Corporation Pager : (408) 699-1144 LSI|LOGIC| (R) 3115 Alfred St. FAX : (408) 986-1486 | | M/S J-201 e-mail : sanford@srds1 |_____| Santa Clara, CA. 95054 Internet : sanford@lsil.com

=============================================================== From: Thomas Tengdin <teto@mbari.org>

I suggest that you get the Nutshell Handbook

"Using C on the UNIX System" by Dave Curry.

It is what is used to write the applications that I have here that listen on telnet ports.

T3 =============================================================== From: Ian MacPhedran <Ian_MacPhedran@engr.usask.ca>

> How can I get the remote user's machine (and maybe username) just like "who" > or "finger" does? I just dont find anything using apropos. Or how do I > know which socket is used for the connection by telnetd? (Knowing > this, I could call getpeername)

Who/finger read the file /etc/utmp to get the hostname information. This is written by telnetd/login, as by the time your program starts it's too late to get this any other way in a reasonable manner. If you use the tcp_wrapper, this information will be logged for you.

I don't believe the socket is available to your program any more.

Ian. =============================================================== From: "Benson I. Margulies" <benson@odi.com>

You will have to fetch the public domain telnet BSD daemon source and adapt it. If you telnet to a port, the thing on the other end has to talk telnet protocol, especially if you want to get terminal type info and the like. Just putting a program into inetd.conf won't teach it anything about that.

=============================================================== From: aad@dvorak.amd.com (Anthony A. Datri)

Ask archie about "superserver". =============================================================== From: davec@cs.ust.hk (Dave Curado)

This is an ugly hack of the Perl book script -- but it works...

Just run this in the background from rc.local -- telnet to port 3003 and presto!

At the bottom you'll an IF statement that says if the client (that's you typing) says "?" then echo a variable name back, other wise do a system( touch filename )

Simply edit up that IF statement and stick in the command you want in place of the "touch filename"

Hope this helps... (and hey, this can be very very bad for security, so be careful!)

Dave C.

#!/usr/local/bin/perl

$port = 3003; $AF_INET = 2; $SOCK_STREAM = 1; $sockaddr = 'S n a4 x8';

$filename="/tmp/coffee";

($name,$aliases,$proto) = getprotobyname('tcp'); $this = pack($sockaddr, $AF_INET, $port, "\0\0\0\0"); select(NS); $| = 1; select(stdout);

socket(S,$AF_INET,$SOCK_STREAM,$proto) || die "socket: $!"; bind(S, $this) || die "bind: $!"; listen(S, 5) || die "connect: $!"; select(S); $! = 1; select(stdout);

$con=0; for(;;) { ($addr = accept(NS,S)) || die $!; $con++; if ( -r $filename ) { $coffeestatus="fullpot";} else { $coffeestatus="empty"; } if (($child[$con] = fork()) ==0) { ($af, $port, $inetaddr) = unpack(sockaddr, $addr); @inetaddr = unpack('C4',$inetaddr); chop($clientsaid=<NS>); $!=1; if ($clientsaid =~/\?/) { select(NS); print "$coffeestatus\n"; $!=1; close(NS); exit; } else { system("touch $filename"); close(NS); exit; } } select(stdout); close(NS); } =============================================================== From: mills@cc.umanitoba.ca

There is a program called ``socket'' that allows you to attach an arbitrary program or shell script to a TCP port. It works very nicely, and is available by anonymous FTP at several locations. The author is:

Juergen Nickelsen Hertzbergstr. 28 1000 Berlin 44 Germany

<nickel@cs.tu-berlin.de>

-Gary Mills- -Networking Group- -U of M Computer Services-

=============================================================== From: mills@cc.umanitoba.ca

>Would you please give more specific ftp address?

You might try ftp.cs.tu-berlin.de. Their ftp server is not responding just now, so I can't tell if socket is there.

-Gary Mills- -Networking Group- -U of M Computer Services- > > Well, I couldn't find it so far... > Tamas

=============================================================== From: steve@gec-epl.co.uk (Steve_Kilbane)

well, getting a connection established is easy enough, but bear in mind that telnet expects to be connected to a telnet daemon, and attempts connection details negotiation with the program at the other end. inetd, on the other hand, just sets up the connection so that the program at the other end thinks its reading stdin and writing stdout - it doesn't know about any network.

> Also the program should know about the terminal type of the user, as > well its name. I think the whole thing is not a big deal, but I don't > know much about networking. So if some of you could explain it to me, > I would really appretiate it.

well, given this requirement too, the simplest way is a pseudo-login with your required program as the shell.

steve =============================================================== From: cciolori@tatca.tc.faa.gov (Chris Ciolorito)

Hi Tamas! Your solution is really quite easy! When you write your program, bind your socket server to port 23. That is the well known port that telnet assumes the telnet server will run on. Then, when somebody makes a connection attempt with telnet, your program will grab it since it is listening on the port.

You may have to disable any REAL telnet server that is listening on that port already. Often times this is invoked by inetd.

-->Also if it is working, it would be nice to find out the person's username -->and address who is logged in through that port. Is it also trivial?

Yes, this is easy. Most of that information is available in the hostent data structure that is returned by a gethostbyname call.

===============================================================



This archive was generated by hypermail 2.1.2 : Fri Sep 28 2001 - 23:08:31 CDT