Is Anybody Out There?
For this project, write a cleansocks program that will take a
host name and tests for the existence and properties of certain
common services. Here's what mine looks like:
tom@fedora$ ./scan sandbox.mc.edu
Testing sandbox.mc.edu:
port 23 (telnet) not open: [connect] Operation now in progress
port 22 (ssh) open.
SSH-2.0-OpenSSH_9.9
port 25 (smtp) not open: [connect] Operation now in progress
port 587 (submission) not open: [connect] Operation now in progress
port 465 (smtps) not open: [connect] Operation now in progress
port 109 (pop2) not open: [connect] Operation now in progress
port 110 (pop3) not open: [connect] Operation now in progress
port 143 (imap) not open: [connect] Operation now in progress
port 993 (imaps) not open: [connect] Operation now in progress
port 995 (pop3s) not open: [connect] Operation now in progress
port 21 (ftp) open.
220-=== Welcome to the Sandbox HTML upload server ===
TLS available.
port 80 (http) open.
Server: Apache/2.4.66 (Fedora Linux) OpenSSL/3.2.6 mod_wsgi/5.0.2 Python/3.13
port 443 (https) open.
Server: Apache/2.4.66 (Fedora Linux) OpenSSL/3.2.6 mod_wsgi/5.0.2 Python/3.13
port 631 (ipp) not open: [connect] Operation now in progress
port 88 (kerberos) not open: [connect] Operation now in progress
port 389 (ldap) not open: [connect] Operation now in progress
port 636 (ldaps) not open: [connect] Operation now in progress
Tests
You are to run the following tests on the services specified. There are
different levels of investigation for different services.
- For these services: telnet,
ssh, smtp, smtps, submission, pop2, pop3,
imap, imaps, pop3s, ftp,
http, https, ipp, kerberos, ldap and
ldaps, simply try to connect
and report that the service is open there. If the connect succeeds, the
service is running, if not, not. If the connect fails, cleansocks will
throw an exception, so you're just looking to see if you can connect
without an exception.
- For
ssh, smtp, smtps, submission, pop2, pop3,
imap, imaps, pop3s and ftp, if there is a successful successful connection,
read and print one line from the server. Each of these protocols will
send an initial hello line that usually identifies the type of server. These
are all plain text protocols, except ssh, which still sends a usable first
line of text. For the TLS protocols, smtps,
imaps and pop3s, you will need to
connect using the TLS layer, and for the others you must not.
- For http, https, and ipp, if there is a successful connect,
send a request for the HEAD of / and read
the returned header. Look for the Server: header and print its
contents. If the response code is an error (400 or larger), print the
code and response string.
For https, of course, you need to use the TLS layer to talk
to the server. IPP is a printing protocol based on HTTP, and you can
simply treat it like HTTP for this purpose. It does not use TLS.
- Older text protocols that connect plain text have, in
recent years, been extended to optionally support TLS when the client
requests it. The client connects using plain text, then makes some kind
of request (generally before sending a password) to switch the connection
to TLS. Not all servers support this extension, however.
For the plain-text protocols, smtp, submission, pop3, imap and ftp,
after fetching and printing the first line from
the server, see if the server supports
TLS by requesting it, and checking of the request succeeds. Print a message
that TLS is supported or not, then close the connection. The details of
the TLS request differs with each protocol, and will be discussed below.
The connect test is to put a connect command in a try, and print
yes after the connect, and no in the catch. For this, and all of the
catches in the tests, catch the
socket_error exception which is
part of Cleansocks, so you won't be catching more pedestrian programming
errors. If you run further tests after after a successful connect, always
place them in a try
as well. If they throw, print the message and stop testing
that service. When you're done testing each
service, however thoroughly and whatever the result (including an exception),
always close the connecting socket before
trying the next service.
Checking if TLS is available
Many older Internet protocols, which operate in plain text, have been
extended to allow the client to request adding the secure layer after
connection. This is done by giving a specified command, requesting that
the server to agree to add a secure layer to an existing connection.
If the server agrees, the TLS negotiation begins on the existing connection.
If the server does not agree, it is usually because it is old and
has no idea what you just asked for, so it sends an error response.
So, if you get a positive response, print on the console that TLS is
supported by the server. If you get anything else, print a message that
it is not. Then close the connection in either case. Here is how to
request TLS under each of the above protocols. These are all line-based
protocols, and lines always end with \r\n\
pop3
This is the easiest one.
| Server: | +OK POP3 server ready\r\n |
| Client: | STLS\r\n |
| Server: | +OK Begin TLS negotiation\r\n |
Connect, read the first line (which you need to do anyway, and print it),
then send STLS. Count TLS as supported for any response line that starts
with
+. Close after receiving any response, or if your attempt
throws an exception.
smtp and submission
These are the same protocol, just running on different ports.
The smtp port is intended for passing mail between exchangers, and submission
is intended to accept new messages from local clients. But they're the same
protocol.
The command is simple enough, but you have to tell the server hello before
it wants to hear commands. Mind your manners.
| Server: | 220 mail.fred.com SMTP ready\r\n |
| Client: | EHLO [10.20.30.40]\r\n |
| Server: | 250 OK\r\n |
| Client: | STARTTLS\r\n |
| Server: | 220 Ready to start TLS\r\n |
If the response to STARTTLS does not start with the code 220, the answer is
no, otherwise yes. The EHLO stands for “Extended Hello.” Long
protocol evolution story. Send your host name or IP address
in brackets, though I don't
think servers pay much attention to it. Cleansocks does not make it easy,
but you can get the local address from a connected socket:
IPendpoint me;
getsockname(connectedsocket,me);
ostringstream getaddr;
getaddr << "["<<me.addr()<<"]";
string addr = getaddr.str();
The
other wrinkle is that the response to EHLO is (usually) multi-line.
220 smtp.gmail.com ESMTP 6a1803df08f44-8a593331651sm61434246d6.10 - gsmtp
EHLO [192.168.1.106]
250-smtp.gmail.com at your service, [2600:382:ba28:64f::283]
250-SIZE 35882577
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-CHUNKING
250 SMTPUTF8
STARTTLS
You will need to loop through the lines until you get one which has a
space instead of a dash in the fourth position.
You might note that you can actually answer the question without
sending the STARTTLS command, since the capability is listed in the
response from EHLO. So you can parse those lines instead, which is not
too difficult. Your call.
ftp
The FTP protocol is not too bad:
| Server: | 220 Welcome to our FTP server\r\n |
| Client: | AUTH TLS\r\n |
| Server: | 234 Begin TLS\r\n |
This is almost as simple as pop, except the initial welcome command
could be a
220-Welcome!' and you must loop through lines until
you get a 220 with a space, much like smtp. The 234 response indicates
success, any other failure.
imap
IMAP is interesting because an IMAP server is allowed to respond to requests
in a different order in which they are submitted. This will work:
| Server: | * OK FredCo IMAP server ready\r\n |
| Client: | wxyz STARTTLS\r\n |
| Server: | * Maybe some informational message\r\n |
| Server: | wxyz OK Begin TLS negotiation now\r\n |
Lines starting with * are informational and are not responses to
requests. Any request starts with an arbitrary label string, and the
response is labelled with the same string. A general client will make
a new label for each request, so it can match the response
to them. Since we're
only making one request, we don't need to be so creative.
After reading and printing the initial line, go ahead and send
the STARTTLS, following whatever label you choose.
Then loop a few times looking for a line that starts with
the same tag you sent. If, after the tag, it says OK, the answer is yes,
otherwise no. I looped an (arbitrary) four times, so if the server is
verbose before answering, I'll miss it. Don't expect that.
Alternatively, you can issue a CAPABILITY command, which sends a *
line listing things the server knows how to do. STARTTLS will be on that
line.
Fix Cleansocks
Most of your scan connections will fail when the sever simply doesn't
respond. So the connection needs to time out. Unfortunately, Cleansocks
wasn't designed with a time out setting, so the result will be that
connect just never returns if the remote server never responds. Here is
a fix. Add this to your program after the #include section.
/*
* Gross hack to add a timeout method to TCPsocket.
*/
class TimedTCPsocket: public TCPsocket {
public:
// Set socket operations timeout in milliseconds.
void timeout(int ms = 1000) {
int okay;
#ifdef WIN32
okay = setsockopt(sock(), SOL_SOCKET, SO_RCVTIMEO,
&ms, sizeof ms) >= 0 &&
setsockopt(sock(), SOL_SOCKET, SO_SNDTIMEO,
&ms, sizeof ms) >= 0;
#else
struct timeval timeout = { ms/1000, 1000*(ms%1000) };
okay = setsockopt(sock(), SOL_SOCKET, SO_RCVTIMEO,
&timeout, sizeof timeout) >= 0 &&
setsockopt(sock(), SOL_SOCKET, SO_SNDTIMEO,
&timeout, sizeof timeout) >= 0;
#endif
if(!okay)
throw new socket_sys_error("timeout", ERRCODE);
}
};
As you can see, this creates a new class which is-a TCPsocket, but
adds a method to set the timeout, defaulting to one second.
Call this before connecting on each test. Closing a socket clears
the timeout setting, so if you reconnect the same socket for the next
test, you'll have to call it again. (Not unlike if you use a new socket
for each service test.) I used the default 1s timeout. I increased it
a bit on some of the tests after connect succeeded, but I don't think
it matters.
I wrote the Windows part from the docs, and
have not tested it. Let me know if it gives you some kind of grief.
I presently have really awful Internet service at home, and sometimes
scan reports ports closed that I know are open, because the poor service
delays the response. So do as you need.
Sites to Scan
Always start out be scanning localhost. You probably don't have much
open, but if you have bugs in your program, at least that won't annoy
anyone. My Linux box does have ipp open; it uses it for local printing.
Sandbox, of course. It has ssh, http, https, and ftp open. The FTP
server does support TLS.
www.google.com shows http and https only, as you would expect.
smtp.gmail.com has all the SMTP-based ports open: smtp, submission
and smtps. The latter is a TLS port, and the others support STARTTLS. It
also has pop3s open.
tty.sdf.org also has ssh, and an FTP server sans TLS support. It also
has the ports for pop3 and imap open, but is apparently running ssh there
as well.
On Campus
You may not be able to reach these from off campus.
Scan m-csc-1, which is the printer across from
my office. There's more open than you might guess. It responds to
telnet, ftp, http, https, and ipp. The FTP server does not support TLS, and
the https fails the TLS connection. Probably no one ever intended it to
work anyway, so it isn't configured correctly.
The login server ad.mc.edu has the usually-MS services kerberos,
ldap and ldaps open. Of course, that's only the connect test, so we
don't find anything about them but that they are there.
I have not yet found a server to test with that has (plain) pop3 or
imap, so I can't be certain that my tests work. If I find one, I'll
let you know; if you find one, please tell me.
Notes
Some sites run the wrong protocol on the standard port. Not much you
can do about it. Make sure each of your service tests has its own try/catch
since it may crash in interesting ways.
I've seen some TLS sites report TLS errors, even though my browser will
connect. Not sure what's up, except that TLS clients can be configured
with different levels of persnicketyness, so one may balk and another
proceed on the same site. Not really sure what's up here.
There are multiple ways for a host to tell you buzz off. Simply
not responding will create a timeout, which my system throws with
“Operation now in progress”. If you get
“Connection refused,” that probably means the server responded to your
connection request with a reset, which is netspeak for go away.
In some cases, the response (or non-response) may come from a firewall,
on the host or in front of it, in which case the response will
actually be an ICMP message.
Some documentation:
- RFC 2595
Using TLS with IMAP, POP3 and ACAP.
- RFC 3207SMTP
Service Extension for Secure SMTP over Transport Layer Security.
- RFC 4217
Securing FTP with TLS.
Submission
When your program is working, nicely commented and properly indented,
submit it using the form
here.